From 99da434b3dab7035b93d2eac03045ae3086faeb5 Mon Sep 17 00:00:00 2001 From: realHannes Date: Thu, 25 Apr 2024 19:07:46 +0200 Subject: [PATCH 01/81] Added conversion str to int --- .../sparqlExpressions/StringExpressions.cpp | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/engine/sparqlExpressions/StringExpressions.cpp b/src/engine/sparqlExpressions/StringExpressions.cpp index 7e48871a94..d468784d00 100644 --- a/src/engine/sparqlExpressions/StringExpressions.cpp +++ b/src/engine/sparqlExpressions/StringExpressions.cpp @@ -399,6 +399,25 @@ class ConcatExpression : public detail::VariadicExpression { using EncodeForUriExpression = StringExpressionImpl<1, decltype(encodeForUriImpl)>; +// TO_INT +auto InvalidArg = std::errc::invalid_argument; +auto OutOfRange = std::errc::result_out_of_range; + +[[maybe_used]] auto strToIntImpl = [] (std::optional input) { + if (!input.has_value()) { + return Id::makeUndefined(); + } else { + int res{}; + auto str = boost::lexical_cast(*input); + auto conv = std::from_chars(str.data(), str.data() + str.size(), res); + if (conv.ec == InvalidArg || conv.ec == OutOfRange) { + return Id::makeUndefined(); + } + return Id::makeFromInt(res); + } +}; +using MakeStrToInt = StringExpressionImpl<1, decltype(strToIntImpl)>; + } // namespace detail::string_expressions using namespace detail::string_expressions; using std::make_unique; @@ -452,4 +471,8 @@ Expr makeEncodeForUriExpression(Expr child) { return make(child); } +Expr makeStrToIntExpression(Expr child) { + return make(child); +} + } // namespace sparqlExpression From 424d023ac6b6ec2fb4c439e8c1b949bcfa7a541a Mon Sep 17 00:00:00 2001 From: realHannes Date: Thu, 25 Apr 2024 19:45:06 +0200 Subject: [PATCH 02/81] templated function for toNumeric, add declaration to NaryExpression.h --- src/engine/sparqlExpressions/NaryExpression.h | 3 +++ .../sparqlExpressions/StringExpressions.cpp | 21 ++++++++++++------- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/engine/sparqlExpressions/NaryExpression.h b/src/engine/sparqlExpressions/NaryExpression.h index 154e039f7b..fc34baae18 100644 --- a/src/engine/sparqlExpressions/NaryExpression.h +++ b/src/engine/sparqlExpressions/NaryExpression.h @@ -82,6 +82,9 @@ SparqlExpression::Ptr makeIfExpression(SparqlExpression::Ptr child1, SparqlExpression::Ptr child2, SparqlExpression::Ptr child3); +SparqlExpression::Ptr makeStrToIntExpression(SparqlExpression::Ptr child); +// SparqlExpression::Ptr makeStrToDoubleExpression(SparqlExpression::Ptr child); + SparqlExpression::Ptr makeEncodeForUriExpression(SparqlExpression::Ptr child); SparqlExpression::Ptr makeIsIriExpression(SparqlExpression::Ptr child); diff --git a/src/engine/sparqlExpressions/StringExpressions.cpp b/src/engine/sparqlExpressions/StringExpressions.cpp index d468784d00..d09e7cb3e7 100644 --- a/src/engine/sparqlExpressions/StringExpressions.cpp +++ b/src/engine/sparqlExpressions/StringExpressions.cpp @@ -403,20 +403,23 @@ using EncodeForUriExpression = auto InvalidArg = std::errc::invalid_argument; auto OutOfRange = std::errc::result_out_of_range; -[[maybe_used]] auto strToIntImpl = [] (std::optional input) { +template +[[maybe_used]] auto toNumeric = [] (std::optional input) { if (!input.has_value()) { return Id::makeUndefined(); } else { - int res{}; + T res{}; auto str = boost::lexical_cast(*input); auto conv = std::from_chars(str.data(), str.data() + str.size(), res); if (conv.ec == InvalidArg || conv.ec == OutOfRange) { return Id::makeUndefined(); } - return Id::makeFromInt(res); + if (std::is_same_v) { return Id::makeFromDouble(res); } + else { return Id::makeFromInt(res); } } }; -using MakeStrToInt = StringExpressionImpl<1, decltype(strToIntImpl)>; +using MakeStrToInt = StringExpressionImpl<1, decltype(toNumeric)>; +using MakeDoubleToInt = StringExpressionImpl<1, decltype(toNumeric)>; } // namespace detail::string_expressions using namespace detail::string_expressions; @@ -471,8 +474,12 @@ Expr makeEncodeForUriExpression(Expr child) { return make(child); } -Expr makeStrToIntExpression(Expr child) { - return make(child); +Expr makeStrToIntExpression(Expr child) { + return make(child); +} + +Expr makeStrToDoubleExpression(Expr child) { + return make(child); } -} // namespace sparqlExpression +} // namespace sparqlExpression \ No newline at end of file From 0117e8277565a5865c786817758ce2c9004c1316 Mon Sep 17 00:00:00 2001 From: realHannes Date: Thu, 25 Apr 2024 20:58:01 +0200 Subject: [PATCH 03/81] str to num for SparqlExpression implemented + added test --- src/engine/sparqlExpressions/NaryExpression.h | 2 +- src/engine/sparqlExpressions/StringExpressions.cpp | 2 +- src/parser/sparqlParser/SparqlQleverVisitor.cpp | 4 ++++ test/SparqlExpressionTest.cpp | 13 +++++++++++++ 4 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/engine/sparqlExpressions/NaryExpression.h b/src/engine/sparqlExpressions/NaryExpression.h index fc34baae18..40c4726597 100644 --- a/src/engine/sparqlExpressions/NaryExpression.h +++ b/src/engine/sparqlExpressions/NaryExpression.h @@ -83,7 +83,7 @@ SparqlExpression::Ptr makeIfExpression(SparqlExpression::Ptr child1, SparqlExpression::Ptr child3); SparqlExpression::Ptr makeStrToIntExpression(SparqlExpression::Ptr child); -// SparqlExpression::Ptr makeStrToDoubleExpression(SparqlExpression::Ptr child); +SparqlExpression::Ptr makeStrToDoubleExpression(SparqlExpression::Ptr child); SparqlExpression::Ptr makeEncodeForUriExpression(SparqlExpression::Ptr child); diff --git a/src/engine/sparqlExpressions/StringExpressions.cpp b/src/engine/sparqlExpressions/StringExpressions.cpp index d09e7cb3e7..9c1d0c1d09 100644 --- a/src/engine/sparqlExpressions/StringExpressions.cpp +++ b/src/engine/sparqlExpressions/StringExpressions.cpp @@ -399,7 +399,7 @@ class ConcatExpression : public detail::VariadicExpression { using EncodeForUriExpression = StringExpressionImpl<1, decltype(encodeForUriImpl)>; -// TO_INT +// TO_NUMERIC (int or double) auto InvalidArg = std::errc::invalid_argument; auto OutOfRange = std::errc::result_out_of_range; diff --git a/src/parser/sparqlParser/SparqlQleverVisitor.cpp b/src/parser/sparqlParser/SparqlQleverVisitor.cpp index 1c8d3fec47..24b8bc1b6e 100644 --- a/src/parser/sparqlParser/SparqlQleverVisitor.cpp +++ b/src/parser/sparqlParser/SparqlQleverVisitor.cpp @@ -1801,6 +1801,10 @@ ExpressionPtr Visitor::visit([[maybe_unused]] Parser::BuiltInCallContext* ctx) { return createUnary(&makeIsLiteralExpression); } else if (functionName == "isnumeric") { return createUnary(&makeIsNumericExpression); + } else if (functionName == "INT()") { + return createUnary(&makeStrToIntExpression); + } else if (functionName == "DOUBLE()") { + return createUnary(&makeStrToDoubleExpression); } else if (functionName == "bound") { return makeBoundExpression( std::make_unique(visit(ctx->var()))); diff --git a/test/SparqlExpressionTest.cpp b/test/SparqlExpressionTest.cpp index 5dee81b2e3..4288d0c480 100644 --- a/test/SparqlExpressionTest.cpp +++ b/test/SparqlExpressionTest.cpp @@ -805,6 +805,19 @@ TEST(SparqlExpression, isSomethingFunctions) { Ids{T, T, T, T, T, T, T, T, T, F}); } +// ____________________________________________________________________________ +TEST(SparqlExpression, testToNumericExpression) { + auto checkGetInt = testUnaryExpression<&makeStrToIntExpression>; + auto checkGetDouble = testUnaryExpression<&makeStrToDoubleExpression>; + + checkGetInt(idOrLitOrStringVec( + {U, "5.97", "-78.97", "-57BoB36", "11111111111111", "FreBurg1", ""}), + Ids{U, I(5), I(-78), I(-57), U, U, U}); + checkGetDouble(idOrLitOrStringVec( + {U, "-122.2", "19,96", "-125878930300334.345","UniFr2", "-ab", "144.86375"}), + Ids{U, D(-122.2), D(19), D(-125878930300334.345), U, U, D(144.86375)}); +} + // ____________________________________________________________________________ TEST(SparqlExpression, geoSparqlExpressions) { auto checkLat = testUnaryExpression<&makeLatitudeExpression>; From 94356c2a25861f4c5150553637c2648c93abba63 Mon Sep 17 00:00:00 2001 From: Hannes Baumann <116301375+realHannes@users.noreply.github.com> Date: Fri, 26 Apr 2024 10:16:05 +0200 Subject: [PATCH 04/81] Update src/engine/sparqlExpressions/StringExpressions.cpp Co-authored-by: Johannes Kalmbach --- src/engine/sparqlExpressions/StringExpressions.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/sparqlExpressions/StringExpressions.cpp b/src/engine/sparqlExpressions/StringExpressions.cpp index 9c1d0c1d09..5daead1893 100644 --- a/src/engine/sparqlExpressions/StringExpressions.cpp +++ b/src/engine/sparqlExpressions/StringExpressions.cpp @@ -399,7 +399,7 @@ class ConcatExpression : public detail::VariadicExpression { using EncodeForUriExpression = StringExpressionImpl<1, decltype(encodeForUriImpl)>; -// TO_NUMERIC (int or double) +// Expressions that convert a string to a number (int or double). auto InvalidArg = std::errc::invalid_argument; auto OutOfRange = std::errc::result_out_of_range; From decc8baca480fac89aca5dfdea689cb11bc37e24 Mon Sep 17 00:00:00 2001 From: Hannes Baumann <116301375+realHannes@users.noreply.github.com> Date: Fri, 26 Apr 2024 10:19:03 +0200 Subject: [PATCH 05/81] Update src/engine/sparqlExpressions/StringExpressions.cpp Co-authored-by: Johannes Kalmbach --- src/engine/sparqlExpressions/StringExpressions.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/sparqlExpressions/StringExpressions.cpp b/src/engine/sparqlExpressions/StringExpressions.cpp index 5daead1893..d2a78f4066 100644 --- a/src/engine/sparqlExpressions/StringExpressions.cpp +++ b/src/engine/sparqlExpressions/StringExpressions.cpp @@ -409,7 +409,7 @@ template return Id::makeUndefined(); } else { T res{}; - auto str = boost::lexical_cast(*input); + const auto& str = input.value(); auto conv = std::from_chars(str.data(), str.data() + str.size(), res); if (conv.ec == InvalidArg || conv.ec == OutOfRange) { return Id::makeUndefined(); From 850152cd3f797acfdb639eb7b83041886b3469ef Mon Sep 17 00:00:00 2001 From: Hannes Baumann <116301375+realHannes@users.noreply.github.com> Date: Fri, 26 Apr 2024 10:22:33 +0200 Subject: [PATCH 06/81] Update src/engine/sparqlExpressions/StringExpressions.cpp Co-authored-by: Johannes Kalmbach --- src/engine/sparqlExpressions/StringExpressions.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/sparqlExpressions/StringExpressions.cpp b/src/engine/sparqlExpressions/StringExpressions.cpp index d2a78f4066..5e1a895804 100644 --- a/src/engine/sparqlExpressions/StringExpressions.cpp +++ b/src/engine/sparqlExpressions/StringExpressions.cpp @@ -411,7 +411,7 @@ template T res{}; const auto& str = input.value(); auto conv = std::from_chars(str.data(), str.data() + str.size(), res); - if (conv.ec == InvalidArg || conv.ec == OutOfRange) { + if (conv.ec != std::error_code{} || conv.ptr != str.data() + str.size()) { return Id::makeUndefined(); } if (std::is_same_v) { return Id::makeFromDouble(res); } From d650d678217c1227203aba29e013dc708b22b1f6 Mon Sep 17 00:00:00 2001 From: Hannes Baumann <116301375+realHannes@users.noreply.github.com> Date: Fri, 26 Apr 2024 10:24:55 +0200 Subject: [PATCH 07/81] Update src/engine/sparqlExpressions/StringExpressions.cpp Co-authored-by: Johannes Kalmbach --- src/engine/sparqlExpressions/StringExpressions.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/sparqlExpressions/StringExpressions.cpp b/src/engine/sparqlExpressions/StringExpressions.cpp index 5e1a895804..c9f2df976e 100644 --- a/src/engine/sparqlExpressions/StringExpressions.cpp +++ b/src/engine/sparqlExpressions/StringExpressions.cpp @@ -418,7 +418,7 @@ template else { return Id::makeFromInt(res); } } }; -using MakeStrToInt = StringExpressionImpl<1, decltype(toNumeric)>; +using MakeStrToInt = StringExpressionImpl<1, decltype(toNumeric)>; using MakeDoubleToInt = StringExpressionImpl<1, decltype(toNumeric)>; } // namespace detail::string_expressions From 46cc69735256da80d5f878c0eb6ad0b439743ae1 Mon Sep 17 00:00:00 2001 From: realHannes Date: Fri, 26 Apr 2024 13:32:20 +0200 Subject: [PATCH 08/81] using now absl::from_chars() and stripping whitespaces for string to number conv. --- .../sparqlExpressions/NaryExpressionImpl.h | 2 ++ .../sparqlExpressions/StringExpressions.cpp | 16 ++++++++-------- test/SparqlExpressionTest.cpp | 8 ++++---- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/engine/sparqlExpressions/NaryExpressionImpl.h b/src/engine/sparqlExpressions/NaryExpressionImpl.h index f099f3eee6..5637223158 100644 --- a/src/engine/sparqlExpressions/NaryExpressionImpl.h +++ b/src/engine/sparqlExpressions/NaryExpressionImpl.h @@ -6,6 +6,8 @@ #include +#include "absl/strings/charconv.h" +#include "absl/strings/ascii.h" #include "engine/sparqlExpressions/NaryExpression.h" #include "engine/sparqlExpressions/SparqlExpressionGenerators.h" #include "engine/sparqlExpressions/SparqlExpressionValueGetters.h" diff --git a/src/engine/sparqlExpressions/StringExpressions.cpp b/src/engine/sparqlExpressions/StringExpressions.cpp index c9f2df976e..a426d489e5 100644 --- a/src/engine/sparqlExpressions/StringExpressions.cpp +++ b/src/engine/sparqlExpressions/StringExpressions.cpp @@ -400,22 +400,22 @@ using EncodeForUriExpression = StringExpressionImpl<1, decltype(encodeForUriImpl)>; // Expressions that convert a string to a number (int or double). -auto InvalidArg = std::errc::invalid_argument; -auto OutOfRange = std::errc::result_out_of_range; - template [[maybe_used]] auto toNumeric = [] (std::optional input) { if (!input.has_value()) { return Id::makeUndefined(); } else { - T res{}; - const auto& str = input.value(); - auto conv = std::from_chars(str.data(), str.data() + str.size(), res); + auto str = absl::StripAsciiWhitespace(input.value()); + double resD{}; + auto conv = absl::from_chars(str.data(), str.data() + str.size(), resD); if (conv.ec != std::error_code{} || conv.ptr != str.data() + str.size()) { return Id::makeUndefined(); } - if (std::is_same_v) { return Id::makeFromDouble(res); } - else { return Id::makeFromInt(res); } + if (std::is_same_v) { + auto resT = static_cast(resD); + return Id::makeFromInt(resT); + } + else { return Id::makeFromDouble(resD); } } }; using MakeStrToInt = StringExpressionImpl<1, decltype(toNumeric)>; diff --git a/test/SparqlExpressionTest.cpp b/test/SparqlExpressionTest.cpp index 4288d0c480..8ed86fce07 100644 --- a/test/SparqlExpressionTest.cpp +++ b/test/SparqlExpressionTest.cpp @@ -811,11 +811,11 @@ TEST(SparqlExpression, testToNumericExpression) { auto checkGetDouble = testUnaryExpression<&makeStrToDoubleExpression>; checkGetInt(idOrLitOrStringVec( - {U, "5.97", "-78.97", "-57BoB36", "11111111111111", "FreBurg1", ""}), - Ids{U, I(5), I(-78), I(-57), U, U, U}); + {U, "5.97", "-78.97", "-5BoB6", "FreBurg1", "", " .", " 42\n", " 0.01 "}), + Ids{U, I(5), I(-78), U, U, U, U, I(42), I(0)}); checkGetDouble(idOrLitOrStringVec( - {U, "-122.2", "19,96", "-125878930300334.345","UniFr2", "-ab", "144.86375"}), - Ids{U, D(-122.2), D(19), D(-125878930300334.345), U, U, D(144.86375)}); + {U, "-122.2", "19,96", " 128789334.345 ", "-0.f" , " 0.007 "," -14.75 "}), + Ids{U, D(-122.2), U, D(128789334.345), U, D(0.007), D(-14.75)}); } // ____________________________________________________________________________ From 7fc5c2805e2dafa662969d2fc866bf9c872491d9 Mon Sep 17 00:00:00 2001 From: realHannes Date: Fri, 26 Apr 2024 16:51:13 +0200 Subject: [PATCH 09/81] added new functions to processIriFuntionCall() (for string to number) --- src/global/Constants.h | 2 ++ .../sparqlParser/SparqlQleverVisitor.cpp | 18 +++++++++++++----- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/global/Constants.h b/src/global/Constants.h index ad5a2ff99d..97f7d8d3f5 100644 --- a/src/global/Constants.h +++ b/src/global/Constants.h @@ -60,6 +60,8 @@ static constexpr std::pair GEOF_PREFIX = { "geof:", "http://www.opengis.net/def/function/geosparql/"}; static constexpr std::pair MATH_PREFIX = { "math:", "http://www.w3.org/2005/xpath-functions/math#"}; +static constexpr std::pair SCHEMA_TYPE = { + "xml_schema", "http://www.w3.org/2001/XMLSchema#"}; static const std::string INTERNAL_VARIABLE_PREFIX = "?_QLever_internal_variable_"; diff --git a/src/parser/sparqlParser/SparqlQleverVisitor.cpp b/src/parser/sparqlParser/SparqlQleverVisitor.cpp index 0f6809cdec..6c3ff08432 100644 --- a/src/parser/sparqlParser/SparqlQleverVisitor.cpp +++ b/src/parser/sparqlParser/SparqlQleverVisitor.cpp @@ -121,7 +121,19 @@ ExpressionPtr Visitor::processIriFunctionCall( checkNumArgs(1); return sparqlExpression::makeTanExpression(std::move(argList[0])); } - } + } else if (checkPrefix(SCHEMA_TYPE)) { + if (functionName == "integer") { + checkNumArgs(1); + return sparqlExpression::makeStrToIntExpression( + std::move(argList[0]) + ); + } else if (functionName == "double") { + checkNumArgs(1); + return sparqlExpression::makeStrToDoubleExpression( + std::move(argList[0]) + ); + } + } reportNotSupported(ctx, "Function \""s + iri.toStringRepresentation() + "\" is"); } @@ -1801,10 +1813,6 @@ ExpressionPtr Visitor::visit([[maybe_unused]] Parser::BuiltInCallContext* ctx) { return createUnary(&makeIsLiteralExpression); } else if (functionName == "isnumeric") { return createUnary(&makeIsNumericExpression); - } else if (functionName == "INT()") { - return createUnary(&makeStrToIntExpression); - } else if (functionName == "DOUBLE()") { - return createUnary(&makeStrToDoubleExpression); } else if (functionName == "bound") { return makeBoundExpression( std::make_unique(visit(ctx->var()))); From efb0e24427c8c939dc8fd3cabf285a96c64f7739 Mon Sep 17 00:00:00 2001 From: realHannes Date: Fri, 26 Apr 2024 17:24:43 +0200 Subject: [PATCH 10/81] renaming to: toIntExpression and toDoubleExpression for later more general implementation --- src/engine/sparqlExpressions/NaryExpression.h | 4 ++-- src/engine/sparqlExpressions/StringExpressions.cpp | 14 +++++++------- src/parser/sparqlParser/SparqlQleverVisitor.cpp | 8 ++------ test/SparqlExpressionTest.cpp | 4 ++-- 4 files changed, 13 insertions(+), 17 deletions(-) diff --git a/src/engine/sparqlExpressions/NaryExpression.h b/src/engine/sparqlExpressions/NaryExpression.h index 40c4726597..64e74675d8 100644 --- a/src/engine/sparqlExpressions/NaryExpression.h +++ b/src/engine/sparqlExpressions/NaryExpression.h @@ -82,8 +82,8 @@ SparqlExpression::Ptr makeIfExpression(SparqlExpression::Ptr child1, SparqlExpression::Ptr child2, SparqlExpression::Ptr child3); -SparqlExpression::Ptr makeStrToIntExpression(SparqlExpression::Ptr child); -SparqlExpression::Ptr makeStrToDoubleExpression(SparqlExpression::Ptr child); +SparqlExpression::Ptr toIntExpression(SparqlExpression::Ptr child); +SparqlExpression::Ptr toDoubleExpression(SparqlExpression::Ptr child); SparqlExpression::Ptr makeEncodeForUriExpression(SparqlExpression::Ptr child); diff --git a/src/engine/sparqlExpressions/StringExpressions.cpp b/src/engine/sparqlExpressions/StringExpressions.cpp index a426d489e5..eb4aa6d94b 100644 --- a/src/engine/sparqlExpressions/StringExpressions.cpp +++ b/src/engine/sparqlExpressions/StringExpressions.cpp @@ -411,15 +411,15 @@ template if (conv.ec != std::error_code{} || conv.ptr != str.data() + str.size()) { return Id::makeUndefined(); } - if (std::is_same_v) { + if constexpr (std::is_same_v) { auto resT = static_cast(resD); return Id::makeFromInt(resT); } else { return Id::makeFromDouble(resD); } } }; -using MakeStrToInt = StringExpressionImpl<1, decltype(toNumeric)>; -using MakeDoubleToInt = StringExpressionImpl<1, decltype(toNumeric)>; +using ToInt = StringExpressionImpl<1, decltype(toNumeric)>; +using ToDouble = StringExpressionImpl<1, decltype(toNumeric)>; } // namespace detail::string_expressions using namespace detail::string_expressions; @@ -474,12 +474,12 @@ Expr makeEncodeForUriExpression(Expr child) { return make(child); } -Expr makeStrToIntExpression(Expr child) { - return make(child); +Expr toIntExpression(Expr child) { + return make(child); } -Expr makeStrToDoubleExpression(Expr child) { - return make(child); +Expr toDoubleExpression(Expr child) { + return make(child); } } // namespace sparqlExpression \ No newline at end of file diff --git a/src/parser/sparqlParser/SparqlQleverVisitor.cpp b/src/parser/sparqlParser/SparqlQleverVisitor.cpp index 6c3ff08432..cc4a34785d 100644 --- a/src/parser/sparqlParser/SparqlQleverVisitor.cpp +++ b/src/parser/sparqlParser/SparqlQleverVisitor.cpp @@ -124,14 +124,10 @@ ExpressionPtr Visitor::processIriFunctionCall( } else if (checkPrefix(SCHEMA_TYPE)) { if (functionName == "integer") { checkNumArgs(1); - return sparqlExpression::makeStrToIntExpression( - std::move(argList[0]) - ); + return sparqlExpression::toIntExpression(std::move(argList[0])); } else if (functionName == "double") { checkNumArgs(1); - return sparqlExpression::makeStrToDoubleExpression( - std::move(argList[0]) - ); + return sparqlExpression::toDoubleExpression(std::move(argList[0])); } } reportNotSupported(ctx, diff --git a/test/SparqlExpressionTest.cpp b/test/SparqlExpressionTest.cpp index 8ed86fce07..39ecc8102b 100644 --- a/test/SparqlExpressionTest.cpp +++ b/test/SparqlExpressionTest.cpp @@ -807,8 +807,8 @@ TEST(SparqlExpression, isSomethingFunctions) { // ____________________________________________________________________________ TEST(SparqlExpression, testToNumericExpression) { - auto checkGetInt = testUnaryExpression<&makeStrToIntExpression>; - auto checkGetDouble = testUnaryExpression<&makeStrToDoubleExpression>; + auto checkGetInt = testUnaryExpression<&toIntExpression>; + auto checkGetDouble = testUnaryExpression<&toDoubleExpression>; checkGetInt(idOrLitOrStringVec( {U, "5.97", "-78.97", "-5BoB6", "FreBurg1", "", " .", " 42\n", " 0.01 "}), From a88537c227cf2cef4d64319d5e8e8003c9308681 Mon Sep 17 00:00:00 2001 From: realHannes Date: Fri, 26 Apr 2024 19:24:25 +0200 Subject: [PATCH 11/81] made format (clang-format-16) --- .../sparqlExpressions/StringExpressions.cpp | 17 +++++++---------- src/parser/sparqlParser/SparqlQleverVisitor.cpp | 2 +- test/SparqlExpressionTest.cpp | 15 ++++++++------- 3 files changed, 16 insertions(+), 18 deletions(-) diff --git a/src/engine/sparqlExpressions/StringExpressions.cpp b/src/engine/sparqlExpressions/StringExpressions.cpp index eb4aa6d94b..b6adde3985 100644 --- a/src/engine/sparqlExpressions/StringExpressions.cpp +++ b/src/engine/sparqlExpressions/StringExpressions.cpp @@ -401,7 +401,7 @@ using EncodeForUriExpression = // Expressions that convert a string to a number (int or double). template -[[maybe_used]] auto toNumeric = [] (std::optional input) { +[[maybe_used]] auto toNumeric = [](std::optional input) { if (!input.has_value()) { return Id::makeUndefined(); } else { @@ -411,11 +411,12 @@ template if (conv.ec != std::error_code{} || conv.ptr != str.data() + str.size()) { return Id::makeUndefined(); } - if constexpr (std::is_same_v) { + if constexpr (std::is_same_v) { auto resT = static_cast(resD); return Id::makeFromInt(resT); + } else { + return Id::makeFromDouble(resD); } - else { return Id::makeFromDouble(resD); } } }; using ToInt = StringExpressionImpl<1, decltype(toNumeric)>; @@ -474,12 +475,8 @@ Expr makeEncodeForUriExpression(Expr child) { return make(child); } -Expr toIntExpression(Expr child) { - return make(child); -} +Expr toIntExpression(Expr child) { return make(child); } -Expr toDoubleExpression(Expr child) { - return make(child); -} +Expr toDoubleExpression(Expr child) { return make(child); } -} // namespace sparqlExpression \ No newline at end of file +} // namespace sparqlExpression diff --git a/src/parser/sparqlParser/SparqlQleverVisitor.cpp b/src/parser/sparqlParser/SparqlQleverVisitor.cpp index cc4a34785d..5e0bd41a57 100644 --- a/src/parser/sparqlParser/SparqlQleverVisitor.cpp +++ b/src/parser/sparqlParser/SparqlQleverVisitor.cpp @@ -129,7 +129,7 @@ ExpressionPtr Visitor::processIriFunctionCall( checkNumArgs(1); return sparqlExpression::toDoubleExpression(std::move(argList[0])); } - } + } reportNotSupported(ctx, "Function \""s + iri.toStringRepresentation() + "\" is"); } diff --git a/test/SparqlExpressionTest.cpp b/test/SparqlExpressionTest.cpp index 39ecc8102b..492816f48c 100644 --- a/test/SparqlExpressionTest.cpp +++ b/test/SparqlExpressionTest.cpp @@ -809,13 +809,14 @@ TEST(SparqlExpression, isSomethingFunctions) { TEST(SparqlExpression, testToNumericExpression) { auto checkGetInt = testUnaryExpression<&toIntExpression>; auto checkGetDouble = testUnaryExpression<&toDoubleExpression>; - - checkGetInt(idOrLitOrStringVec( - {U, "5.97", "-78.97", "-5BoB6", "FreBurg1", "", " .", " 42\n", " 0.01 "}), - Ids{U, I(5), I(-78), U, U, U, U, I(42), I(0)}); - checkGetDouble(idOrLitOrStringVec( - {U, "-122.2", "19,96", " 128789334.345 ", "-0.f" , " 0.007 "," -14.75 "}), - Ids{U, D(-122.2), U, D(128789334.345), U, D(0.007), D(-14.75)}); + + checkGetInt(idOrLitOrStringVec({U, "5.97", "-78.97", "-5BoB6", "FreBurg1", "", + " .", " 42\n", " 0.01 "}), + Ids{U, I(5), I(-78), U, U, U, U, I(42), I(0)}); + checkGetDouble( + idOrLitOrStringVec({U, "-122.2", "19,96", " 128789334.345 ", "-0.f", + " 0.007 ", " -14.75 "}), + Ids{U, D(-122.2), U, D(128789334.345), U, D(0.007), D(-14.75)}); } // ____________________________________________________________________________ From ca1e2e05cfd15d51d103c843ad1c40d3c77861dc Mon Sep 17 00:00:00 2001 From: Hannes Baumann <116301375+realHannes@users.noreply.github.com> Date: Mon, 29 Apr 2024 09:10:33 +0200 Subject: [PATCH 12/81] Update src/parser/sparqlParser/SparqlQleverVisitor.cpp Co-authored-by: Johannes Kalmbach --- src/parser/sparqlParser/SparqlQleverVisitor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/parser/sparqlParser/SparqlQleverVisitor.cpp b/src/parser/sparqlParser/SparqlQleverVisitor.cpp index 5e0bd41a57..a6a2692613 100644 --- a/src/parser/sparqlParser/SparqlQleverVisitor.cpp +++ b/src/parser/sparqlParser/SparqlQleverVisitor.cpp @@ -125,7 +125,7 @@ ExpressionPtr Visitor::processIriFunctionCall( if (functionName == "integer") { checkNumArgs(1); return sparqlExpression::toIntExpression(std::move(argList[0])); - } else if (functionName == "double") { + } else if (functionName == "double" || functionName == "decimal") { checkNumArgs(1); return sparqlExpression::toDoubleExpression(std::move(argList[0])); } From 4adc831fd9e97f8527feeeb61a9afcf975b7ec47 Mon Sep 17 00:00:00 2001 From: Hannes Baumann <116301375+realHannes@users.noreply.github.com> Date: Mon, 29 Apr 2024 09:10:48 +0200 Subject: [PATCH 13/81] Update src/parser/sparqlParser/SparqlQleverVisitor.cpp Co-authored-by: Johannes Kalmbach --- src/parser/sparqlParser/SparqlQleverVisitor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/parser/sparqlParser/SparqlQleverVisitor.cpp b/src/parser/sparqlParser/SparqlQleverVisitor.cpp index a6a2692613..9b0cbf7706 100644 --- a/src/parser/sparqlParser/SparqlQleverVisitor.cpp +++ b/src/parser/sparqlParser/SparqlQleverVisitor.cpp @@ -122,7 +122,7 @@ ExpressionPtr Visitor::processIriFunctionCall( return sparqlExpression::makeTanExpression(std::move(argList[0])); } } else if (checkPrefix(SCHEMA_TYPE)) { - if (functionName == "integer") { + if (functionName == "integer" || functionName == "int") { checkNumArgs(1); return sparqlExpression::toIntExpression(std::move(argList[0])); } else if (functionName == "double" || functionName == "decimal") { From d0f0d632f29ae9a0c097174c18043791d1a70b05 Mon Sep 17 00:00:00 2001 From: realHannes Date: Mon, 29 Apr 2024 10:19:10 +0200 Subject: [PATCH 14/81] renaming in NaryExpression.h for accordance with other function, adding correct prefix in Constants.h --- src/engine/sparqlExpressions/NaryExpression.h | 4 ++-- src/engine/sparqlExpressions/StringExpressions.cpp | 9 +++++---- src/global/Constants.h | 4 ++-- src/parser/sparqlParser/SparqlQleverVisitor.cpp | 6 +++--- test/SparqlExpressionTest.cpp | 4 ++-- 5 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/engine/sparqlExpressions/NaryExpression.h b/src/engine/sparqlExpressions/NaryExpression.h index 64e74675d8..992fbb10e4 100644 --- a/src/engine/sparqlExpressions/NaryExpression.h +++ b/src/engine/sparqlExpressions/NaryExpression.h @@ -82,8 +82,8 @@ SparqlExpression::Ptr makeIfExpression(SparqlExpression::Ptr child1, SparqlExpression::Ptr child2, SparqlExpression::Ptr child3); -SparqlExpression::Ptr toIntExpression(SparqlExpression::Ptr child); -SparqlExpression::Ptr toDoubleExpression(SparqlExpression::Ptr child); +SparqlExpression::Ptr makeIntExpression(SparqlExpression::Ptr child); +SparqlExpression::Ptr makeDoubleExpression(SparqlExpression::Ptr child); SparqlExpression::Ptr makeEncodeForUriExpression(SparqlExpression::Ptr child); diff --git a/src/engine/sparqlExpressions/StringExpressions.cpp b/src/engine/sparqlExpressions/StringExpressions.cpp index b6adde3985..f1f1ef4dd1 100644 --- a/src/engine/sparqlExpressions/StringExpressions.cpp +++ b/src/engine/sparqlExpressions/StringExpressions.cpp @@ -406,9 +406,10 @@ template return Id::makeUndefined(); } else { auto str = absl::StripAsciiWhitespace(input.value()); + auto strEnd = str.data() + str.size(); double resD{}; - auto conv = absl::from_chars(str.data(), str.data() + str.size(), resD); - if (conv.ec != std::error_code{} || conv.ptr != str.data() + str.size()) { + auto conv = absl::from_chars(str.data(), strEnd, resD); + if (conv.ec != std::error_code{} || conv.ptr != strEnd) { return Id::makeUndefined(); } if constexpr (std::is_same_v) { @@ -475,8 +476,8 @@ Expr makeEncodeForUriExpression(Expr child) { return make(child); } -Expr toIntExpression(Expr child) { return make(child); } +Expr makeIntExpression(Expr child) { return make(child); } -Expr toDoubleExpression(Expr child) { return make(child); } +Expr makeDoubleExpression(Expr child) { return make(child); } } // namespace sparqlExpression diff --git a/src/global/Constants.h b/src/global/Constants.h index 97f7d8d3f5..c7b68d5735 100644 --- a/src/global/Constants.h +++ b/src/global/Constants.h @@ -60,8 +60,8 @@ static constexpr std::pair GEOF_PREFIX = { "geof:", "http://www.opengis.net/def/function/geosparql/"}; static constexpr std::pair MATH_PREFIX = { "math:", "http://www.w3.org/2005/xpath-functions/math#"}; -static constexpr std::pair SCHEMA_TYPE = { - "xml_schema", "http://www.w3.org/2001/XMLSchema#"}; +static constexpr std::pair XSD_PREFIX = { + "xsd", "http://www.w3.org/2001/XMLSchema#"}; static const std::string INTERNAL_VARIABLE_PREFIX = "?_QLever_internal_variable_"; diff --git a/src/parser/sparqlParser/SparqlQleverVisitor.cpp b/src/parser/sparqlParser/SparqlQleverVisitor.cpp index 9b0cbf7706..02bbde7e26 100644 --- a/src/parser/sparqlParser/SparqlQleverVisitor.cpp +++ b/src/parser/sparqlParser/SparqlQleverVisitor.cpp @@ -121,13 +121,13 @@ ExpressionPtr Visitor::processIriFunctionCall( checkNumArgs(1); return sparqlExpression::makeTanExpression(std::move(argList[0])); } - } else if (checkPrefix(SCHEMA_TYPE)) { + } else if (checkPrefix(XSD_PREFIX)) { if (functionName == "integer" || functionName == "int") { checkNumArgs(1); - return sparqlExpression::toIntExpression(std::move(argList[0])); + return sparqlExpression::makeIntExpression(std::move(argList[0])); } else if (functionName == "double" || functionName == "decimal") { checkNumArgs(1); - return sparqlExpression::toDoubleExpression(std::move(argList[0])); + return sparqlExpression::makeDoubleExpression(std::move(argList[0])); } } reportNotSupported(ctx, diff --git a/test/SparqlExpressionTest.cpp b/test/SparqlExpressionTest.cpp index 492816f48c..f2102cb7e0 100644 --- a/test/SparqlExpressionTest.cpp +++ b/test/SparqlExpressionTest.cpp @@ -807,8 +807,8 @@ TEST(SparqlExpression, isSomethingFunctions) { // ____________________________________________________________________________ TEST(SparqlExpression, testToNumericExpression) { - auto checkGetInt = testUnaryExpression<&toIntExpression>; - auto checkGetDouble = testUnaryExpression<&toDoubleExpression>; + auto checkGetInt = testUnaryExpression<&makeIntExpression>; + auto checkGetDouble = testUnaryExpression<&makeDoubleExpression>; checkGetInt(idOrLitOrStringVec({U, "5.97", "-78.97", "-5BoB6", "FreBurg1", "", " .", " 42\n", " 0.01 "}), From a1186098b8fefcca27c8ebe7da20a9971027f100 Mon Sep 17 00:00:00 2001 From: realHannes Date: Mon, 29 Apr 2024 12:20:14 +0200 Subject: [PATCH 15/81] added test coverage for function calls makeIntExpression and make DoubleExpression --- src/engine/sparqlExpressions/StringExpressions.cpp | 10 ++++++---- test/SparqlAntlrParserTest.cpp | 9 +++++++++ 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/engine/sparqlExpressions/StringExpressions.cpp b/src/engine/sparqlExpressions/StringExpressions.cpp index f1f1ef4dd1..16c7613940 100644 --- a/src/engine/sparqlExpressions/StringExpressions.cpp +++ b/src/engine/sparqlExpressions/StringExpressions.cpp @@ -420,8 +420,8 @@ template } } }; -using ToInt = StringExpressionImpl<1, decltype(toNumeric)>; -using ToDouble = StringExpressionImpl<1, decltype(toNumeric)>; +using ToIntExpression = StringExpressionImpl<1, decltype(toNumeric)>; +using ToDoubleExpression = StringExpressionImpl<1, decltype(toNumeric)>; } // namespace detail::string_expressions using namespace detail::string_expressions; @@ -476,8 +476,10 @@ Expr makeEncodeForUriExpression(Expr child) { return make(child); } -Expr makeIntExpression(Expr child) { return make(child); } +Expr makeIntExpression(Expr child) { return make(child); } -Expr makeDoubleExpression(Expr child) { return make(child); } +Expr makeDoubleExpression(Expr child) { + return make(child); +} } // namespace sparqlExpression diff --git a/test/SparqlAntlrParserTest.cpp b/test/SparqlAntlrParserTest.cpp index a11d54f865..4de7c953b7 100644 --- a/test/SparqlAntlrParserTest.cpp +++ b/test/SparqlAntlrParserTest.cpp @@ -1534,6 +1534,7 @@ TEST(SparqlParser, FunctionCall) { // manually add it when constructing parser inputs. auto geof = absl::StrCat("<", GEOF_PREFIX.second); auto math = absl::StrCat("<", MATH_PREFIX.second); + auto xsd = absl::StrCat("<", XSD_PREFIX.second); // Correct function calls. Check that the parser picks the correct expression. expectFunctionCall(absl::StrCat(geof, "latitude>(?x)"), @@ -1555,6 +1556,14 @@ TEST(SparqlParser, FunctionCall) { matchUnary(&makeCosExpression)); expectFunctionCall(absl::StrCat(math, "tan>(?x)"), matchUnary(&makeTanExpression)); + expectFunctionCall(absl::StrCat(xsd, "int>(?x)"), + matchUnary(&makeIntExpression)); + expectFunctionCall(absl::StrCat(xsd, "integer>(?x)"), + matchUnary(&makeIntExpression)); + expectFunctionCall(absl::StrCat(xsd, "double>(?x)"), + matchUnary(&makeDoubleExpression)); + expectFunctionCall(absl::StrCat(xsd, "decimal>(?x)"), + matchUnary(&makeDoubleExpression)); // Wrong number of arguments. expectFunctionCallFails( From 062052e142380cb1a39e8216048824c24d1ee59f Mon Sep 17 00:00:00 2001 From: realHannes Date: Mon, 29 Apr 2024 15:46:59 +0200 Subject: [PATCH 16/81] toNumeric has now correct behavior and uses absl::from_chars() and std::from_chars() --- .../sparqlExpressions/StringExpressions.cpp | 19 +++++++++++-------- test/SparqlExpressionTest.cpp | 6 +++--- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/engine/sparqlExpressions/StringExpressions.cpp b/src/engine/sparqlExpressions/StringExpressions.cpp index 16c7613940..fb6f3dc0b7 100644 --- a/src/engine/sparqlExpressions/StringExpressions.cpp +++ b/src/engine/sparqlExpressions/StringExpressions.cpp @@ -407,17 +407,20 @@ template } else { auto str = absl::StripAsciiWhitespace(input.value()); auto strEnd = str.data() + str.size(); - double resD{}; - auto conv = absl::from_chars(str.data(), strEnd, resD); - if (conv.ec != std::error_code{} || conv.ptr != strEnd) { - return Id::makeUndefined(); - } + auto strStart = str.data(); + T resD{}; if constexpr (std::is_same_v) { - auto resT = static_cast(resD); - return Id::makeFromInt(resT); + auto conv = std::from_chars(strStart, strEnd, resD); + if (conv.ec == std::error_code{} && conv.ptr == strEnd) { + return Id::makeFromInt(resD); + } } else { - return Id::makeFromDouble(resD); + auto conv = absl::from_chars(strStart, strEnd, resD); + if (conv.ec == std::error_code{} && conv.ptr == strEnd) { + return Id::makeFromDouble(resD); + } } + return Id::makeUndefined(); } }; using ToIntExpression = StringExpressionImpl<1, decltype(toNumeric)>; diff --git a/test/SparqlExpressionTest.cpp b/test/SparqlExpressionTest.cpp index f2102cb7e0..d010d9a94a 100644 --- a/test/SparqlExpressionTest.cpp +++ b/test/SparqlExpressionTest.cpp @@ -810,9 +810,9 @@ TEST(SparqlExpression, testToNumericExpression) { auto checkGetInt = testUnaryExpression<&makeIntExpression>; auto checkGetDouble = testUnaryExpression<&makeDoubleExpression>; - checkGetInt(idOrLitOrStringVec({U, "5.97", "-78.97", "-5BoB6", "FreBurg1", "", - " .", " 42\n", " 0.01 "}), - Ids{U, I(5), I(-78), U, U, U, U, I(42), I(0)}); + checkGetInt(idOrLitOrStringVec({U, " -1275", "5.97", "-78.97", "-5BoB6", + "FreBurg1", "", " .", " 42\n", " 0.01 ", ""}), + Ids{U, I(-1275), U, U, U, U, U, U, I(42), U, U}); checkGetDouble( idOrLitOrStringVec({U, "-122.2", "19,96", " 128789334.345 ", "-0.f", " 0.007 ", " -14.75 "}), From 6d0f42a8b4c70bc906135e9cc54478d4c43136e2 Mon Sep 17 00:00:00 2001 From: realHannes Date: Mon, 29 Apr 2024 16:07:40 +0200 Subject: [PATCH 17/81] made clang-format for NaryExpressionImpl.h --- src/engine/sparqlExpressions/NaryExpressionImpl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/sparqlExpressions/NaryExpressionImpl.h b/src/engine/sparqlExpressions/NaryExpressionImpl.h index 5637223158..1a5d89c315 100644 --- a/src/engine/sparqlExpressions/NaryExpressionImpl.h +++ b/src/engine/sparqlExpressions/NaryExpressionImpl.h @@ -6,8 +6,8 @@ #include -#include "absl/strings/charconv.h" #include "absl/strings/ascii.h" +#include "absl/strings/charconv.h" #include "engine/sparqlExpressions/NaryExpression.h" #include "engine/sparqlExpressions/SparqlExpressionGenerators.h" #include "engine/sparqlExpressions/SparqlExpressionValueGetters.h" From edc974a5572d3c254222320182d388aa85976e7e Mon Sep 17 00:00:00 2001 From: realHannes Date: Wed, 18 Sep 2024 09:29:42 +0200 Subject: [PATCH 18/81] Add implementation for pre-filtering blocks on their metadata --- src/index/CMakeLists.txt | 2 +- src/index/CompressedBlockPrefiltering.cpp | 161 ++++++++ src/index/CompressedBlockPrefiltering.h | 129 ++++++ test/CMakeLists.txt | 2 + test/CompressedBlockPrefilteringTest.cpp | 482 ++++++++++++++++++++++ test/SparqlExpressionTestHelpers.h | 32 ++ 6 files changed, 807 insertions(+), 1 deletion(-) create mode 100644 src/index/CompressedBlockPrefiltering.cpp create mode 100644 src/index/CompressedBlockPrefiltering.h create mode 100644 test/CompressedBlockPrefilteringTest.cpp diff --git a/src/index/CMakeLists.txt b/src/index/CMakeLists.txt index e41fd2471f..ac795a0fd4 100644 --- a/src/index/CMakeLists.txt +++ b/src/index/CMakeLists.txt @@ -5,5 +5,5 @@ add_library(index LocatedTriples.cpp Permutation.cpp TextMetaData.cpp DocsDB.cpp FTSAlgorithms.cpp PrefixHeuristic.cpp CompressedRelation.cpp - PatternCreator.cpp ScanSpecification.cpp) + PatternCreator.cpp ScanSpecification.cpp CompressedBlockPrefiltering.cpp) qlever_target_link_libraries(index util parser vocabulary compilationInfo ${STXXL_LIBRARIES}) diff --git a/src/index/CompressedBlockPrefiltering.cpp b/src/index/CompressedBlockPrefiltering.cpp new file mode 100644 index 0000000000..564d768127 --- /dev/null +++ b/src/index/CompressedBlockPrefiltering.cpp @@ -0,0 +1,161 @@ +// Copyright 2024, University of Freiburg, +// Chair of Algorithms and Data Structures +// Author: Hannes Baumann + +#include "index/CompressedBlockPrefiltering.h" + +// SECTION RELATIONAL OPERATIONS +//______________________________________________________________________________ +template +bool RelationalExpressions::compare(const ValueId& firstId, + const ValueId& secondId) { + // For compactness we combine LE and GT here. In evaluate (see below), for + // LE we select all blocks at a smaller (lower_bound) index position, and + // for GT all the respective blocks at larger or equal indices. + if constexpr (Comparison == CompOp::LE || Comparison == CompOp::GT) { + return compareIds(firstId, secondId, CompOp::LE) == + valueIdComparators::ComparisonResult::True; + } else { + // Follows the same logic for evaluation as explained above. + // Used for CompOp::LT, CompOp::GE, CompOp::EQ and CompOp::NE. + // For CompOp::EQ we also use the LT operation: This is possible because in + // evaluate we calculate a lower_bound and upper_bound over this LT + // operation, and those bounds act as the plausible range for equality + // (evaluation of CompOp::NE follows a similar approach). + return compareIds(firstId, secondId, CompOp::LT) == + valueIdComparators::ComparisonResult::True; + } +} + +//______________________________________________________________________________ +template +auto RelationalExpressions::lowerIndex( + std::vector& input, size_t evaluationColumn) { + // Custom compare function for binary search (lower bound). + auto makeCompare = [this, evaluationColumn]( + const BlockMetadata& blockMetadata, + const ValueId& referenceId) -> bool { + ValueId blockId = + Comparison == CompOp::LT || Comparison == CompOp::LE || + Comparison == CompOp::NE + ? getIdFromColumnIndex(blockMetadata.firstTriple_, evaluationColumn) + : getIdFromColumnIndex(blockMetadata.lastTriple_, evaluationColumn); + return compare(blockId, referenceId); + }; + return std::lower_bound(input.begin(), input.end(), referenceId_, + makeCompare); +}; + +//______________________________________________________________________________ +template +auto RelationalExpressions::upperIndex( + std::vector& input, size_t evaluationColumn) + requires(Comparison == CompOp::EQ || Comparison == CompOp::NE) { + // Custom compare function for binary search (upper bound). + auto makeCompare = [this, evaluationColumn](const ValueId& referenceId, + BlockMetadata& blockMetadata) { + return compare( + referenceId, + Comparison == CompOp::EQ + ? getIdFromColumnIndex(blockMetadata.firstTriple_, evaluationColumn) + : getIdFromColumnIndex(blockMetadata.lastTriple_, + evaluationColumn)); + }; + // Use the same comparison operations as for the method lowerIndex + // (std::lower_bound). However, given that std::upper_bound should return an + // index to the first block that is larger than the provided reference value, + // we have to swap the input values when calling compare() in makeCompare. + // compare(blockId, referenceId) -> compare(referenceId, blockId) + return std::upper_bound(input.begin(), input.end(), referenceId_, + makeCompare); +}; + +//______________________________________________________________________________ +template +std::vector RelationalExpressions::evaluate( + std::vector& input, size_t evaluationColumn) { + if constexpr (Comparison == CompOp::LT || Comparison == CompOp::LE) { + return std::vector(input.begin(), + lowerIndex(input, evaluationColumn)); + } else if constexpr (Comparison == CompOp::GE || Comparison == CompOp::GT) { + // Complementary block selection to LT and LE + return std::vector(lowerIndex(input, evaluationColumn), + input.end()); + } else if constexpr (Comparison == CompOp::EQ) { + return std::vector(lowerIndex(input, evaluationColumn), + upperIndex(input, evaluationColumn)); + } else { + // CompOP::NE + // Get the indices that define the boundaries w.r.t. blocks potentially + // containing non-equal values. + auto firstBound = lowerIndex(input, evaluationColumn); + auto secondBound = upperIndex(input, evaluationColumn); + if (firstBound > secondBound) { + return input; // all blocks are relevant + } + // Extract the values from the respective ranges defined by the bounding + // indices from vector input. + // [input.begin(), firstBound) and [secondBound, input.end()] + std::vector vecNe; + vecNe.reserve(std::distance(input.begin(), firstBound) + + std::distance(secondBound, input.end())); + vecNe.insert(vecNe.end(), input.begin(), firstBound); + vecNe.insert(vecNe.end(), secondBound, input.end()); + return vecNe; + } +}; + +//______________________________________________________________________________ +// Necessary instantiation of template specializations +template class RelationalExpressions; +template class RelationalExpressions; +template class RelationalExpressions; +template class RelationalExpressions; +template class RelationalExpressions; +template class RelationalExpressions; + +// SECTION LOGICAL OPERATIONS +//______________________________________________________________________________ +template +std::vector LogicalExpressions::evaluateOr( + std::vector& input, size_t evaluationColumn) + requires(Operation == LogicalOperators::OR) { + auto relevantBlocksChild1 = child1_->evaluate(input, evaluationColumn); + auto relevantBlocksChild2 = child2_->evaluate(input, evaluationColumn); + // The result vector, reserve space for the logical upper bound (num blocks) + // |relevantBlocksChild1| + |relevantBlocksChild2| regarding the OR + // operation (union of blocks) + std::vector unionedRelevantBlocks; + unionedRelevantBlocks.reserve(relevantBlocksChild1.size() + + relevantBlocksChild2.size()); + // lambda function that implements the less-than comparison required for + // std::set_union + auto lessThan = [evaluationColumn](const BlockMetadata& block1, + const BlockMetadata& block2) { + return getIdFromColumnIndex(block1.lastTriple_, evaluationColumn) < + getIdFromColumnIndex(block2.lastTriple_, evaluationColumn); + }; + // Given that we have vectors with sorted (BlockMedata) values, we can + // use std::set_union, thus the complexity is O(n + m). + std::set_union(relevantBlocksChild1.begin(), relevantBlocksChild1.end(), + relevantBlocksChild2.begin(), relevantBlocksChild2.end(), + std::back_inserter(unionedRelevantBlocks), lessThan); + return unionedRelevantBlocks; +}; + +//______________________________________________________________________________ +template +std::vector LogicalExpressions::evaluate( + std::vector& input, size_t evaluationColumn) { + if constexpr (Operation == LogicalOperators::AND) { + auto resultChild1 = child1_->evaluate(input, evaluationColumn); + return child2_->evaluate(resultChild1, evaluationColumn); + } else { + return evaluateOr(input, evaluationColumn); + } +}; + +//______________________________________________________________________________ +// Necessary instantiation of template specializations +template class LogicalExpressions; +template class LogicalExpressions; diff --git a/src/index/CompressedBlockPrefiltering.h b/src/index/CompressedBlockPrefiltering.h new file mode 100644 index 0000000000..a35b36e71d --- /dev/null +++ b/src/index/CompressedBlockPrefiltering.h @@ -0,0 +1,129 @@ +// Copyright 2024, University of Freiburg, +// Chair of Algorithms and Data Structures +// Author: Hannes Baumann + +#pragma once + +#include +#include + +#include "global/Id.h" +#include "global/ValueIdComparators.h" +#include "index/CompressedRelation.h" + +// The compressed block metadata (see `CompressedRelation.h`) that we use to +// filter out the non-relevant blocks by checking their content of +// `firstTriple_` and `lastTriple_` (`PermutedTriple`) +using BlockMetadata = CompressedBlockMetadata; + +//______________________________________________________________________________ +/* +`PrefilterExpression` represents a base class for the following sub-classes that +implement the block-filtering procedure for the specific relational + logical +operations/expressions. + +Remark: We do not actually evaluate the respective SPARQL Expression. We only +pre-filter w.r.t. blocks that contain relevant data for the actual evaluation of +those expressions to make the evaluation procedure more efficient. + +The block-filtering is applied with the following operations: +Relational Expressions - `<=`, `>=`, `<`, `>`, `==` and `!=` +Logical Operations - `and` and `or` +*/ + +class PrefilterExpression { + public: + // The respective metadata to the blocks is expected to be provided in + // a sorted order (w.r.t. the relevant column). + virtual std::vector evaluate(std::vector& input, + size_t evaluationColumn) = 0; + virtual ~PrefilterExpression() = default; +}; + +//______________________________________________________________________________ +// For the actual comparison of the relevant ValueIds from the metadata triples, +// we use the implementations from ValueIdComparators. +// +// Supported comparisons are: +// - LessThan, LessEqual, Equal, NotEqual, GreaterEqual, GreaterThan +using CompOp = valueIdComparators::Comparison; + +//______________________________________________________________________________ +// Given a PermutedTriple, retrieve the suitable Id w.r.t. a column (index). +constexpr auto getIdFromColumnIndex = + [](const BlockMetadata::PermutedTriple& permutedTriple, + const size_t columnIndex) { + switch (columnIndex) { + case 0: + return permutedTriple.col0Id_; + case 1: + return permutedTriple.col1Id_; + case 2: + return permutedTriple.col2Id_; + default: + // Triple! + AD_FAIL(); + } + }; + +//______________________________________________________________________________ +template +class RelationalExpressions : public PrefilterExpression { + public: + explicit RelationalExpressions(ValueId referenceId) + : referenceId_(referenceId) {} + + std::vector evaluate(std::vector& input, + size_t evaluationColumn) override; + + private: + // The ValueId the comparison refers to. + ValueId referenceId_; + // Helper function that implements the relational comparison on the block + // values. + bool compare(const ValueId& tripleId, const ValueId& otherId); + // Helper for providing the lower indices during the evaluation procedure. + auto lowerIndex(std::vector& input, size_t evaluationColumn); + // Helper to get the upper indices, necessary for EQ and NE. + auto upperIndex(std::vector& input, size_t evaluationColumn) + requires(Comparison == CompOp::EQ || Comparison == CompOp::NE); +}; + +//______________________________________________________________________________ +// Helper struct for a compact class implementation regarding the logical +// operations `AND`, `OR` and `NOT`. +enum struct LogicalOperators { AND, OR, NOT }; + +//______________________________________________________________________________ +template +class LogicalExpressions : public PrefilterExpression { + public: + explicit LogicalExpressions(std::unique_ptr child1, + std::unique_ptr child2) + : child1_(std::move(child1)), child2_(std::move(child2)) {} + + std::vector evaluate(std::vector& input, + size_t evaluationColumn) override; + + private: + std::unique_ptr child1_; + std::unique_ptr child2_; + // Introduce a helper method for the specification LogicalOperators::OR + std::vector evaluateOr(std::vector& input, + size_t evaluationColumn) + requires(Operation == LogicalOperators::OR); +}; + +//______________________________________________________________________________ +// Definition of the RelationalExpressions for LT, LE, EQ, NE, GE and GT. +using LessThanExpression = RelationalExpressions; +using LessEqualExpression = RelationalExpressions; +using EqualExpression = RelationalExpressions; +using NotEqualExpression = RelationalExpressions; +using GreaterEqualExpression = RelationalExpressions; +using GreaterThanExpression = RelationalExpressions; + +//______________________________________________________________________________ +// Defintion of the LogicalExpressions for AND, OR and NOT. +using AndExpression = LogicalExpressions; +using OrExpression = LogicalExpressions; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index d9a5d2a0fe..d7e2a6d855 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -287,6 +287,8 @@ addLinkAndDiscoverTest(AlgorithmTest) addLinkAndDiscoverTestSerial(CompressedRelationsTest index) +addLinkAndDiscoverTestSerial(CompressedBlockPrefilteringTest index) + addLinkAndDiscoverTest(ExceptionTest) addLinkAndDiscoverTestSerial(RandomExpressionTest index) diff --git a/test/CompressedBlockPrefilteringTest.cpp b/test/CompressedBlockPrefilteringTest.cpp new file mode 100644 index 0000000000..3f99cc3448 --- /dev/null +++ b/test/CompressedBlockPrefilteringTest.cpp @@ -0,0 +1,482 @@ +// Copyright 2024, University of Freiburg, +// Chair of Algorithms and Data Structures +// Author: Hannes Baumann + +#include + +#include + +#include "./SparqlExpressionTestHelpers.h" +#include "index/CompressedBlockPrefiltering.h" +#include "util/IdTestHelpers.h" + +using ad_utility::testing::DoubleId; +using ad_utility::testing::IntId; +using ad_utility::testing::UndefId; +using sparqlExpression::TestContext; + +//______________________________________________________________________________ +struct MetadataBlocks { + // Define five blocks, for convenience all of their columns are sorted on + // their data when appended in the given order to a vector. + // Columns indexed: COLUMN 0 | COLUMN 1 | COLUMN 2 + BlockMetadata nb1{{}, + 0, + {IntId(16), IntId(0), DoubleId(0.0)}, // firstTriple + {IntId(38), IntId(0), DoubleId(12.5)}}; // lastTriple + BlockMetadata nb2{{}, + 0, + {IntId(42), IntId(0), DoubleId(12.5)}, + {IntId(42), IntId(2), DoubleId(14.575)}}; + BlockMetadata nb3{{}, + 0, + {IntId(42), IntId(2), DoubleId(16.33)}, + {IntId(45), IntId(2), DoubleId(18.32)}}; + BlockMetadata nb4{{}, + 0, + {IntId(46), IntId(2), DoubleId(22.29)}, + {IntId(47), IntId(6), DoubleId(111.223)}}; + BlockMetadata nb5{{}, + 0, + {IntId(48), IntId(6), DoubleId(111.333)}, + {IntId(51), IntId(6), DoubleId(112.00)}}; + std::vector numericBlocks = {nb1, nb2, nb3, nb4, nb5}; + + //____________________________________________________________________________ + // Define Blocks containing VocabIds + // Use TestContext from SparqlExpressionTestHelpers.h + sparqlExpression::TestContext tc{}; + Id Undef = UndefId(); + // Columns indexed: COLUMN 0 | COLUMN 1 | COLUMN 2 + // undef. | LocalVocab | undef. + BlockMetadata vb1{{}, 0, {Undef, tc.bonn, Undef}, {Undef, tc.cologne, Undef}}; + BlockMetadata vb2{ + {}, 0, {Undef, tc.dortmund, Undef}, {Undef, tc.essen, Undef}}; + BlockMetadata vb3{ + {}, 0, {Undef, tc.frankfurt, Undef}, {Undef, tc.frankfurt, Undef}}; + BlockMetadata vb4{ + {}, 0, {Undef, tc.hamburg, Undef}, {Undef, tc.karlsruhe, Undef}}; + BlockMetadata vb5{ + {}, 0, {Undef, tc.karlsruhe, Undef}, {Undef, tc.karlsruhe, Undef}}; + std::vector vocabBlocks = {vb1, vb2, vb3, vb4, vb5}; +}; + +// Static tests, they focus on corner case values for the given block triples. +//______________________________________________________________________________ +//______________________________________________________________________________ +// Test Relational Expressions + +// Test LessThanExpression +// Remark: We compare w.r.t. the first triple! +TEST(RelationalExpressions, testLessThanExpressions) { + MetadataBlocks blocks{}; + std::vector expectedResult = {}; + // NUMERIC + EXPECT_EQ(expectedResult, + LessThanExpression{IntId(10)}.evaluate(blocks.numericBlocks, 0)); + EXPECT_EQ(expectedResult, + LessThanExpression{IntId(16)}.evaluate(blocks.numericBlocks, 0)); + expectedResult = {blocks.nb1}; + EXPECT_EQ(expectedResult, + LessThanExpression{IntId(40)}.evaluate(blocks.numericBlocks, 0)); + EXPECT_EQ(expectedResult, + LessThanExpression{IntId(42)}.evaluate(blocks.numericBlocks, 0)); + expectedResult = {blocks.nb1, blocks.nb2, blocks.nb3}; + EXPECT_EQ(expectedResult, + LessThanExpression{IntId(46)}.evaluate(blocks.numericBlocks, 0)); + EXPECT_EQ(blocks.numericBlocks, + LessThanExpression{IntId(100)}.evaluate(blocks.numericBlocks, 0)); + // VOCAB + TestContext tc{}; + expectedResult = {}; + // tc.alpha is an Id of type Vocab, w.r.t. a lexicographical order, all the + // citiy IDs (LocalVocab) should be greater than the ID (Vocab) of "alpha". + // Thus we expect that none of the blocks are relevant. + EXPECT_EQ(expectedResult, + LessThanExpression{tc.alpha}.evaluate(blocks.vocabBlocks, 1)); + EXPECT_EQ(expectedResult, + LessThanExpression{tc.berlin}.evaluate(blocks.vocabBlocks, 1)); + // All cities used within vocabBlocks should be smaller than city "munich" + // (given their respective LocalVocab IDs). + EXPECT_EQ(blocks.vocabBlocks, + LessThanExpression{tc.munich}.evaluate(blocks.vocabBlocks, 1)); + // All blocks from vocabBlocks should contain values less-than the (Vocab) + // ID for "zz". + EXPECT_EQ(blocks.vocabBlocks, + LessThanExpression{tc.zz}.evaluate(blocks.vocabBlocks, 1)); + expectedResult = {blocks.vb1, blocks.vb2}; + EXPECT_EQ(expectedResult, + LessThanExpression{tc.düsseldorf}.evaluate(blocks.vocabBlocks, 1)); + expectedResult = {blocks.vb1, blocks.vb2, blocks.vb3}; + EXPECT_EQ(expectedResult, LessThanExpression{tc.frankfurt_oder}.evaluate( + blocks.vocabBlocks, 1)); + expectedResult = {blocks.vb1, blocks.vb2, blocks.vb3, blocks.vb4}; + EXPECT_EQ(expectedResult, + LessThanExpression{tc.ingolstadt}.evaluate(blocks.vocabBlocks, 1)); +} + +//______________________________________________________________________________ +// Test LessEqualExpression +// Remark: We compare w.r.t. the first triple +TEST(RelationalExpressions, testLessEqualExpressions) { + MetadataBlocks blocks{}; + std::vector expectedResult = {}; + // NUMERIC + EXPECT_EQ(expectedResult, + LessEqualExpression{IntId(10)}.evaluate(blocks.numericBlocks, 0)); + expectedResult = {blocks.nb1}; + EXPECT_EQ(expectedResult, + LessEqualExpression{IntId(16)}.evaluate(blocks.numericBlocks, 0)); + EXPECT_EQ(expectedResult, + LessEqualExpression{IntId(40)}.evaluate(blocks.numericBlocks, 0)); + expectedResult = {blocks.nb1, blocks.nb2, blocks.nb3}; + EXPECT_EQ(expectedResult, + LessEqualExpression{IntId(42)}.evaluate(blocks.numericBlocks, 0)); + expectedResult = {blocks.nb1, blocks.nb2, blocks.nb3, blocks.nb4}; + EXPECT_EQ(expectedResult, + LessEqualExpression{IntId(46)}.evaluate(blocks.numericBlocks, 0)); + EXPECT_EQ(blocks.numericBlocks, + LessEqualExpression{IntId(100)}.evaluate(blocks.numericBlocks, 0)); + // VOCAB + TestContext tc{}; + expectedResult = {}; + EXPECT_EQ(expectedResult, + LessEqualExpression{tc.alpha}.evaluate(blocks.vocabBlocks, 1)); + EXPECT_EQ(expectedResult, + LessEqualExpression{tc.berlin}.evaluate(blocks.vocabBlocks, 1)); + expectedResult = {blocks.vb1, blocks.vb2}; + EXPECT_EQ(expectedResult, + LessEqualExpression{tc.dortmund}.evaluate(blocks.vocabBlocks, 1)); + expectedResult = {blocks.vb1, blocks.vb2, blocks.vb3, blocks.vb4}; + EXPECT_EQ(expectedResult, + LessEqualExpression{tc.hannover}.evaluate(blocks.vocabBlocks, 1)); + EXPECT_EQ(blocks.vocabBlocks, + LessEqualExpression{tc.karlsruhe}.evaluate(blocks.vocabBlocks, 1)); +} + +//______________________________________________________________________________ +// Test GreaterThanExpression +// Remark: We compare w.r.t. the last triple +TEST(RelationalExpressions, testGreaterThanExpression) { + MetadataBlocks blocks{}; + EXPECT_EQ(blocks.numericBlocks, + GreaterThanExpression{IntId(10)}.evaluate(blocks.numericBlocks, 0)); + EXPECT_EQ(blocks.numericBlocks, + GreaterThanExpression{IntId(16)}.evaluate(blocks.numericBlocks, 0)); + std::vector expectedResult = {blocks.nb2, blocks.nb3, + blocks.nb4, blocks.nb5}; + EXPECT_EQ(expectedResult, + GreaterThanExpression{IntId(38)}.evaluate(blocks.numericBlocks, 0)); + expectedResult = {blocks.nb3, blocks.nb4, blocks.nb5}; + EXPECT_EQ(expectedResult, + GreaterThanExpression{IntId(42)}.evaluate(blocks.numericBlocks, 0)); + expectedResult = {blocks.nb4, blocks.nb5}; + EXPECT_EQ(expectedResult, + GreaterThanExpression{IntId(46)}.evaluate(blocks.numericBlocks, 0)); + expectedResult = {}; + EXPECT_EQ(expectedResult, + GreaterThanExpression{IntId(52)}.evaluate(blocks.numericBlocks, 0)); + // VOCAB + TestContext tc{}; + expectedResult = {}; + EXPECT_EQ(expectedResult, + GreaterThanExpression{tc.munich}.evaluate(blocks.vocabBlocks, 1)); + EXPECT_EQ(expectedResult, GreaterThanExpression{tc.karlsruhe}.evaluate( + blocks.vocabBlocks, 1)); + EXPECT_EQ(blocks.vocabBlocks, + GreaterThanExpression{tc.alpha}.evaluate(blocks.vocabBlocks, 1)); + expectedResult = {blocks.vb4, blocks.vb5}; + EXPECT_EQ(expectedResult, + GreaterThanExpression{tc.hamburg}.evaluate(blocks.vocabBlocks, 1)); + expectedResult = {blocks.vb4, blocks.vb5}; + EXPECT_EQ(expectedResult, + GreaterThanExpression{tc.hannover}.evaluate(blocks.vocabBlocks, 1)); + expectedResult = {blocks.vb2, blocks.vb3, blocks.vb4, blocks.vb5}; + EXPECT_EQ(expectedResult, GreaterThanExpression{tc.düsseldorf}.evaluate( + blocks.vocabBlocks, 1)); +} + +//______________________________________________________________________________ +// Test GreaterEqualExpression +// Remark: We compare w.r.t. the last triple +TEST(RelationalExpressions, testGreaterEqualExpression) { + MetadataBlocks blocks{}; + EXPECT_EQ(blocks.numericBlocks, GreaterEqualExpression{IntId(10)}.evaluate( + blocks.numericBlocks, 0)); + EXPECT_EQ(blocks.numericBlocks, GreaterEqualExpression{IntId(38)}.evaluate( + blocks.numericBlocks, 0)); + std::vector expectedResult = {blocks.nb2, blocks.nb3, + blocks.nb4, blocks.nb5}; + EXPECT_EQ(expectedResult, GreaterEqualExpression{IntId(40)}.evaluate( + blocks.numericBlocks, 0)); + EXPECT_EQ(expectedResult, GreaterEqualExpression{IntId(42)}.evaluate( + blocks.numericBlocks, 0)); + expectedResult = {blocks.nb3, blocks.nb4, blocks.nb5}; + EXPECT_EQ(expectedResult, GreaterEqualExpression{IntId(45)}.evaluate( + blocks.numericBlocks, 0)); + expectedResult = {blocks.nb4, blocks.nb5}; + EXPECT_EQ(expectedResult, GreaterEqualExpression{IntId(47)}.evaluate( + blocks.numericBlocks, 0)); + expectedResult = {}; + EXPECT_EQ(expectedResult, GreaterEqualExpression{IntId(100)}.evaluate( + blocks.numericBlocks, 0)); + // VOCAB + TestContext tc{}; + EXPECT_EQ(blocks.vocabBlocks, + GreaterEqualExpression{tc.alpha}.evaluate(blocks.vocabBlocks, 1)); + EXPECT_EQ(blocks.vocabBlocks, + GreaterEqualExpression{tc.bonn}.evaluate(blocks.vocabBlocks, 1)); + EXPECT_EQ(blocks.vocabBlocks, + GreaterEqualExpression{tc.cologne}.evaluate(blocks.vocabBlocks, 1)); + expectedResult = {blocks.vb2, blocks.vb3, blocks.vb4, blocks.vb5}; + EXPECT_EQ(expectedResult, GreaterEqualExpression{tc.düsseldorf}.evaluate( + blocks.vocabBlocks, 1)); + expectedResult = {blocks.vb4, blocks.vb5}; + EXPECT_EQ(expectedResult, GreaterEqualExpression{tc.frankfurt_oder}.evaluate( + blocks.vocabBlocks, 1)); + EXPECT_EQ(expectedResult, GreaterEqualExpression{tc.karlsruhe}.evaluate( + blocks.vocabBlocks, 1)); + expectedResult = {}; + EXPECT_EQ(expectedResult, + GreaterEqualExpression{tc.munich}.evaluate(blocks.vocabBlocks, 1)); + EXPECT_EQ(expectedResult, + GreaterEqualExpression{tc.zz}.evaluate(blocks.vocabBlocks, 1)); +} + +//______________________________________________________________________________ +// Test EqualExpression +TEST(RelationalExpressions, testEqualExpression) { + MetadataBlocks blocks{}; + std::vector expectedResult = {}; + EXPECT_EQ(expectedResult, + EqualExpression{IntId(10)}.evaluate(blocks.numericBlocks, 1)); + EXPECT_EQ(expectedResult, + EqualExpression{IntId(10)}.evaluate(blocks.numericBlocks, 0)); + expectedResult = {blocks.nb4}; + EXPECT_EQ(expectedResult, + EqualExpression{IntId(5)}.evaluate(blocks.numericBlocks, 1)); + expectedResult = {blocks.nb2, blocks.nb3, blocks.nb4}; + EXPECT_EQ(expectedResult, + EqualExpression{IntId(2)}.evaluate(blocks.numericBlocks, 1)); + expectedResult = {blocks.nb4, blocks.nb5}; + EXPECT_EQ(expectedResult, + EqualExpression{IntId(6)}.evaluate(blocks.numericBlocks, 1)); + expectedResult = {blocks.nb2, blocks.nb3}; + EXPECT_EQ(expectedResult, + EqualExpression{IntId(42)}.evaluate(blocks.numericBlocks, 0)); + expectedResult = {blocks.nb5}; + EXPECT_EQ(expectedResult, + EqualExpression{IntId(112)}.evaluate(blocks.numericBlocks, 2)); + // VOCAB + TestContext tc{}; + expectedResult = {}; + EXPECT_EQ(expectedResult, + EqualExpression{tc.zz}.evaluate(blocks.vocabBlocks, 1)); + EXPECT_EQ(expectedResult, + EqualExpression{tc.alpha}.evaluate(blocks.vocabBlocks, 1)); + EXPECT_EQ(expectedResult, + EqualExpression{tc.munich}.evaluate(blocks.vocabBlocks, 1)); + EXPECT_EQ(expectedResult, + EqualExpression{tc.frankfurt_oder}.evaluate(blocks.vocabBlocks, 1)); + expectedResult = {blocks.vb4, blocks.vb5}; + EXPECT_EQ(expectedResult, + EqualExpression{tc.karlsruhe}.evaluate(blocks.vocabBlocks, 1)); + expectedResult = {blocks.vb3}; + EXPECT_EQ(expectedResult, + EqualExpression{tc.frankfurt}.evaluate(blocks.vocabBlocks, 1)); + expectedResult = {blocks.vb2}; + EXPECT_EQ(expectedResult, + EqualExpression{tc.düsseldorf}.evaluate(blocks.vocabBlocks, 1)); + expectedResult = {blocks.vb1}; + EXPECT_EQ(expectedResult, + EqualExpression{tc.bonn}.evaluate(blocks.vocabBlocks, 1)); + EXPECT_EQ(expectedResult, + EqualExpression{tc.cologne}.evaluate(blocks.vocabBlocks, 1)); +} + +//______________________________________________________________________________ +// Test NotEqualExpression +TEST(RelationalExpressions, testNotEqualExpression) { + MetadataBlocks blocks{}; + std::vector expectedResult{}; + EXPECT_EQ(blocks.numericBlocks, + NotEqualExpression{IntId(8)}.evaluate(blocks.numericBlocks, 0)); + EXPECT_EQ(blocks.numericBlocks, + NotEqualExpression{IntId(16)}.evaluate(blocks.numericBlocks, 0)); + EXPECT_EQ(blocks.numericBlocks, + NotEqualExpression{IntId(45)}.evaluate(blocks.numericBlocks, 0)); + EXPECT_EQ(blocks.numericBlocks, + NotEqualExpression{IntId(51)}.evaluate(blocks.numericBlocks, 0)); + EXPECT_EQ(blocks.numericBlocks, + NotEqualExpression{IntId(48)}.evaluate(blocks.numericBlocks, 0)); + EXPECT_EQ(blocks.numericBlocks, NotEqualExpression{DoubleId(18.32)}.evaluate( + blocks.numericBlocks, 2)); + EXPECT_EQ(blocks.numericBlocks, NotEqualExpression{DoubleId(22.33)}.evaluate( + blocks.numericBlocks, 2)); + EXPECT_EQ(blocks.numericBlocks, + NotEqualExpression{IntId(17)}.evaluate(blocks.numericBlocks, 0)); + expectedResult = {blocks.nb1, blocks.nb3, blocks.nb4, blocks.nb5}; + EXPECT_EQ(expectedResult, + NotEqualExpression{IntId(42)}.evaluate(blocks.numericBlocks, 0)); + expectedResult = {blocks.nb1, blocks.nb2, blocks.nb4, blocks.nb5}; + EXPECT_EQ(expectedResult, + NotEqualExpression{IntId(2)}.evaluate(blocks.numericBlocks, 1)); + expectedResult = {blocks.nb1, blocks.nb2, blocks.nb3, blocks.nb4}; + EXPECT_EQ(expectedResult, NotEqualExpression{DoubleId(6.00)}.evaluate( + blocks.numericBlocks, 1)); + expectedResult = {blocks.nb2, blocks.nb3, blocks.nb4, blocks.nb5}; + EXPECT_EQ(expectedResult, + NotEqualExpression{IntId(0)}.evaluate(blocks.numericBlocks, 1)); + // VOCAB + TestContext tc{}; + expectedResult = {}; + EXPECT_EQ(blocks.vocabBlocks, + NotEqualExpression{tc.zz}.evaluate(blocks.vocabBlocks, 1)); + EXPECT_EQ(blocks.vocabBlocks, + NotEqualExpression{tc.alpha}.evaluate(blocks.vocabBlocks, 1)); + EXPECT_EQ(blocks.vocabBlocks, NotEqualExpression{tc.frankfurt_oder}.evaluate( + blocks.vocabBlocks, 1)); + EXPECT_EQ(blocks.vocabBlocks, + NotEqualExpression{tc.munich}.evaluate(blocks.vocabBlocks, 1)); + EXPECT_EQ(blocks.vocabBlocks, + NotEqualExpression{tc.bonn}.evaluate(blocks.vocabBlocks, 1)); + EXPECT_EQ(blocks.vocabBlocks, + NotEqualExpression{tc.cologne}.evaluate(blocks.vocabBlocks, 1)); + EXPECT_EQ(blocks.vocabBlocks, + NotEqualExpression{tc.düsseldorf}.evaluate(blocks.vocabBlocks, 1)); + expectedResult = {blocks.vb1, blocks.vb2, blocks.vb4, blocks.vb5}; + EXPECT_EQ(expectedResult, + NotEqualExpression{tc.frankfurt}.evaluate(blocks.vocabBlocks, 1)); + expectedResult = {blocks.vb1, blocks.vb2, blocks.vb3, blocks.vb4}; + EXPECT_EQ(expectedResult, + NotEqualExpression{tc.karlsruhe}.evaluate(blocks.vocabBlocks, 1)); +} + +//______________________________________________________________________________ +//______________________________________________________________________________ +// Test Logical Expressions + +// Test AndExpression +TEST(LogicalExpressions, testAndExpression) { + MetadataBlocks blocks{}; + std::vector expectedResult{}; + EXPECT_EQ(expectedResult, + AndExpression(std::make_unique(IntId(42)), + std::make_unique(IntId(45))) + .evaluate(blocks.numericBlocks, 0)); + EXPECT_EQ( + expectedResult, + AndExpression(std::make_unique(IntId(42)), + std::make_unique(DoubleId(52.33))) + .evaluate(blocks.numericBlocks, 0)); + EXPECT_EQ( + expectedResult, + AndExpression(std::make_unique( + std::make_unique(IntId(42)), + std::make_unique(IntId(45))), + std::make_unique(IntId(49))) + .evaluate(blocks.numericBlocks, 0)); + expectedResult = {blocks.nb1, blocks.nb2}; + EXPECT_EQ(expectedResult, + AndExpression(std::make_unique( + std::make_unique(IntId(0)), + std::make_unique(IntId(0))), + std::make_unique(IntId(6))) + .evaluate(blocks.numericBlocks, 1)); + // !!! Reason that we don't expect an empty result here: + // Block blocks.nb3 contains value within the range [42, 45] on column 0. + // Thus, this block is a valid candidate for values unequal to 42 and values + // that are equal to 45. + expectedResult = {blocks.nb3}; + EXPECT_EQ(expectedResult, + AndExpression(std::make_unique(IntId(42)), + std::make_unique(DoubleId(42.00))) + .evaluate(blocks.numericBlocks, 0)); + expectedResult = {blocks.nb1, blocks.nb2, blocks.nb3, blocks.nb4}; + EXPECT_EQ(expectedResult, + AndExpression(std::make_unique(DoubleId(6.00)), + std::make_unique(IntId(2.00))) + .evaluate(blocks.numericBlocks, 1)); + expectedResult = {blocks.nb4, blocks.nb5}; + EXPECT_EQ( + expectedResult, + AndExpression(std::make_unique( + std::make_unique(IntId(7)), + std::make_unique(IntId(5))), + std::make_unique(IntId(0))) + .evaluate(blocks.numericBlocks, 1)); + expectedResult = {blocks.nb1, blocks.nb2}; + EXPECT_EQ( + expectedResult, + AndExpression(std::make_unique(DoubleId(14.575)), + std::make_unique(IntId(12))) + .evaluate(blocks.numericBlocks, 2)); + expectedResult = {blocks.nb2, blocks.nb3, blocks.nb4}; + EXPECT_EQ(expectedResult, + AndExpression(std::make_unique(DoubleId(0.00)), + std::make_unique(DoubleId(6.00))) + .evaluate(blocks.numericBlocks, 1)); + expectedResult = {blocks.nb1, blocks.nb2}; + EXPECT_EQ(expectedResult, + AndExpression(std::make_unique(DoubleId(1.99)), + std::make_unique(DoubleId(1.5))) + .evaluate(blocks.numericBlocks, 1)); +} + +//______________________________________________________________________________ +// Test OrExpression +TEST(LogicalExpressions, testOrExpression) { + MetadataBlocks blocks{}; + std::vector expectedResult = {blocks.nb1, blocks.nb4, + blocks.nb5}; + EXPECT_EQ(expectedResult, + OrExpression(std::make_unique(IntId(42)), + std::make_unique(IntId(45))) + .evaluate(blocks.numericBlocks, 0)); + expectedResult = {}; + EXPECT_EQ(expectedResult, + OrExpression(std::make_unique(DoubleId(-14.23)), + std::make_unique(IntId(51))) + .evaluate(blocks.numericBlocks, 0)); + expectedResult = {blocks.nb2, blocks.nb3, blocks.nb4, blocks.nb5}; + EXPECT_EQ( + expectedResult, + OrExpression(std::make_unique(IntId(0)), + std::make_unique( + std::make_unique(IntId(5)), + std::make_unique(DoubleId(1.00)))) + .evaluate(blocks.numericBlocks, 1)); + expectedResult = {blocks.nb1, blocks.nb2, blocks.nb4, blocks.nb5}; + EXPECT_EQ(expectedResult, + OrExpression(std::make_unique(IntId(0)), + std::make_unique( + std::make_unique(IntId(3)), + std::make_unique(DoubleId(6)))) + .evaluate(blocks.numericBlocks, 1)); + expectedResult = {blocks.nb4, blocks.nb5}; + EXPECT_EQ( + expectedResult, + OrExpression(std::make_unique(IntId(20)), + std::make_unique(DoubleId(113.3))) + .evaluate(blocks.numericBlocks, 2)); + expectedResult = {blocks.nb1, blocks.nb5}; + EXPECT_EQ( + expectedResult, + OrExpression(std::make_unique(IntId(42)), + std::make_unique( + std::make_unique(IntId(49)), + std::make_unique(DoubleId(2.00)))) + .evaluate(blocks.numericBlocks, 0)); + expectedResult = {blocks.nb1, blocks.nb2, blocks.nb3, blocks.nb4}; + EXPECT_EQ(expectedResult, + OrExpression(std::make_unique( + std::make_unique(IntId(0)), + std::make_unique(DoubleId(2.00))), + std::make_unique(IntId(6))) + .evaluate(blocks.numericBlocks, 1)); + expectedResult = {blocks.nb1, blocks.nb2, blocks.nb3}; + EXPECT_EQ(expectedResult, + OrExpression(std::make_unique(DoubleId(17.00)), + std::make_unique(IntId(16.35))) + .evaluate(blocks.numericBlocks, 2)); +} diff --git a/test/SparqlExpressionTestHelpers.h b/test/SparqlExpressionTestHelpers.h index 6ded863c41..cdbb4f90f4 100644 --- a/test/SparqlExpressionTestHelpers.h +++ b/test/SparqlExpressionTestHelpers.h @@ -61,6 +61,10 @@ struct TestContext { // vocab. Id notInVocabA, notInVocabB, notInVocabC, notInVocabD, notInVocabAelpha, notInVocabIri, notInVocabIriLit; + // LocalVocab IDs used for testing the block pre-filtering procedure in + // CompressedBlockPrefilteringTest + Id berlin, bonn, cologne, dortmund, düsseldorf, essen, frankfurt, + frankfurt_oder, hamburg, hannover, ingolstadt, karlsruhe, munich; TestContext() { // First get some IDs for strings from the vocabulary to later reuse them. // Note the `u_` inserted for the blank node (see 'BlankNode.cpp'). @@ -90,12 +94,40 @@ struct TestContext { localVocab.getIndexAndAddIfNotContained(iri(""))); notInVocabAelpha = Id::makeFromLocalVocabIndex( localVocab.getIndexAndAddIfNotContained(lit("notInVocabÄlpha"))); + notInVocabAelpha = Id::makeFromLocalVocabIndex( + localVocab.getIndexAndAddIfNotContained(lit("notInVocabÄlpha"))); notInVocabIri = Id::makeFromLocalVocabIndex(localVocab.getIndexAndAddIfNotContained( iri(""))); notInVocabIriLit = Id::makeFromLocalVocabIndex(localVocab.getIndexAndAddIfNotContained( lit("http://www.w3.org/1999/02/22-rdf-syntax-ns#langString"))); + berlin = Id::makeFromLocalVocabIndex( + localVocab.getIndexAndAddIfNotContained(lit("berlin"))); + bonn = Id::makeFromLocalVocabIndex( + localVocab.getIndexAndAddIfNotContained(lit("bonn"))); + cologne = Id::makeFromLocalVocabIndex( + localVocab.getIndexAndAddIfNotContained(lit("cologne"))); + düsseldorf = Id::makeFromLocalVocabIndex( + localVocab.getIndexAndAddIfNotContained(lit("düsseldorf"))); + dortmund = Id::makeFromLocalVocabIndex( + localVocab.getIndexAndAddIfNotContained(lit("dortmund"))); + essen = Id::makeFromLocalVocabIndex( + localVocab.getIndexAndAddIfNotContained(lit("essen"))); + frankfurt = Id::makeFromLocalVocabIndex( + localVocab.getIndexAndAddIfNotContained(lit("frankfurt"))); + frankfurt_oder = Id::makeFromLocalVocabIndex( + localVocab.getIndexAndAddIfNotContained(lit("frankfurt (oder)"))); + hamburg = Id::makeFromLocalVocabIndex( + localVocab.getIndexAndAddIfNotContained(lit("hamburg"))); + hannover = Id::makeFromLocalVocabIndex( + localVocab.getIndexAndAddIfNotContained(lit("hannover"))); + ingolstadt = Id::makeFromLocalVocabIndex( + localVocab.getIndexAndAddIfNotContained(lit("ingolstadt"))); + karlsruhe = Id::makeFromLocalVocabIndex( + localVocab.getIndexAndAddIfNotContained(lit("karlsruhe"))); + munich = Id::makeFromLocalVocabIndex( + localVocab.getIndexAndAddIfNotContained(lit("munich"))); // Set up the `table` that represents the previous partial query results. It // has five columns/variables: ?ints (only integers), ?doubles (only From 46be51acd98747911ade4b01f19f37d181bf7187 Mon Sep 17 00:00:00 2001 From: realHannes Date: Wed, 18 Sep 2024 09:53:24 +0200 Subject: [PATCH 19/81] fix spelling error --- src/index/CompressedBlockPrefiltering.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index/CompressedBlockPrefiltering.h b/src/index/CompressedBlockPrefiltering.h index a35b36e71d..fbb5236345 100644 --- a/src/index/CompressedBlockPrefiltering.h +++ b/src/index/CompressedBlockPrefiltering.h @@ -124,6 +124,6 @@ using GreaterEqualExpression = RelationalExpressions; using GreaterThanExpression = RelationalExpressions; //______________________________________________________________________________ -// Defintion of the LogicalExpressions for AND, OR and NOT. +// Definition of the LogicalExpressions for AND, OR and NOT. using AndExpression = LogicalExpressions; using OrExpression = LogicalExpressions; From 0385b561e40ba5103d176fadb9a0f002d540b4e6 Mon Sep 17 00:00:00 2001 From: realHannes Date: Wed, 18 Sep 2024 11:57:29 +0200 Subject: [PATCH 20/81] add namespace for CompressedBlockPrefiltering.h --- src/index/CompressedBlockPrefiltering.cpp | 5 ++++ src/index/CompressedBlockPrefiltering.h | 29 ++++++++++++++++------- test/SparqlExpressionTestHelpers.h | 2 +- 3 files changed, 26 insertions(+), 10 deletions(-) diff --git a/src/index/CompressedBlockPrefiltering.cpp b/src/index/CompressedBlockPrefiltering.cpp index 564d768127..7e9712fa12 100644 --- a/src/index/CompressedBlockPrefiltering.cpp +++ b/src/index/CompressedBlockPrefiltering.cpp @@ -4,6 +4,11 @@ #include "index/CompressedBlockPrefiltering.h" +using prefilterExpressions::CompOp; +using prefilterExpressions::LogicalExpressions; +using prefilterExpressions::LogicalOperators; +using prefilterExpressions::RelationalExpressions; + // SECTION RELATIONAL OPERATIONS //______________________________________________________________________________ template diff --git a/src/index/CompressedBlockPrefiltering.h b/src/index/CompressedBlockPrefiltering.h index fbb5236345..28a78b5046 100644 --- a/src/index/CompressedBlockPrefiltering.h +++ b/src/index/CompressedBlockPrefiltering.h @@ -16,6 +16,7 @@ // `firstTriple_` and `lastTriple_` (`PermutedTriple`) using BlockMetadata = CompressedBlockMetadata; +namespace prefilterExpressions { //______________________________________________________________________________ /* `PrefilterExpression` represents a base class for the following sub-classes that @@ -114,16 +115,26 @@ class LogicalExpressions : public PrefilterExpression { requires(Operation == LogicalOperators::OR); }; +} // namespace prefilterExpressions + //______________________________________________________________________________ // Definition of the RelationalExpressions for LT, LE, EQ, NE, GE and GT. -using LessThanExpression = RelationalExpressions; -using LessEqualExpression = RelationalExpressions; -using EqualExpression = RelationalExpressions; -using NotEqualExpression = RelationalExpressions; -using GreaterEqualExpression = RelationalExpressions; -using GreaterThanExpression = RelationalExpressions; +using LessThanExpression = prefilterExpressions::RelationalExpressions< + prefilterExpressions::CompOp::LT>; +using LessEqualExpression = prefilterExpressions::RelationalExpressions< + prefilterExpressions::CompOp::LE>; +using EqualExpression = prefilterExpressions::RelationalExpressions< + prefilterExpressions::CompOp::EQ>; +using NotEqualExpression = prefilterExpressions::RelationalExpressions< + prefilterExpressions::CompOp::NE>; +using GreaterEqualExpression = prefilterExpressions::RelationalExpressions< + prefilterExpressions::CompOp::GE>; +using GreaterThanExpression = prefilterExpressions::RelationalExpressions< + prefilterExpressions::CompOp::GT>; //______________________________________________________________________________ -// Definition of the LogicalExpressions for AND, OR and NOT. -using AndExpression = LogicalExpressions; -using OrExpression = LogicalExpressions; +// Definition of the LogicalExpressions for AND and OR. +using AndExpression = prefilterExpressions::LogicalExpressions< + prefilterExpressions::LogicalOperators::AND>; +using OrExpression = prefilterExpressions::LogicalExpressions< + prefilterExpressions::LogicalOperators::OR>; diff --git a/test/SparqlExpressionTestHelpers.h b/test/SparqlExpressionTestHelpers.h index cdbb4f90f4..dc4d627ab2 100644 --- a/test/SparqlExpressionTestHelpers.h +++ b/test/SparqlExpressionTestHelpers.h @@ -117,7 +117,7 @@ struct TestContext { frankfurt = Id::makeFromLocalVocabIndex( localVocab.getIndexAndAddIfNotContained(lit("frankfurt"))); frankfurt_oder = Id::makeFromLocalVocabIndex( - localVocab.getIndexAndAddIfNotContained(lit("frankfurt (oder)"))); + localVocab.getIndexAndAddIfNotContained(lit("frankfurt (FFO)"))); hamburg = Id::makeFromLocalVocabIndex( localVocab.getIndexAndAddIfNotContained(lit("hamburg"))); hannover = Id::makeFromLocalVocabIndex( From 570733845248299a0b1785ce438cc026d3bc5b74 Mon Sep 17 00:00:00 2001 From: realHannes Date: Wed, 18 Sep 2024 13:44:25 +0200 Subject: [PATCH 21/81] use std::ranges::set_union, std::ranges::upper_bound/lower_bound --- src/index/CompressedBlockPrefiltering.cpp | 91 ++++++++++++----------- src/index/CompressedBlockPrefiltering.h | 2 +- 2 files changed, 50 insertions(+), 43 deletions(-) diff --git a/src/index/CompressedBlockPrefiltering.cpp b/src/index/CompressedBlockPrefiltering.cpp index 7e9712fa12..1849d9d062 100644 --- a/src/index/CompressedBlockPrefiltering.cpp +++ b/src/index/CompressedBlockPrefiltering.cpp @@ -12,13 +12,14 @@ using prefilterExpressions::RelationalExpressions; // SECTION RELATIONAL OPERATIONS //______________________________________________________________________________ template -bool RelationalExpressions::compare(const ValueId& firstId, - const ValueId& secondId) { +bool RelationalExpressions::compareImpl( + const ValueId& firstId, const ValueId& secondId) const { + using enum CompOp; // For compactness we combine LE and GT here. In evaluate (see below), for // LE we select all blocks at a smaller (lower_bound) index position, and // for GT all the respective blocks at larger or equal indices. - if constexpr (Comparison == CompOp::LE || Comparison == CompOp::GT) { - return compareIds(firstId, secondId, CompOp::LE) == + if constexpr (Comparison == LE || Comparison == GT) { + return compareIds(firstId, secondId, LE) == valueIdComparators::ComparisonResult::True; } else { // Follows the same logic for evaluation as explained above. @@ -27,7 +28,7 @@ bool RelationalExpressions::compare(const ValueId& firstId, // evaluate we calculate a lower_bound and upper_bound over this LT // operation, and those bounds act as the plausible range for equality // (evaluation of CompOp::NE follows a similar approach). - return compareIds(firstId, secondId, CompOp::LT) == + return compareIds(firstId, secondId, LT) == valueIdComparators::ComparisonResult::True; } } @@ -36,19 +37,22 @@ bool RelationalExpressions::compare(const ValueId& firstId, template auto RelationalExpressions::lowerIndex( std::vector& input, size_t evaluationColumn) { - // Custom compare function for binary search (lower bound). - auto makeCompare = [this, evaluationColumn]( - const BlockMetadata& blockMetadata, - const ValueId& referenceId) -> bool { - ValueId blockId = - Comparison == CompOp::LT || Comparison == CompOp::LE || - Comparison == CompOp::NE - ? getIdFromColumnIndex(blockMetadata.firstTriple_, evaluationColumn) - : getIdFromColumnIndex(blockMetadata.lastTriple_, evaluationColumn); - return compare(blockId, referenceId); - }; - return std::lower_bound(input.begin(), input.end(), referenceId_, - makeCompare); + using enum CompOp; + // Extract evaluationColumn specific ValueId from BlockMetadata + auto getRelevantIdFromBlock = + [this, evaluationColumn](const BlockMetadata& blockMetadata) { + return Comparison == LT || Comparison == LE || Comparison == NE + ? getIdFromColumnIndex(blockMetadata.firstTriple_, + evaluationColumn) + : getIdFromColumnIndex(blockMetadata.lastTriple_, + evaluationColumn); + }; + return std::ranges::lower_bound( + input, referenceId_, + [this](const ValueId& blockId, const ValueId& referenceId) { + return compareImpl(blockId, referenceId); + }, + getRelevantIdFromBlock); }; //______________________________________________________________________________ @@ -56,37 +60,41 @@ template auto RelationalExpressions::upperIndex( std::vector& input, size_t evaluationColumn) requires(Comparison == CompOp::EQ || Comparison == CompOp::NE) { - // Custom compare function for binary search (upper bound). - auto makeCompare = [this, evaluationColumn](const ValueId& referenceId, - BlockMetadata& blockMetadata) { - return compare( - referenceId, - Comparison == CompOp::EQ - ? getIdFromColumnIndex(blockMetadata.firstTriple_, evaluationColumn) - : getIdFromColumnIndex(blockMetadata.lastTriple_, - evaluationColumn)); - }; - // Use the same comparison operations as for the method lowerIndex - // (std::lower_bound). However, given that std::upper_bound should return an - // index to the first block that is larger than the provided reference value, - // we have to swap the input values when calling compare() in makeCompare. - // compare(blockId, referenceId) -> compare(referenceId, blockId) - return std::upper_bound(input.begin(), input.end(), referenceId_, - makeCompare); + // Extract evaluationColumn specific ValueId from BlockMetadata + auto getRelevantIdFromBlock = + [this, evaluationColumn](const BlockMetadata& blockMetadata) { + return Comparison == CompOp::EQ + ? getIdFromColumnIndex(blockMetadata.firstTriple_, + evaluationColumn) + : getIdFromColumnIndex(blockMetadata.lastTriple_, + evaluationColumn); + }; + // Use the same comparison operations compareImpl as for the method lowerIndex + // (with std::ranges::lower_bound). + // However, the difference here is the helper lambda getRelevantIdFromBlock(), + // for CompOp::EQ we have to consider the first triple of the respective + // BlockMetadata value. + return std::ranges::upper_bound( + input, referenceId_, + [this](const ValueId& blockId, const ValueId& referenceId) { + return compareImpl(blockId, referenceId); + }, + getRelevantIdFromBlock); }; //______________________________________________________________________________ template std::vector RelationalExpressions::evaluate( std::vector& input, size_t evaluationColumn) { - if constexpr (Comparison == CompOp::LT || Comparison == CompOp::LE) { + using enum CompOp; + if constexpr (Comparison == LT || Comparison == LE) { return std::vector(input.begin(), lowerIndex(input, evaluationColumn)); - } else if constexpr (Comparison == CompOp::GE || Comparison == CompOp::GT) { + } else if constexpr (Comparison == GE || Comparison == GT) { // Complementary block selection to LT and LE return std::vector(lowerIndex(input, evaluationColumn), input.end()); - } else if constexpr (Comparison == CompOp::EQ) { + } else if constexpr (Comparison == EQ) { return std::vector(lowerIndex(input, evaluationColumn), upperIndex(input, evaluationColumn)); } else { @@ -141,10 +149,9 @@ std::vector LogicalExpressions::evaluateOr( getIdFromColumnIndex(block2.lastTriple_, evaluationColumn); }; // Given that we have vectors with sorted (BlockMedata) values, we can - // use std::set_union, thus the complexity is O(n + m). - std::set_union(relevantBlocksChild1.begin(), relevantBlocksChild1.end(), - relevantBlocksChild2.begin(), relevantBlocksChild2.end(), - std::back_inserter(unionedRelevantBlocks), lessThan); + // use std::ranges::set_union, thus the complexity is O(n + m). + std::ranges::set_union(relevantBlocksChild1, relevantBlocksChild2, + std::back_inserter(unionedRelevantBlocks), lessThan); return unionedRelevantBlocks; }; diff --git a/src/index/CompressedBlockPrefiltering.h b/src/index/CompressedBlockPrefiltering.h index 28a78b5046..67c3da012c 100644 --- a/src/index/CompressedBlockPrefiltering.h +++ b/src/index/CompressedBlockPrefiltering.h @@ -82,7 +82,7 @@ class RelationalExpressions : public PrefilterExpression { ValueId referenceId_; // Helper function that implements the relational comparison on the block // values. - bool compare(const ValueId& tripleId, const ValueId& otherId); + bool compareImpl(const ValueId& tripleId, const ValueId& otherId) const; // Helper for providing the lower indices during the evaluation procedure. auto lowerIndex(std::vector& input, size_t evaluationColumn); // Helper to get the upper indices, necessary for EQ and NE. From c9687eca7e6094188faaf182badecf731ce6b524 Mon Sep 17 00:00:00 2001 From: realHannes Date: Wed, 18 Sep 2024 15:18:54 +0200 Subject: [PATCH 22/81] move template specializations into namespace prefilterExpressions --- src/index/CompressedBlockPrefiltering.cpp | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/index/CompressedBlockPrefiltering.cpp b/src/index/CompressedBlockPrefiltering.cpp index 1849d9d062..a5e18ceb62 100644 --- a/src/index/CompressedBlockPrefiltering.cpp +++ b/src/index/CompressedBlockPrefiltering.cpp @@ -118,15 +118,6 @@ std::vector RelationalExpressions::evaluate( } }; -//______________________________________________________________________________ -// Necessary instantiation of template specializations -template class RelationalExpressions; -template class RelationalExpressions; -template class RelationalExpressions; -template class RelationalExpressions; -template class RelationalExpressions; -template class RelationalExpressions; - // SECTION LOGICAL OPERATIONS //______________________________________________________________________________ template @@ -167,7 +158,17 @@ std::vector LogicalExpressions::evaluate( } }; +namespace prefilterExpressions { //______________________________________________________________________________ // Necessary instantiation of template specializations +template class RelationalExpressions; +template class RelationalExpressions; +template class RelationalExpressions; +template class RelationalExpressions; +template class RelationalExpressions; +template class RelationalExpressions; + template class LogicalExpressions; template class LogicalExpressions; + +} // namespace prefilterExpressions From d8246f709ab3001b59c1abf1fddded43bb3b3d91 Mon Sep 17 00:00:00 2001 From: realHannes Date: Wed, 18 Sep 2024 15:44:30 +0200 Subject: [PATCH 23/81] fix --- src/index/CompressedBlockPrefiltering.cpp | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/index/CompressedBlockPrefiltering.cpp b/src/index/CompressedBlockPrefiltering.cpp index a5e18ceb62..48a87ddb98 100644 --- a/src/index/CompressedBlockPrefiltering.cpp +++ b/src/index/CompressedBlockPrefiltering.cpp @@ -4,10 +4,7 @@ #include "index/CompressedBlockPrefiltering.h" -using prefilterExpressions::CompOp; -using prefilterExpressions::LogicalExpressions; -using prefilterExpressions::LogicalOperators; -using prefilterExpressions::RelationalExpressions; +using namespace prefilterExpressions; // SECTION RELATIONAL OPERATIONS //______________________________________________________________________________ @@ -40,7 +37,7 @@ auto RelationalExpressions::lowerIndex( using enum CompOp; // Extract evaluationColumn specific ValueId from BlockMetadata auto getRelevantIdFromBlock = - [this, evaluationColumn](const BlockMetadata& blockMetadata) { + [evaluationColumn](const BlockMetadata& blockMetadata) { return Comparison == LT || Comparison == LE || Comparison == NE ? getIdFromColumnIndex(blockMetadata.firstTriple_, evaluationColumn) @@ -62,7 +59,7 @@ auto RelationalExpressions::upperIndex( requires(Comparison == CompOp::EQ || Comparison == CompOp::NE) { // Extract evaluationColumn specific ValueId from BlockMetadata auto getRelevantIdFromBlock = - [this, evaluationColumn](const BlockMetadata& blockMetadata) { + [evaluationColumn](const BlockMetadata& blockMetadata) { return Comparison == CompOp::EQ ? getIdFromColumnIndex(blockMetadata.firstTriple_, evaluationColumn) @@ -158,7 +155,6 @@ std::vector LogicalExpressions::evaluate( } }; -namespace prefilterExpressions { //______________________________________________________________________________ // Necessary instantiation of template specializations template class RelationalExpressions; @@ -170,5 +166,3 @@ template class RelationalExpressions; template class LogicalExpressions; template class LogicalExpressions; - -} // namespace prefilterExpressions From 66b4872939231d2f785b2fcdb2050eb77447dd50 Mon Sep 17 00:00:00 2001 From: realHannes Date: Wed, 18 Sep 2024 16:08:24 +0200 Subject: [PATCH 24/81] hopefully fix namespace issue --- src/index/CompressedBlockPrefiltering.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/index/CompressedBlockPrefiltering.cpp b/src/index/CompressedBlockPrefiltering.cpp index 48a87ddb98..65cb605f6b 100644 --- a/src/index/CompressedBlockPrefiltering.cpp +++ b/src/index/CompressedBlockPrefiltering.cpp @@ -4,7 +4,7 @@ #include "index/CompressedBlockPrefiltering.h" -using namespace prefilterExpressions; +namespace prefilterExpressions { // SECTION RELATIONAL OPERATIONS //______________________________________________________________________________ @@ -166,3 +166,5 @@ template class RelationalExpressions; template class LogicalExpressions; template class LogicalExpressions; + +} // namespace prefilterExpressions From b5d7dd7adc2f69d218287ff3a6ae7e50986b4cf2 Mon Sep 17 00:00:00 2001 From: realHannes Date: Fri, 20 Sep 2024 12:07:37 +0200 Subject: [PATCH 25/81] first adaptations regarding the comments --- src/index/CMakeLists.txt | 5 +- src/index/CompressedBlockPrefiltering.cpp | 56 +++++++++----- src/index/CompressedBlockPrefiltering.h | 90 ++++++++++------------- test/CompressedBlockPrefilteringTest.cpp | 17 +++-- 4 files changed, 89 insertions(+), 79 deletions(-) diff --git a/src/index/CMakeLists.txt b/src/index/CMakeLists.txt index 1a56e78a76..81459f7a26 100644 --- a/src/index/CMakeLists.txt +++ b/src/index/CMakeLists.txt @@ -5,5 +5,6 @@ add_library(index LocatedTriples.cpp Permutation.cpp TextMetaData.cpp DocsDB.cpp FTSAlgorithms.cpp PrefixHeuristic.cpp CompressedRelation.cpp - PatternCreator.cpp ScanSpecification.cpp CompressedBlockPrefiltering.cpp) -qlever_target_link_libraries(index util parser vocabulary ${STXXL_LIBRARIES}) \ No newline at end of file + PatternCreator.cpp ScanSpecification.cpp + CompressedBlockPrefiltering.cpp) +qlever_target_link_libraries(index util parser vocabulary ${STXXL_LIBRARIES}) diff --git a/src/index/CompressedBlockPrefiltering.cpp b/src/index/CompressedBlockPrefiltering.cpp index 65cb605f6b..4456b01e8c 100644 --- a/src/index/CompressedBlockPrefiltering.cpp +++ b/src/index/CompressedBlockPrefiltering.cpp @@ -6,10 +6,28 @@ namespace prefilterExpressions { +// HELPER FUNCTION +//______________________________________________________________________________ +// Given a PermutedTriple retrieve the suitable Id w.r.t. a column (index). +static constexpr auto getIdFromColumnIndex( + const BlockMetadata::PermutedTriple& permutedTriple, size_t columnIndex) { + switch (columnIndex) { + case 0: + return permutedTriple.col0Id_; + case 1: + return permutedTriple.col1Id_; + case 2: + return permutedTriple.col2Id_; + default: + // Triple! + AD_FAIL(); + } +}; + // SECTION RELATIONAL OPERATIONS //______________________________________________________________________________ template -bool RelationalExpressions::compareImpl( +bool RelationalExpression::compareImpl( const ValueId& firstId, const ValueId& secondId) const { using enum CompOp; // For compactness we combine LE and GT here. In evaluate (see below), for @@ -32,8 +50,8 @@ bool RelationalExpressions::compareImpl( //______________________________________________________________________________ template -auto RelationalExpressions::lowerIndex( - std::vector& input, size_t evaluationColumn) { +auto RelationalExpression::lowerIndex( + const std::vector& input, size_t evaluationColumn) const { using enum CompOp; // Extract evaluationColumn specific ValueId from BlockMetadata auto getRelevantIdFromBlock = @@ -54,8 +72,8 @@ auto RelationalExpressions::lowerIndex( //______________________________________________________________________________ template -auto RelationalExpressions::upperIndex( - std::vector& input, size_t evaluationColumn) +auto RelationalExpression::upperIndex( + const std::vector& input, size_t evaluationColumn) const requires(Comparison == CompOp::EQ || Comparison == CompOp::NE) { // Extract evaluationColumn specific ValueId from BlockMetadata auto getRelevantIdFromBlock = @@ -81,8 +99,8 @@ auto RelationalExpressions::upperIndex( //______________________________________________________________________________ template -std::vector RelationalExpressions::evaluate( - std::vector& input, size_t evaluationColumn) { +std::vector RelationalExpression::evaluate( + const std::vector& input, size_t evaluationColumn) const { using enum CompOp; if constexpr (Comparison == LT || Comparison == LE) { return std::vector(input.begin(), @@ -118,8 +136,8 @@ std::vector RelationalExpressions::evaluate( // SECTION LOGICAL OPERATIONS //______________________________________________________________________________ template -std::vector LogicalExpressions::evaluateOr( - std::vector& input, size_t evaluationColumn) +std::vector LogicalExpression::evaluateOr( + const std::vector& input, size_t evaluationColumn) const requires(Operation == LogicalOperators::OR) { auto relevantBlocksChild1 = child1_->evaluate(input, evaluationColumn); auto relevantBlocksChild2 = child2_->evaluate(input, evaluationColumn); @@ -145,8 +163,8 @@ std::vector LogicalExpressions::evaluateOr( //______________________________________________________________________________ template -std::vector LogicalExpressions::evaluate( - std::vector& input, size_t evaluationColumn) { +std::vector LogicalExpression::evaluate( + const std::vector& input, size_t evaluationColumn) const { if constexpr (Operation == LogicalOperators::AND) { auto resultChild1 = child1_->evaluate(input, evaluationColumn); return child2_->evaluate(resultChild1, evaluationColumn); @@ -157,14 +175,14 @@ std::vector LogicalExpressions::evaluate( //______________________________________________________________________________ // Necessary instantiation of template specializations -template class RelationalExpressions; -template class RelationalExpressions; -template class RelationalExpressions; -template class RelationalExpressions; -template class RelationalExpressions; -template class RelationalExpressions; +template class RelationalExpression; +template class RelationalExpression; +template class RelationalExpression; +template class RelationalExpression; +template class RelationalExpression; +template class RelationalExpression; -template class LogicalExpressions; -template class LogicalExpressions; +template class LogicalExpression; +template class LogicalExpression; } // namespace prefilterExpressions diff --git a/src/index/CompressedBlockPrefiltering.h b/src/index/CompressedBlockPrefiltering.h index 67c3da012c..af37d1ee27 100644 --- a/src/index/CompressedBlockPrefiltering.h +++ b/src/index/CompressedBlockPrefiltering.h @@ -11,12 +11,13 @@ #include "global/ValueIdComparators.h" #include "index/CompressedRelation.h" +namespace prefilterExpressions { + // The compressed block metadata (see `CompressedRelation.h`) that we use to // filter out the non-relevant blocks by checking their content of // `firstTriple_` and `lastTriple_` (`PermutedTriple`) using BlockMetadata = CompressedBlockMetadata; -namespace prefilterExpressions { //______________________________________________________________________________ /* `PrefilterExpression` represents a base class for the following sub-classes that @@ -36,8 +37,9 @@ class PrefilterExpression { public: // The respective metadata to the blocks is expected to be provided in // a sorted order (w.r.t. the relevant column). - virtual std::vector evaluate(std::vector& input, - size_t evaluationColumn) = 0; + virtual std::vector evaluate( + const std::vector& input, + size_t evaluationColumn) const = 0; virtual ~PrefilterExpression() = default; }; @@ -49,44 +51,30 @@ class PrefilterExpression { // - LessThan, LessEqual, Equal, NotEqual, GreaterEqual, GreaterThan using CompOp = valueIdComparators::Comparison; -//______________________________________________________________________________ -// Given a PermutedTriple, retrieve the suitable Id w.r.t. a column (index). -constexpr auto getIdFromColumnIndex = - [](const BlockMetadata::PermutedTriple& permutedTriple, - const size_t columnIndex) { - switch (columnIndex) { - case 0: - return permutedTriple.col0Id_; - case 1: - return permutedTriple.col1Id_; - case 2: - return permutedTriple.col2Id_; - default: - // Triple! - AD_FAIL(); - } - }; - //______________________________________________________________________________ template -class RelationalExpressions : public PrefilterExpression { +class RelationalExpression : public PrefilterExpression { + private: + // The ValueId on which we perform the relational comparison on. + ValueId referenceId_; + public: - explicit RelationalExpressions(ValueId referenceId) + explicit RelationalExpression(const ValueId referenceId) : referenceId_(referenceId) {} - std::vector evaluate(std::vector& input, - size_t evaluationColumn) override; + std::vector evaluate(const std::vector& input, + size_t evaluationColumn) const override; private: - // The ValueId the comparison refers to. - ValueId referenceId_; // Helper function that implements the relational comparison on the block // values. bool compareImpl(const ValueId& tripleId, const ValueId& otherId) const; // Helper for providing the lower indices during the evaluation procedure. - auto lowerIndex(std::vector& input, size_t evaluationColumn); + auto lowerIndex(const std::vector& input, + size_t evaluationColumn) const; // Helper to get the upper indices, necessary for EQ and NE. - auto upperIndex(std::vector& input, size_t evaluationColumn) + auto upperIndex(const std::vector& input, + size_t evaluationColumn) const requires(Comparison == CompOp::EQ || Comparison == CompOp::NE); }; @@ -97,44 +85,46 @@ enum struct LogicalOperators { AND, OR, NOT }; //______________________________________________________________________________ template -class LogicalExpressions : public PrefilterExpression { +class LogicalExpression : public PrefilterExpression { + private: + std::unique_ptr child1_; + std::unique_ptr child2_; + public: - explicit LogicalExpressions(std::unique_ptr child1, - std::unique_ptr child2) + explicit LogicalExpression(std::unique_ptr child1, + std::unique_ptr child2) : child1_(std::move(child1)), child2_(std::move(child2)) {} - std::vector evaluate(std::vector& input, - size_t evaluationColumn) override; + std::vector evaluate(const std::vector& input, + size_t evaluationColumn) const override; private: - std::unique_ptr child1_; - std::unique_ptr child2_; // Introduce a helper method for the specification LogicalOperators::OR - std::vector evaluateOr(std::vector& input, - size_t evaluationColumn) + std::vector evaluateOr(const std::vector& input, + size_t evaluationColumn) const requires(Operation == LogicalOperators::OR); }; -} // namespace prefilterExpressions - //______________________________________________________________________________ -// Definition of the RelationalExpressions for LT, LE, EQ, NE, GE and GT. -using LessThanExpression = prefilterExpressions::RelationalExpressions< +// Definition of the RelationalExpression for LT, LE, EQ, NE, GE and GT. +using LessThanExpression = prefilterExpressions::RelationalExpression< prefilterExpressions::CompOp::LT>; -using LessEqualExpression = prefilterExpressions::RelationalExpressions< +using LessEqualExpression = prefilterExpressions::RelationalExpression< prefilterExpressions::CompOp::LE>; -using EqualExpression = prefilterExpressions::RelationalExpressions< +using EqualExpression = prefilterExpressions::RelationalExpression< prefilterExpressions::CompOp::EQ>; -using NotEqualExpression = prefilterExpressions::RelationalExpressions< +using NotEqualExpression = prefilterExpressions::RelationalExpression< prefilterExpressions::CompOp::NE>; -using GreaterEqualExpression = prefilterExpressions::RelationalExpressions< +using GreaterEqualExpression = prefilterExpressions::RelationalExpression< prefilterExpressions::CompOp::GE>; -using GreaterThanExpression = prefilterExpressions::RelationalExpressions< +using GreaterThanExpression = prefilterExpressions::RelationalExpression< prefilterExpressions::CompOp::GT>; //______________________________________________________________________________ -// Definition of the LogicalExpressions for AND and OR. -using AndExpression = prefilterExpressions::LogicalExpressions< +// Definition of the LogicalExpression for AND and OR. +using AndExpression = prefilterExpressions::LogicalExpression< prefilterExpressions::LogicalOperators::AND>; -using OrExpression = prefilterExpressions::LogicalExpressions< +using OrExpression = prefilterExpressions::LogicalExpression< prefilterExpressions::LogicalOperators::OR>; + +} // namespace prefilterExpressions diff --git a/test/CompressedBlockPrefilteringTest.cpp b/test/CompressedBlockPrefilteringTest.cpp index 3f99cc3448..a66cee6e9d 100644 --- a/test/CompressedBlockPrefilteringTest.cpp +++ b/test/CompressedBlockPrefilteringTest.cpp @@ -14,6 +14,7 @@ using ad_utility::testing::DoubleId; using ad_utility::testing::IntId; using ad_utility::testing::UndefId; using sparqlExpression::TestContext; +using namespace prefilterExpressions; //______________________________________________________________________________ struct MetadataBlocks { @@ -68,7 +69,7 @@ struct MetadataBlocks { // Test LessThanExpression // Remark: We compare w.r.t. the first triple! -TEST(RelationalExpressions, testLessThanExpressions) { +TEST(RelationalExpression, testLessThanExpressions) { MetadataBlocks blocks{}; std::vector expectedResult = {}; // NUMERIC @@ -118,7 +119,7 @@ TEST(RelationalExpressions, testLessThanExpressions) { //______________________________________________________________________________ // Test LessEqualExpression // Remark: We compare w.r.t. the first triple -TEST(RelationalExpressions, testLessEqualExpressions) { +TEST(RelationalExpression, testLessEqualExpressions) { MetadataBlocks blocks{}; std::vector expectedResult = {}; // NUMERIC @@ -157,7 +158,7 @@ TEST(RelationalExpressions, testLessEqualExpressions) { //______________________________________________________________________________ // Test GreaterThanExpression // Remark: We compare w.r.t. the last triple -TEST(RelationalExpressions, testGreaterThanExpression) { +TEST(RelationalExpression, testGreaterThanExpression) { MetadataBlocks blocks{}; EXPECT_EQ(blocks.numericBlocks, GreaterThanExpression{IntId(10)}.evaluate(blocks.numericBlocks, 0)); @@ -199,7 +200,7 @@ TEST(RelationalExpressions, testGreaterThanExpression) { //______________________________________________________________________________ // Test GreaterEqualExpression // Remark: We compare w.r.t. the last triple -TEST(RelationalExpressions, testGreaterEqualExpression) { +TEST(RelationalExpression, testGreaterEqualExpression) { MetadataBlocks blocks{}; EXPECT_EQ(blocks.numericBlocks, GreaterEqualExpression{IntId(10)}.evaluate( blocks.numericBlocks, 0)); @@ -245,7 +246,7 @@ TEST(RelationalExpressions, testGreaterEqualExpression) { //______________________________________________________________________________ // Test EqualExpression -TEST(RelationalExpressions, testEqualExpression) { +TEST(RelationalExpression, testEqualExpression) { MetadataBlocks blocks{}; std::vector expectedResult = {}; EXPECT_EQ(expectedResult, @@ -296,7 +297,7 @@ TEST(RelationalExpressions, testEqualExpression) { //______________________________________________________________________________ // Test NotEqualExpression -TEST(RelationalExpressions, testNotEqualExpression) { +TEST(RelationalExpression, testNotEqualExpression) { MetadataBlocks blocks{}; std::vector expectedResult{}; EXPECT_EQ(blocks.numericBlocks, @@ -357,7 +358,7 @@ TEST(RelationalExpressions, testNotEqualExpression) { // Test Logical Expressions // Test AndExpression -TEST(LogicalExpressions, testAndExpression) { +TEST(LogicalExpression, testAndExpression) { MetadataBlocks blocks{}; std::vector expectedResult{}; EXPECT_EQ(expectedResult, @@ -425,7 +426,7 @@ TEST(LogicalExpressions, testAndExpression) { //______________________________________________________________________________ // Test OrExpression -TEST(LogicalExpressions, testOrExpression) { +TEST(LogicalExpression, testOrExpression) { MetadataBlocks blocks{}; std::vector expectedResult = {blocks.nb1, blocks.nb4, blocks.nb5}; From 662b1d4cc153d7d9451c832f3e3a1fc6341a25f2 Mon Sep 17 00:00:00 2001 From: realHannes Date: Tue, 24 Sep 2024 18:18:23 +0200 Subject: [PATCH 26/81] applied suggestions from review --- src/global/ValueIdComparators.h | 30 ++- src/index/CompressedBlockPrefiltering.cpp | 220 +++++++++++----------- src/index/CompressedBlockPrefiltering.h | 29 +-- test/CompressedBlockPrefilteringTest.cpp | 102 +++++++++- test/SparqlExpressionTestHelpers.h | 80 ++++---- 5 files changed, 281 insertions(+), 180 deletions(-) diff --git a/src/global/ValueIdComparators.h b/src/global/ValueIdComparators.h index 9f46aab872..4acc0ef875 100644 --- a/src/global/ValueIdComparators.h +++ b/src/global/ValueIdComparators.h @@ -349,9 +349,12 @@ inline std::vector> getRangesForIndexTypes( // Helper function: Sort the non-overlapping ranges in `input` by the first // element, remove the empty ranges, and merge directly adjacent ranges inline auto simplifyRanges = - [](std::vector> input) { - // Eliminate empty ranges - std::erase_if(input, [](const auto& p) { return p.first == p.second; }); + [](std::vector> input, + bool removeEmptyRanges = true) { + if (removeEmptyRanges) { + // Eliminate empty ranges + std::erase_if(input, [](const auto& p) { return p.first == p.second; }); + } std::sort(input.begin(), input.end()); if (input.empty()) { return input; @@ -378,9 +381,13 @@ inline auto simplifyRanges = // 2. The condition x `comparison` value is fulfilled, where value is the value // of `valueId`. // 3. The datatype of x and `valueId` are compatible. +// +// When setting the flag argument `removeEmptyRanges` to false, empty ranges +// [`begin`, `end`] where `begin` is equal to `end` will not be discarded. template inline std::vector> getRangesForId( - RandomIt begin, RandomIt end, ValueId valueId, Comparison comparison) { + RandomIt begin, RandomIt end, ValueId valueId, Comparison comparison, + bool removeEmptyRanges = true) { // For the evaluation of FILTERs, comparisons that involve undefined values // are always false. if (valueId.getDatatype() == Datatype::Undefined) { @@ -389,11 +396,15 @@ inline std::vector> getRangesForId( // This lambda enforces the invariants `non-empty` and `sorted`. switch (valueId.getDatatype()) { case Datatype::Double: - return detail::simplifyRanges(detail::getRangesForIntsAndDoubles( - begin, end, valueId.getDouble(), comparison)); + return detail::simplifyRanges( + detail::getRangesForIntsAndDoubles(begin, end, valueId.getDouble(), + comparison), + removeEmptyRanges); case Datatype::Int: - return detail::simplifyRanges(detail::getRangesForIntsAndDoubles( - begin, end, valueId.getInt(), comparison)); + return detail::simplifyRanges( + detail::getRangesForIntsAndDoubles(begin, end, valueId.getInt(), + comparison), + removeEmptyRanges); case Datatype::Undefined: case Datatype::VocabIndex: case Datatype::LocalVocabIndex: @@ -404,7 +415,8 @@ inline std::vector> getRangesForId( case Datatype::BlankNodeIndex: // For `Date` the trivial comparison via bits is also correct. return detail::simplifyRanges( - detail::getRangesForIndexTypes(begin, end, valueId, comparison)); + detail::getRangesForIndexTypes(begin, end, valueId, comparison), + removeEmptyRanges); } AD_FAIL(); } diff --git a/src/index/CompressedBlockPrefiltering.cpp b/src/index/CompressedBlockPrefiltering.cpp index 4456b01e8c..85e8286a92 100644 --- a/src/index/CompressedBlockPrefiltering.cpp +++ b/src/index/CompressedBlockPrefiltering.cpp @@ -4,6 +4,8 @@ #include "index/CompressedBlockPrefiltering.h" +#include "global/ValueIdComparators.h" + namespace prefilterExpressions { // HELPER FUNCTION @@ -27,137 +29,136 @@ static constexpr auto getIdFromColumnIndex( // SECTION RELATIONAL OPERATIONS //______________________________________________________________________________ template -bool RelationalExpression::compareImpl( - const ValueId& firstId, const ValueId& secondId) const { +std::unique_ptr +RelationalExpression::logicalComplement() const { using enum CompOp; - // For compactness we combine LE and GT here. In evaluate (see below), for - // LE we select all blocks at a smaller (lower_bound) index position, and - // for GT all the respective blocks at larger or equal indices. - if constexpr (Comparison == LE || Comparison == GT) { - return compareIds(firstId, secondId, LE) == - valueIdComparators::ComparisonResult::True; - } else { - // Follows the same logic for evaluation as explained above. - // Used for CompOp::LT, CompOp::GE, CompOp::EQ and CompOp::NE. - // For CompOp::EQ we also use the LT operation: This is possible because in - // evaluate we calculate a lower_bound and upper_bound over this LT - // operation, and those bounds act as the plausible range for equality - // (evaluation of CompOp::NE follows a similar approach). - return compareIds(firstId, secondId, LT) == - valueIdComparators::ComparisonResult::True; + switch (Comparison) { + case LT: + // Complement X < Y: X >= Y + return std::make_unique(referenceId_); + case LE: + // Complement X <= Y: X > Y + return std::make_unique(referenceId_); + case EQ: + // Complement X == Y: X != Y + return std::make_unique(referenceId_); + case NE: + // Complement X != Y: X == Y + return std::make_unique(referenceId_); + case GE: + // Complement X >= Y: X < Y + return std::make_unique(referenceId_); + case GT: + // Complement X > Y: X <= Y + return std::make_unique(referenceId_); + default: + AD_FAIL(); } -} +}; //______________________________________________________________________________ template -auto RelationalExpression::lowerIndex( +std::vector RelationalExpression::evaluate( const std::vector& input, size_t evaluationColumn) const { - using enum CompOp; - // Extract evaluationColumn specific ValueId from BlockMetadata - auto getRelevantIdFromBlock = - [evaluationColumn](const BlockMetadata& blockMetadata) { - return Comparison == LT || Comparison == LE || Comparison == NE - ? getIdFromColumnIndex(blockMetadata.firstTriple_, - evaluationColumn) - : getIdFromColumnIndex(blockMetadata.lastTriple_, - evaluationColumn); - }; - return std::ranges::lower_bound( - input, referenceId_, - [this](const ValueId& blockId, const ValueId& referenceId) { - return compareImpl(blockId, referenceId); - }, - getRelevantIdFromBlock); -}; + using namespace valueIdComparators; + std::vector valueIdsInput; + // For each BlockMetadata value in vector input, we have a respective Id for + // firstTriple and lastTriple + valueIdsInput.reserve(2 * input.size()); -//______________________________________________________________________________ -template -auto RelationalExpression::upperIndex( - const std::vector& input, size_t evaluationColumn) const - requires(Comparison == CompOp::EQ || Comparison == CompOp::NE) { - // Extract evaluationColumn specific ValueId from BlockMetadata - auto getRelevantIdFromBlock = - [evaluationColumn](const BlockMetadata& blockMetadata) { - return Comparison == CompOp::EQ - ? getIdFromColumnIndex(blockMetadata.firstTriple_, - evaluationColumn) - : getIdFromColumnIndex(blockMetadata.lastTriple_, - evaluationColumn); - }; - // Use the same comparison operations compareImpl as for the method lowerIndex - // (with std::ranges::lower_bound). - // However, the difference here is the helper lambda getRelevantIdFromBlock(), - // for CompOp::EQ we have to consider the first triple of the respective - // BlockMetadata value. - return std::ranges::upper_bound( - input, referenceId_, - [this](const ValueId& blockId, const ValueId& referenceId) { - return compareImpl(blockId, referenceId); - }, - getRelevantIdFromBlock); + for (const auto& block : input) { + valueIdsInput.push_back( + getIdFromColumnIndex(block.firstTriple_, evaluationColumn)); + valueIdsInput.push_back( + getIdFromColumnIndex(block.lastTriple_, evaluationColumn)); + } + // Use getRangesForId (from valueIdComparators) to extract the ranges + // containing the relevant ValueIds. + // For pre-filtering with CompOp::EQ, we have to consider empty ranges. + // Reason: The referenceId_ could be contained within the bounds formed by + // the IDs of firstTriple_ and lastTriple_ (set false flag to keep + // empty ranges). + auto relevantIdRanges = + Comparison != CompOp::EQ + ? getRangesForId(valueIdsInput.begin(), valueIdsInput.end(), + referenceId_, Comparison) + : getRangesForId(valueIdsInput.begin(), valueIdsInput.end(), + referenceId_, Comparison, false); + // The vector for relevant BlockMetadata values that contain ValueIds defined + // as relevant relevantIdRanges. + std::vector relevantBlocks; + // Reserve memory, input.size() is upper bound. + relevantBlocks.reserve(input.size()); + // Given the relevant Id ranges, retrieve the corresponding relevant + // BlockMetadata values from vector input and add them to the relevantBlocks + // vector. + for (const auto& range : relevantIdRanges) { + relevantBlocks.insert( + relevantBlocks.end(), + input.begin() + std::distance(valueIdsInput.begin(), range.first) / 2, + // Round up, for Ids contained within the bounding Ids of firstTriple + // and lastTriple we have to include the respective metadata block + // (that block is partially relevant). + input.begin() + + std::distance(valueIdsInput.begin(), range.second + 1) / 2); + } + relevantBlocks.shrink_to_fit(); + return relevantBlocks; }; +// SECTION LOGICAL OPERATIONS //______________________________________________________________________________ -template -std::vector RelationalExpression::evaluate( - const std::vector& input, size_t evaluationColumn) const { - using enum CompOp; - if constexpr (Comparison == LT || Comparison == LE) { - return std::vector(input.begin(), - lowerIndex(input, evaluationColumn)); - } else if constexpr (Comparison == GE || Comparison == GT) { - // Complementary block selection to LT and LE - return std::vector(lowerIndex(input, evaluationColumn), - input.end()); - } else if constexpr (Comparison == EQ) { - return std::vector(lowerIndex(input, evaluationColumn), - upperIndex(input, evaluationColumn)); - } else { - // CompOP::NE - // Get the indices that define the boundaries w.r.t. blocks potentially - // containing non-equal values. - auto firstBound = lowerIndex(input, evaluationColumn); - auto secondBound = upperIndex(input, evaluationColumn); - if (firstBound > secondBound) { - return input; // all blocks are relevant - } - // Extract the values from the respective ranges defined by the bounding - // indices from vector input. - // [input.begin(), firstBound) and [secondBound, input.end()] - std::vector vecNe; - vecNe.reserve(std::distance(input.begin(), firstBound) + - std::distance(secondBound, input.end())); - vecNe.insert(vecNe.end(), input.begin(), firstBound); - vecNe.insert(vecNe.end(), secondBound, input.end()); - return vecNe; +template +std::unique_ptr +LogicalExpression::logicalComplement() const { + using enum LogicalOperators; + switch (Operation) { + case NOT: + // Logically we complement (negate) a NOT here => NOT cancels out. + // Therefore, we can simply return the child of the respective NOT + // expression after undoing its previous complementation. + return std::move(child1_->logicalComplement()); + // Source De-Morgan's laws: De Morgan's laws, Wikipedia. + // Reference: https://en.wikipedia.org/wiki/De_Morgan%27s_laws + case OR: + AD_CONTRACT_CHECK(child2_.has_value()); + // De Morgan's law: not (A or B) = (not A) and (not B) + return std::make_unique( + child1_->logicalComplement(), child2_.value()->logicalComplement()); + case AND: + AD_CONTRACT_CHECK(child2_.has_value()); + // De Morgan's law: not (A and B) = (not A) or (not B) + return std::make_unique( + child1_->logicalComplement(), child2_.value()->logicalComplement()); + default: + AD_FAIL(); } }; -// SECTION LOGICAL OPERATIONS //______________________________________________________________________________ template std::vector LogicalExpression::evaluateOr( const std::vector& input, size_t evaluationColumn) const requires(Operation == LogicalOperators::OR) { auto relevantBlocksChild1 = child1_->evaluate(input, evaluationColumn); - auto relevantBlocksChild2 = child2_->evaluate(input, evaluationColumn); + auto relevantBlocksChild2 = + child2_.value()->evaluate(input, evaluationColumn); // The result vector, reserve space for the logical upper bound (num blocks) // |relevantBlocksChild1| + |relevantBlocksChild2| regarding the OR // operation (union of blocks) std::vector unionedRelevantBlocks; unionedRelevantBlocks.reserve(relevantBlocksChild1.size() + relevantBlocksChild2.size()); - // lambda function that implements the less-than comparison required for - // std::set_union - auto lessThan = [evaluationColumn](const BlockMetadata& block1, - const BlockMetadata& block2) { - return getIdFromColumnIndex(block1.lastTriple_, evaluationColumn) < - getIdFromColumnIndex(block2.lastTriple_, evaluationColumn); + // Lambda function that implements the projection to retrieve ValueIds from + // BlockMetadata values. + auto retrieveValueId = [evaluationColumn](const BlockMetadata& block) { + return getIdFromColumnIndex(block.lastTriple_, evaluationColumn); }; // 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(relevantBlocksChild1, relevantBlocksChild2, - std::back_inserter(unionedRelevantBlocks), lessThan); + std::back_inserter(unionedRelevantBlocks), + std::ranges::less{}, retrieveValueId, retrieveValueId); return unionedRelevantBlocks; }; @@ -165,11 +166,19 @@ std::vector LogicalExpression::evaluateOr( template std::vector LogicalExpression::evaluate( const std::vector& input, size_t evaluationColumn) const { - if constexpr (Operation == LogicalOperators::AND) { + using enum LogicalOperators; + if constexpr (Operation == AND) { auto resultChild1 = child1_->evaluate(input, evaluationColumn); - return child2_->evaluate(resultChild1, evaluationColumn); - } else { + AD_CONTRACT_CHECK(child2_.has_value()); + return child2_.value()->evaluate(resultChild1, evaluationColumn); + } else if constexpr (Operation == OR) { + AD_CONTRACT_CHECK(child2_.has_value()); return evaluateOr(input, evaluationColumn); + } else { + static_assert(Operation == NOT); + // Given that the child expression has already been negated during the (NOT) + // expression construction, we just have to evaluate it. + return child1_->evaluate(input, evaluationColumn); } }; @@ -184,5 +193,6 @@ template class RelationalExpression; template class LogicalExpression; template class LogicalExpression; +template class LogicalExpression; } // namespace prefilterExpressions diff --git a/src/index/CompressedBlockPrefiltering.h b/src/index/CompressedBlockPrefiltering.h index af37d1ee27..7887733607 100644 --- a/src/index/CompressedBlockPrefiltering.h +++ b/src/index/CompressedBlockPrefiltering.h @@ -35,12 +35,15 @@ Logical Operations - `and` and `or` class PrefilterExpression { public: + virtual ~PrefilterExpression() = default; + + virtual std::unique_ptr logicalComplement() const = 0; + // The respective metadata to the blocks is expected to be provided in // a sorted order (w.r.t. the relevant column). virtual std::vector evaluate( const std::vector& input, size_t evaluationColumn) const = 0; - virtual ~PrefilterExpression() = default; }; //______________________________________________________________________________ @@ -62,20 +65,10 @@ class RelationalExpression : public PrefilterExpression { explicit RelationalExpression(const ValueId referenceId) : referenceId_(referenceId) {} + std::unique_ptr logicalComplement() const override; + std::vector evaluate(const std::vector& input, size_t evaluationColumn) const override; - - private: - // Helper function that implements the relational comparison on the block - // values. - bool compareImpl(const ValueId& tripleId, const ValueId& otherId) const; - // Helper for providing the lower indices during the evaluation procedure. - auto lowerIndex(const std::vector& input, - size_t evaluationColumn) const; - // Helper to get the upper indices, necessary for EQ and NE. - auto upperIndex(const std::vector& input, - size_t evaluationColumn) const - requires(Comparison == CompOp::EQ || Comparison == CompOp::NE); }; //______________________________________________________________________________ @@ -88,12 +81,18 @@ template class LogicalExpression : public PrefilterExpression { private: std::unique_ptr child1_; - std::unique_ptr child2_; + std::optional> child2_; public: + // AND and OR explicit LogicalExpression(std::unique_ptr child1, std::unique_ptr child2) : child1_(std::move(child1)), child2_(std::move(child2)) {} + // NOT + explicit LogicalExpression(std::unique_ptr child1) + : child1_(std::move(child1->logicalComplement())), child2_(nullptr) {} + + std::unique_ptr logicalComplement() const override; std::vector evaluate(const std::vector& input, size_t evaluationColumn) const override; @@ -126,5 +125,7 @@ using AndExpression = prefilterExpressions::LogicalExpression< prefilterExpressions::LogicalOperators::AND>; using OrExpression = prefilterExpressions::LogicalExpression< prefilterExpressions::LogicalOperators::OR>; +using NotExpression = prefilterExpressions::LogicalExpression< + prefilterExpressions::LogicalOperators::NOT>; } // namespace prefilterExpressions diff --git a/test/CompressedBlockPrefilteringTest.cpp b/test/CompressedBlockPrefilteringTest.cpp index a66cee6e9d..78f660d5c8 100644 --- a/test/CompressedBlockPrefilteringTest.cpp +++ b/test/CompressedBlockPrefilteringTest.cpp @@ -68,7 +68,6 @@ struct MetadataBlocks { // Test Relational Expressions // Test LessThanExpression -// Remark: We compare w.r.t. the first triple! TEST(RelationalExpression, testLessThanExpressions) { MetadataBlocks blocks{}; std::vector expectedResult = {}; @@ -118,7 +117,6 @@ TEST(RelationalExpression, testLessThanExpressions) { //______________________________________________________________________________ // Test LessEqualExpression -// Remark: We compare w.r.t. the first triple TEST(RelationalExpression, testLessEqualExpressions) { MetadataBlocks blocks{}; std::vector expectedResult = {}; @@ -157,7 +155,6 @@ TEST(RelationalExpression, testLessEqualExpressions) { //______________________________________________________________________________ // Test GreaterThanExpression -// Remark: We compare w.r.t. the last triple TEST(RelationalExpression, testGreaterThanExpression) { MetadataBlocks blocks{}; EXPECT_EQ(blocks.numericBlocks, @@ -199,7 +196,6 @@ TEST(RelationalExpression, testGreaterThanExpression) { //______________________________________________________________________________ // Test GreaterEqualExpression -// Remark: We compare w.r.t. the last triple TEST(RelationalExpression, testGreaterEqualExpression) { MetadataBlocks blocks{}; EXPECT_EQ(blocks.numericBlocks, GreaterEqualExpression{IntId(10)}.evaluate( @@ -481,3 +477,101 @@ TEST(LogicalExpression, testOrExpression) { std::make_unique(IntId(16.35))) .evaluate(blocks.numericBlocks, 2)); } + +//______________________________________________________________________________ +// Test NotExpression +TEST(LogicalExpression, testNotExpression) { + MetadataBlocks blocks{}; + TestContext tc{}; + std::vector expectedResult = {}; + EXPECT_EQ(expectedResult, + NotExpression(std::make_unique(IntId(16))) + .evaluate(blocks.numericBlocks, 0)); + EXPECT_EQ( + expectedResult, + NotExpression(std::make_unique( + std::make_unique(tc.munich))) + .evaluate(blocks.vocabBlocks, 1)); + EXPECT_EQ(expectedResult, + NotExpression(std::make_unique( + std::make_unique(tc.zz))) + .evaluate(blocks.vocabBlocks, 1)); + expectedResult = {blocks.vb4, blocks.vb5}; + EXPECT_EQ( + expectedResult, + NotExpression(std::make_unique( + std::make_unique(tc.karlsruhe))) + .evaluate(blocks.vocabBlocks, 1)); + EXPECT_EQ( + blocks.numericBlocks, + NotExpression(std::make_unique( + std::make_unique(IntId(16)))) + .evaluate(blocks.numericBlocks, 0)); + expectedResult = {blocks.vb1, blocks.vb2, blocks.vb3}; + EXPECT_EQ(expectedResult, + NotExpression(std::make_unique(tc.frankfurt)) + .evaluate(blocks.vocabBlocks, 1)); + EXPECT_EQ(blocks.vocabBlocks, + NotExpression(std::make_unique(tc.berlin)) + .evaluate(blocks.vocabBlocks, 1)); + expectedResult = {blocks.vb1, blocks.vb2}; + EXPECT_EQ( + expectedResult, + NotExpression(std::make_unique(tc.düsseldorf)) + .evaluate(blocks.vocabBlocks, 1)); + EXPECT_EQ(blocks.numericBlocks, + NotExpression( + std::make_unique( + std::make_unique(IntId(13)), + std::make_unique(DoubleId(111.01)))) + .evaluate(blocks.numericBlocks, 2)); + expectedResult = {}; + EXPECT_EQ( + expectedResult, + NotExpression( + std::make_unique(std::make_unique( + std::make_unique(IntId(13)), + std::make_unique(DoubleId(111.01))))) + .evaluate(blocks.numericBlocks, 2)); + expectedResult = {blocks.vb4, blocks.vb5}; + EXPECT_EQ( + expectedResult, + NotExpression(std::make_unique( + std::make_unique(tc.munich), + std::make_unique(tc.ingolstadt))) + .evaluate(blocks.vocabBlocks, 1)); + expectedResult = {blocks.vb2, blocks.vb3, blocks.vb4}; + EXPECT_EQ( + expectedResult, + NotExpression(std::make_unique( + std::make_unique(tc.hamburg), + std::make_unique(tc.düsseldorf))) + .evaluate(blocks.vocabBlocks, 1)); + expectedResult = {blocks.nb2, blocks.nb3, blocks.nb4}; + EXPECT_EQ(expectedResult, + NotExpression(std::make_unique( + std::make_unique(DoubleId(0.0)), + std::make_unique(IntId(6)))) + .evaluate(blocks.numericBlocks, 1)); + expectedResult = {blocks.nb1, blocks.nb2, blocks.nb4, blocks.nb5}; + EXPECT_EQ( + expectedResult, + NotExpression(std::make_unique( + std::make_unique(DoubleId(0.0)), + std::make_unique(IntId(6)))) + .evaluate(blocks.numericBlocks, 1)); + expectedResult = {blocks.nb3, blocks.nb4}; + EXPECT_EQ( + expectedResult, + NotExpression(std::make_unique( + std::make_unique(DoubleId(42.0)), + std::make_unique(IntId(48)))) + .evaluate(blocks.numericBlocks, 0)); + expectedResult = {blocks.nb1, blocks.nb2, blocks.nb3, blocks.nb5}; + EXPECT_EQ(expectedResult, + NotExpression( + std::make_unique(std::make_unique( + std::make_unique(DoubleId(42.25)), + std::make_unique(IntId(51))))) + .evaluate(blocks.numericBlocks, 0)); +} diff --git a/test/SparqlExpressionTestHelpers.h b/test/SparqlExpressionTestHelpers.h index dc4d627ab2..80cdae8a12 100644 --- a/test/SparqlExpressionTestHelpers.h +++ b/test/SparqlExpressionTestHelpers.h @@ -76,58 +76,42 @@ struct TestContext { zz = getId("\"zz\"@en"); blank = Id::makeFromBlankNodeIndex(BlankNodeIndex::make(0)); - constexpr auto lit = [](std::string_view s) { - return ad_utility::triple_component::LiteralOrIri::literalWithoutQuotes( - s); + auto addLocalLiteral = [this](std::string_view s) { + return Id::makeFromLocalVocabIndex( + this->localVocab.getIndexAndAddIfNotContained( + ad_utility::triple_component::LiteralOrIri::literalWithoutQuotes( + s))); }; - constexpr auto iri = [](const std::string& s) { - return ad_utility::triple_component::LiteralOrIri::iriref(s); + + auto addLocalIri = [this](const std::string& s) { + return Id::makeFromLocalVocabIndex( + this->localVocab.getIndexAndAddIfNotContained( + ad_utility::triple_component::LiteralOrIri::iriref(s))); }; - notInVocabA = Id::makeFromLocalVocabIndex( - localVocab.getIndexAndAddIfNotContained(lit("notInVocabA"))); - notInVocabB = Id::makeFromLocalVocabIndex( - localVocab.getIndexAndAddIfNotContained(lit("notInVocabB"))); - notInVocabC = Id::makeFromLocalVocabIndex( - localVocab.getIndexAndAddIfNotContained(iri(""))); - notInVocabD = Id::makeFromLocalVocabIndex( - localVocab.getIndexAndAddIfNotContained(iri(""))); - notInVocabAelpha = Id::makeFromLocalVocabIndex( - localVocab.getIndexAndAddIfNotContained(lit("notInVocabÄlpha"))); - notInVocabAelpha = Id::makeFromLocalVocabIndex( - localVocab.getIndexAndAddIfNotContained(lit("notInVocabÄlpha"))); + notInVocabA = addLocalLiteral("notInVocabA"); + notInVocabB = addLocalLiteral("notInVocabB"); + notInVocabC = addLocalIri(""); + notInVocabD = addLocalIri(""); + notInVocabAelpha = addLocalLiteral("notInVocabÄlpha"); + notInVocabAelpha = addLocalLiteral("notInVocabÄlpha"); notInVocabIri = - Id::makeFromLocalVocabIndex(localVocab.getIndexAndAddIfNotContained( - iri(""))); - notInVocabIriLit = - Id::makeFromLocalVocabIndex(localVocab.getIndexAndAddIfNotContained( - lit("http://www.w3.org/1999/02/22-rdf-syntax-ns#langString"))); - berlin = Id::makeFromLocalVocabIndex( - localVocab.getIndexAndAddIfNotContained(lit("berlin"))); - bonn = Id::makeFromLocalVocabIndex( - localVocab.getIndexAndAddIfNotContained(lit("bonn"))); - cologne = Id::makeFromLocalVocabIndex( - localVocab.getIndexAndAddIfNotContained(lit("cologne"))); - düsseldorf = Id::makeFromLocalVocabIndex( - localVocab.getIndexAndAddIfNotContained(lit("düsseldorf"))); - dortmund = Id::makeFromLocalVocabIndex( - localVocab.getIndexAndAddIfNotContained(lit("dortmund"))); - essen = Id::makeFromLocalVocabIndex( - localVocab.getIndexAndAddIfNotContained(lit("essen"))); - frankfurt = Id::makeFromLocalVocabIndex( - localVocab.getIndexAndAddIfNotContained(lit("frankfurt"))); - frankfurt_oder = Id::makeFromLocalVocabIndex( - localVocab.getIndexAndAddIfNotContained(lit("frankfurt (FFO)"))); - hamburg = Id::makeFromLocalVocabIndex( - localVocab.getIndexAndAddIfNotContained(lit("hamburg"))); - hannover = Id::makeFromLocalVocabIndex( - localVocab.getIndexAndAddIfNotContained(lit("hannover"))); - ingolstadt = Id::makeFromLocalVocabIndex( - localVocab.getIndexAndAddIfNotContained(lit("ingolstadt"))); - karlsruhe = Id::makeFromLocalVocabIndex( - localVocab.getIndexAndAddIfNotContained(lit("karlsruhe"))); - munich = Id::makeFromLocalVocabIndex( - localVocab.getIndexAndAddIfNotContained(lit("munich"))); + addLocalIri(""); + notInVocabIriLit = addLocalLiteral( + "http://www.w3.org/1999/02/22-rdf-syntax-ns#langString"); + berlin = addLocalLiteral("berlin"); + bonn = addLocalLiteral("bonn"); + cologne = addLocalLiteral("cologne"); + düsseldorf = addLocalLiteral("düsseldorf"); + dortmund = addLocalLiteral("dortmund"); + essen = addLocalLiteral("essen"); + frankfurt = addLocalLiteral("frankfurt"); + frankfurt_oder = addLocalLiteral("frankfurt (FFO)"); + hamburg = addLocalLiteral("hamburg"); + hannover = addLocalLiteral("hannover"); + ingolstadt = addLocalLiteral("ingolstadt"); + karlsruhe = addLocalLiteral("karlsruhe"); + munich = addLocalLiteral("munich"); // Set up the `table` that represents the previous partial query results. It // has five columns/variables: ?ints (only integers), ?doubles (only From 510aa9e773a0c5c58ace19b3b5a03cc2751f00a8 Mon Sep 17 00:00:00 2001 From: realHannes Date: Tue, 24 Sep 2024 21:06:48 +0200 Subject: [PATCH 27/81] fix std::move --- src/index/CompressedBlockPrefiltering.cpp | 11 ++++------- src/index/CompressedBlockPrefiltering.h | 2 +- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/index/CompressedBlockPrefiltering.cpp b/src/index/CompressedBlockPrefiltering.cpp index 85e8286a92..91c673b9a0 100644 --- a/src/index/CompressedBlockPrefiltering.cpp +++ b/src/index/CompressedBlockPrefiltering.cpp @@ -92,15 +92,14 @@ std::vector RelationalExpression::evaluate( // Given the relevant Id ranges, retrieve the corresponding relevant // BlockMetadata values from vector input and add them to the relevantBlocks // vector. - for (const auto& range : relevantIdRanges) { + for (const auto& [firstId, secondId] : relevantIdRanges) { relevantBlocks.insert( relevantBlocks.end(), - input.begin() + std::distance(valueIdsInput.begin(), range.first) / 2, + input.begin() + std::distance(valueIdsInput.begin(), firstId) / 2, // Round up, for Ids contained within the bounding Ids of firstTriple // and lastTriple we have to include the respective metadata block // (that block is partially relevant). - input.begin() + - std::distance(valueIdsInput.begin(), range.second + 1) / 2); + input.begin() + std::distance(valueIdsInput.begin(), secondId + 1) / 2); } relevantBlocks.shrink_to_fit(); return relevantBlocks; @@ -117,7 +116,7 @@ LogicalExpression::logicalComplement() const { // Logically we complement (negate) a NOT here => NOT cancels out. // Therefore, we can simply return the child of the respective NOT // expression after undoing its previous complementation. - return std::move(child1_->logicalComplement()); + return child1_->logicalComplement(); // Source De-Morgan's laws: De Morgan's laws, Wikipedia. // Reference: https://en.wikipedia.org/wiki/De_Morgan%27s_laws case OR: @@ -176,8 +175,6 @@ std::vector LogicalExpression::evaluate( return evaluateOr(input, evaluationColumn); } else { static_assert(Operation == NOT); - // Given that the child expression has already been negated during the (NOT) - // expression construction, we just have to evaluate it. return child1_->evaluate(input, evaluationColumn); } }; diff --git a/src/index/CompressedBlockPrefiltering.h b/src/index/CompressedBlockPrefiltering.h index 7887733607..8cfe559091 100644 --- a/src/index/CompressedBlockPrefiltering.h +++ b/src/index/CompressedBlockPrefiltering.h @@ -90,7 +90,7 @@ class LogicalExpression : public PrefilterExpression { : child1_(std::move(child1)), child2_(std::move(child2)) {} // NOT explicit LogicalExpression(std::unique_ptr child1) - : child1_(std::move(child1->logicalComplement())), child2_(nullptr) {} + : child1_(child1->logicalComplement()), child2_(nullptr) {} std::unique_ptr logicalComplement() const override; From c11920021335684af13bec16095e3fbdfc003cd0 Mon Sep 17 00:00:00 2001 From: realHannes Date: Thu, 26 Sep 2024 16:18:25 +0200 Subject: [PATCH 28/81] adjusted CompressedBlockPrefiltering --- src/index/CompressedBlockPrefiltering.cpp | 163 ++-- src/index/CompressedBlockPrefiltering.h | 56 +- test/CompressedBlockPrefilteringTest.cpp | 962 +++++++++++----------- 3 files changed, 635 insertions(+), 546 deletions(-) diff --git a/src/index/CompressedBlockPrefiltering.cpp b/src/index/CompressedBlockPrefiltering.cpp index 91c673b9a0..73c341c06b 100644 --- a/src/index/CompressedBlockPrefiltering.cpp +++ b/src/index/CompressedBlockPrefiltering.cpp @@ -11,21 +11,83 @@ namespace prefilterExpressions { // HELPER FUNCTION //______________________________________________________________________________ // Given a PermutedTriple retrieve the suitable Id w.r.t. a column (index). -static constexpr auto getIdFromColumnIndex( - const BlockMetadata::PermutedTriple& permutedTriple, size_t columnIndex) { +static auto getIdFromColumnIndex(const BlockMetadata::PermutedTriple& triple, + size_t columnIndex) { switch (columnIndex) { case 0: - return permutedTriple.col0Id_; + return triple.col0Id_; case 1: - return permutedTriple.col1Id_; + return triple.col1Id_; case 2: - return permutedTriple.col2Id_; + return triple.col2Id_; default: - // Triple! + // columnIndex out of bounds AD_FAIL(); } }; +//______________________________________________________________________________ +// Extract the Ids from the given `PermutedTriple` in a tuple up to the +// position (column index) defined by `ignoreIndex`. The ignored positions are +// filled with Ids `Id::makeUndefined()`. `Id::makeUndefined()` is guaranteed +// to be smaller than Ids of all other types. +static auto getTiedMaskedPermutedTriple( + const BlockMetadata::PermutedTriple& triple, size_t ignoreIndex = 3) { + const auto undefined = Id::makeUndefined(); + switch (ignoreIndex) { + case 3: + return std::tie(triple.col0Id_, triple.col1Id_, triple.col2Id_); + case 2: + return std::tie(triple.col0Id_, triple.col1Id_, undefined); + case 1: + return std::tie(triple.col0Id_, undefined, undefined); + case 0: + return std::tie(undefined, undefined, undefined); + default: + // ignoreIndex out of bounds + AD_FAIL(); + } +}; + +//______________________________________________________________________________ +// Check required conditions. +static auto checkEvalRequirements(const std::vector& input, + size_t evaluationColumn) { + auto throwRuntimeError = [](const std::string& errorMessage) { + throw std::runtime_error(errorMessage); + }; + + for (size_t i = 1; i < input.size(); i++) { + const auto& block1 = input[i - 1]; + const auto& block2 = input[i]; + if (block1 == block2) { + throwRuntimeError("The provided data blocks must be unique."); + } + const auto& triple1 = block1.lastTriple_; + const auto& triple2 = block2.firstTriple_; + if (getIdFromColumnIndex(triple1, evaluationColumn) > + getIdFromColumnIndex(triple2, evaluationColumn)) { + throwRuntimeError("The data blocks must be provided in sorted order."); + } + if (getTiedMaskedPermutedTriple(triple1, evaluationColumn) != + getTiedMaskedPermutedTriple(triple2, evaluationColumn)) { + throwRuntimeError( + "The columns up to the evaluation column must contain the same " + "values."); + } + } +}; + +// SECTION PREFILTER EXPRESSION (BASE CLASS) +//______________________________________________________________________________ +std::vector PrefilterExpression::evaluate( + const std::vector& input, size_t evaluationColumn) const { + checkEvalRequirements(input, evaluationColumn); + const auto& relevantBlocks = evaluateImpl(input, evaluationColumn); + checkEvalRequirements(relevantBlocks, evaluationColumn); + return relevantBlocks; +}; + // SECTION RELATIONAL OPERATIONS //______________________________________________________________________________ template @@ -58,7 +120,7 @@ RelationalExpression::logicalComplement() const { //______________________________________________________________________________ template -std::vector RelationalExpression::evaluate( +std::vector RelationalExpression::evaluateImpl( const std::vector& input, size_t evaluationColumn) const { using namespace valueIdComparators; std::vector valueIdsInput; @@ -85,21 +147,26 @@ std::vector RelationalExpression::evaluate( : getRangesForId(valueIdsInput.begin(), valueIdsInput.end(), referenceId_, Comparison, false); // The vector for relevant BlockMetadata values that contain ValueIds defined - // as relevant relevantIdRanges. + // as relevant by relevantIdRanges. std::vector relevantBlocks; // Reserve memory, input.size() is upper bound. relevantBlocks.reserve(input.size()); // Given the relevant Id ranges, retrieve the corresponding relevant // BlockMetadata values from vector input and add them to the relevantBlocks // vector. + auto endValueIdsInput = valueIdsInput.end(); for (const auto& [firstId, secondId] : relevantIdRanges) { + // Ensures that index is within bounds of index vector. + auto secondIdAdjusted = + secondId < endValueIdsInput ? secondId + 1 : secondId; relevantBlocks.insert( relevantBlocks.end(), input.begin() + std::distance(valueIdsInput.begin(), firstId) / 2, // Round up, for Ids contained within the bounding Ids of firstTriple // and lastTriple we have to include the respective metadata block // (that block is partially relevant). - input.begin() + std::distance(valueIdsInput.begin(), secondId + 1) / 2); + input.begin() + + std::distance(valueIdsInput.begin(), secondIdAdjusted) / 2); } relevantBlocks.shrink_to_fit(); return relevantBlocks; @@ -111,74 +178,73 @@ template std::unique_ptr LogicalExpression::logicalComplement() const { using enum LogicalOperators; - switch (Operation) { - case NOT: - // Logically we complement (negate) a NOT here => NOT cancels out. - // Therefore, we can simply return the child of the respective NOT - // expression after undoing its previous complementation. - return child1_->logicalComplement(); - // Source De-Morgan's laws: De Morgan's laws, Wikipedia. - // Reference: https://en.wikipedia.org/wiki/De_Morgan%27s_laws - case OR: - AD_CONTRACT_CHECK(child2_.has_value()); - // De Morgan's law: not (A or B) = (not A) and (not B) - return std::make_unique( - child1_->logicalComplement(), child2_.value()->logicalComplement()); - case AND: - AD_CONTRACT_CHECK(child2_.has_value()); - // De Morgan's law: not (A and B) = (not A) or (not B) - return std::make_unique( - child1_->logicalComplement(), child2_.value()->logicalComplement()); - default: - AD_FAIL(); + // Source De-Morgan's laws: De Morgan's laws, Wikipedia. + // Reference: https://en.wikipedia.org/wiki/De_Morgan%27s_laws + if constexpr (Operation == OR) { + // De Morgan's law: not (A or B) = (not A) and (not B) + return std::make_unique(child1_->logicalComplement(), + child2_->logicalComplement()); + } else { + static_assert(Operation == AND); + // De Morgan's law: not (A and B) = (not A) or (not B) + return std::make_unique(child1_->logicalComplement(), + child2_->logicalComplement()); } }; +//______________________________________________________________________________ +std::unique_ptr NotExpression::logicalComplement() const { + // Logically we complement (negate) a NOT here => NOT cancels out. + // Therefore, we can simply return the child of the respective NOT + // expression after undoing its previous complementation. + return child_->logicalComplement(); +}; + //______________________________________________________________________________ template -std::vector LogicalExpression::evaluateOr( +std::vector LogicalExpression::evaluateOrImpl( const std::vector& input, size_t evaluationColumn) const requires(Operation == LogicalOperators::OR) { auto relevantBlocksChild1 = child1_->evaluate(input, evaluationColumn); - auto relevantBlocksChild2 = - child2_.value()->evaluate(input, evaluationColumn); - // The result vector, reserve space for the logical upper bound (num blocks) - // |relevantBlocksChild1| + |relevantBlocksChild2| regarding the OR - // operation (union of blocks) + auto relevantBlocksChild2 = child2_->evaluate(input, evaluationColumn); + std::vector unionedRelevantBlocks; unionedRelevantBlocks.reserve(relevantBlocksChild1.size() + relevantBlocksChild2.size()); - // Lambda function that implements the projection to retrieve ValueIds from - // BlockMetadata values. - auto retrieveValueId = [evaluationColumn](const BlockMetadata& block) { - return getIdFromColumnIndex(block.lastTriple_, evaluationColumn); + + auto blockLessThanBlock = [](const BlockMetadata& block1, + const BlockMetadata& block2) { + return getTiedMaskedPermutedTriple(block1.lastTriple_) < + getTiedMaskedPermutedTriple(block2.firstTriple_); }; // 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(relevantBlocksChild1, relevantBlocksChild2, std::back_inserter(unionedRelevantBlocks), - std::ranges::less{}, retrieveValueId, retrieveValueId); + blockLessThanBlock); return unionedRelevantBlocks; }; //______________________________________________________________________________ template -std::vector LogicalExpression::evaluate( +std::vector LogicalExpression::evaluateImpl( const std::vector& input, size_t evaluationColumn) const { using enum LogicalOperators; if constexpr (Operation == AND) { auto resultChild1 = child1_->evaluate(input, evaluationColumn); - AD_CONTRACT_CHECK(child2_.has_value()); - return child2_.value()->evaluate(resultChild1, evaluationColumn); - } else if constexpr (Operation == OR) { - AD_CONTRACT_CHECK(child2_.has_value()); - return evaluateOr(input, evaluationColumn); + return child2_->evaluate(resultChild1, evaluationColumn); } else { - static_assert(Operation == NOT); - return child1_->evaluate(input, evaluationColumn); + static_assert(Operation == OR); + return evaluateOrImpl(input, evaluationColumn); } }; +//______________________________________________________________________________ +std::vector NotExpression::evaluateImpl( + const std::vector& input, size_t evaluationColumn) const { + return child_->evaluate(input, evaluationColumn); +}; + //______________________________________________________________________________ // Necessary instantiation of template specializations template class RelationalExpression; @@ -190,6 +256,5 @@ template class RelationalExpression; template class LogicalExpression; template class LogicalExpression; -template class LogicalExpression; } // namespace prefilterExpressions diff --git a/src/index/CompressedBlockPrefiltering.h b/src/index/CompressedBlockPrefiltering.h index 8cfe559091..613fd5c9c2 100644 --- a/src/index/CompressedBlockPrefiltering.h +++ b/src/index/CompressedBlockPrefiltering.h @@ -29,8 +29,8 @@ pre-filter w.r.t. blocks that contain relevant data for the actual evaluation of those expressions to make the evaluation procedure more efficient. The block-filtering is applied with the following operations: -Relational Expressions - `<=`, `>=`, `<`, `>`, `==` and `!=` -Logical Operations - `and` and `or` +Relational Expressions - `<=`, `>=`, `<`, `>`, `==` and `!=`. +Logical Operations - `and`, `or` and `not`. */ class PrefilterExpression { @@ -41,7 +41,11 @@ class PrefilterExpression { // The respective metadata to the blocks is expected to be provided in // a sorted order (w.r.t. the relevant column). - virtual std::vector evaluate( + std::vector evaluate(const std::vector& input, + size_t evaluationColumn) const; + + private: + virtual std::vector evaluateImpl( const std::vector& input, size_t evaluationColumn) const = 0; }; @@ -67,41 +71,59 @@ class RelationalExpression : public PrefilterExpression { std::unique_ptr logicalComplement() const override; - std::vector evaluate(const std::vector& input, - size_t evaluationColumn) const override; + private: + std::vector evaluateImpl( + const std::vector& input, + size_t evaluationColumn) const override; }; //______________________________________________________________________________ // Helper struct for a compact class implementation regarding the logical -// operations `AND`, `OR` and `NOT`. -enum struct LogicalOperators { AND, OR, NOT }; +// operations `AND` and `OR`. `NOT` is implemented separately given that the +// expression is unary (single child expression). +enum struct LogicalOperators { AND, OR }; //______________________________________________________________________________ template class LogicalExpression : public PrefilterExpression { private: std::unique_ptr child1_; - std::optional> child2_; + std::unique_ptr child2_; public: // AND and OR explicit LogicalExpression(std::unique_ptr child1, std::unique_ptr child2) : child1_(std::move(child1)), child2_(std::move(child2)) {} - // NOT - explicit LogicalExpression(std::unique_ptr child1) - : child1_(child1->logicalComplement()), child2_(nullptr) {} std::unique_ptr logicalComplement() const override; - std::vector evaluate(const std::vector& input, - size_t evaluationColumn) const override; - private: // Introduce a helper method for the specification LogicalOperators::OR - std::vector evaluateOr(const std::vector& input, - size_t evaluationColumn) const + std::vector evaluateOrImpl( + const std::vector& input, size_t evaluationColumn) const requires(Operation == LogicalOperators::OR); + + std::vector evaluateImpl( + const std::vector& input, + size_t evaluationColumn) const override; +}; + +//______________________________________________________________________________ +class NotExpression : public PrefilterExpression { + private: + std::unique_ptr child_; + + public: + explicit NotExpression(std::unique_ptr child) + : child_(child->logicalComplement()) {} + + std::unique_ptr logicalComplement() const; + + private: + std::vector evaluateImpl( + const std::vector& input, + size_t evaluationColumn) const override; }; //______________________________________________________________________________ @@ -125,7 +147,5 @@ using AndExpression = prefilterExpressions::LogicalExpression< prefilterExpressions::LogicalOperators::AND>; using OrExpression = prefilterExpressions::LogicalExpression< prefilterExpressions::LogicalOperators::OR>; -using NotExpression = prefilterExpressions::LogicalExpression< - prefilterExpressions::LogicalOperators::NOT>; } // namespace prefilterExpressions diff --git a/test/CompressedBlockPrefilteringTest.cpp b/test/CompressedBlockPrefilteringTest.cpp index 78f660d5c8..69eea2c2c6 100644 --- a/test/CompressedBlockPrefilteringTest.cpp +++ b/test/CompressedBlockPrefilteringTest.cpp @@ -42,24 +42,26 @@ struct MetadataBlocks { {IntId(48), IntId(6), DoubleId(111.333)}, {IntId(51), IntId(6), DoubleId(112.00)}}; std::vector numericBlocks = {nb1, nb2, nb3, nb4, nb5}; +}; +/* - //____________________________________________________________________________ - // Define Blocks containing VocabIds - // Use TestContext from SparqlExpressionTestHelpers.h - sparqlExpression::TestContext tc{}; - Id Undef = UndefId(); - // Columns indexed: COLUMN 0 | COLUMN 1 | COLUMN 2 - // undef. | LocalVocab | undef. - BlockMetadata vb1{{}, 0, {Undef, tc.bonn, Undef}, {Undef, tc.cologne, Undef}}; - BlockMetadata vb2{ - {}, 0, {Undef, tc.dortmund, Undef}, {Undef, tc.essen, Undef}}; - BlockMetadata vb3{ - {}, 0, {Undef, tc.frankfurt, Undef}, {Undef, tc.frankfurt, Undef}}; - BlockMetadata vb4{ - {}, 0, {Undef, tc.hamburg, Undef}, {Undef, tc.karlsruhe, Undef}}; - BlockMetadata vb5{ - {}, 0, {Undef, tc.karlsruhe, Undef}, {Undef, tc.karlsruhe, Undef}}; - std::vector vocabBlocks = {vb1, vb2, vb3, vb4, vb5}; +//____________________________________________________________________________ +// Define Blocks containing VocabIds +// Use TestContext from SparqlExpressionTestHelpers.h +sparqlExpression::TestContext tc{}; +Id Undef = UndefId(); +// Columns indexed: COLUMN 0 | COLUMN 1 | COLUMN 2 +// undef. | LocalVocab | undef. +BlockMetadata vb1{{}, 0, {Undef, tc.bonn, Undef}, {Undef, tc.cologne, Undef}}; +BlockMetadata vb2{ + {}, 0, {Undef, tc.dortmund, Undef}, {Undef, tc.essen, Undef}}; +BlockMetadata vb3{ + {}, 0, {Undef, tc.frankfurt, Undef}, {Undef, tc.frankfurt, Undef}}; +BlockMetadata vb4{ + {}, 0, {Undef, tc.hamburg, Undef}, {Undef, tc.karlsruhe, Undef}}; +BlockMetadata vb5{ + {}, 0, {Undef, tc.karlsruhe, Undef}, {Undef, tc.karlsruhe, Undef}}; +std::vector vocabBlocks = {vb1, vb2, vb3, vb4, vb5}; }; // Static tests, they focus on corner case values for the given block triples. @@ -69,284 +71,284 @@ struct MetadataBlocks { // Test LessThanExpression TEST(RelationalExpression, testLessThanExpressions) { - MetadataBlocks blocks{}; - std::vector expectedResult = {}; - // NUMERIC - EXPECT_EQ(expectedResult, - LessThanExpression{IntId(10)}.evaluate(blocks.numericBlocks, 0)); - EXPECT_EQ(expectedResult, - LessThanExpression{IntId(16)}.evaluate(blocks.numericBlocks, 0)); - expectedResult = {blocks.nb1}; - EXPECT_EQ(expectedResult, - LessThanExpression{IntId(40)}.evaluate(blocks.numericBlocks, 0)); - EXPECT_EQ(expectedResult, - LessThanExpression{IntId(42)}.evaluate(blocks.numericBlocks, 0)); - expectedResult = {blocks.nb1, blocks.nb2, blocks.nb3}; - EXPECT_EQ(expectedResult, - LessThanExpression{IntId(46)}.evaluate(blocks.numericBlocks, 0)); - EXPECT_EQ(blocks.numericBlocks, - LessThanExpression{IntId(100)}.evaluate(blocks.numericBlocks, 0)); - // VOCAB - TestContext tc{}; - expectedResult = {}; - // tc.alpha is an Id of type Vocab, w.r.t. a lexicographical order, all the - // citiy IDs (LocalVocab) should be greater than the ID (Vocab) of "alpha". - // Thus we expect that none of the blocks are relevant. - EXPECT_EQ(expectedResult, - LessThanExpression{tc.alpha}.evaluate(blocks.vocabBlocks, 1)); - EXPECT_EQ(expectedResult, - LessThanExpression{tc.berlin}.evaluate(blocks.vocabBlocks, 1)); - // All cities used within vocabBlocks should be smaller than city "munich" - // (given their respective LocalVocab IDs). - EXPECT_EQ(blocks.vocabBlocks, - LessThanExpression{tc.munich}.evaluate(blocks.vocabBlocks, 1)); - // All blocks from vocabBlocks should contain values less-than the (Vocab) - // ID for "zz". - EXPECT_EQ(blocks.vocabBlocks, - LessThanExpression{tc.zz}.evaluate(blocks.vocabBlocks, 1)); - expectedResult = {blocks.vb1, blocks.vb2}; - EXPECT_EQ(expectedResult, - LessThanExpression{tc.düsseldorf}.evaluate(blocks.vocabBlocks, 1)); - expectedResult = {blocks.vb1, blocks.vb2, blocks.vb3}; - EXPECT_EQ(expectedResult, LessThanExpression{tc.frankfurt_oder}.evaluate( - blocks.vocabBlocks, 1)); - expectedResult = {blocks.vb1, blocks.vb2, blocks.vb3, blocks.vb4}; - EXPECT_EQ(expectedResult, - LessThanExpression{tc.ingolstadt}.evaluate(blocks.vocabBlocks, 1)); +MetadataBlocks blocks{}; +std::vector expectedResult = {}; +// NUMERIC +EXPECT_EQ(expectedResult, + LessThanExpression{IntId(10)}.evaluate(blocks.numericBlocks, 0)); +EXPECT_EQ(expectedResult, + LessThanExpression{IntId(16)}.evaluate(blocks.numericBlocks, 0)); +expectedResult = {blocks.nb1}; +EXPECT_EQ(expectedResult, + LessThanExpression{IntId(40)}.evaluate(blocks.numericBlocks, 0)); +EXPECT_EQ(expectedResult, + LessThanExpression{IntId(42)}.evaluate(blocks.numericBlocks, 0)); +expectedResult = {blocks.nb1, blocks.nb2, blocks.nb3}; +EXPECT_EQ(expectedResult, + LessThanExpression{IntId(46)}.evaluate(blocks.numericBlocks, 0)); +EXPECT_EQ(blocks.numericBlocks, + LessThanExpression{IntId(100)}.evaluate(blocks.numericBlocks, 0)); +// VOCAB +TestContext tc{}; +expectedResult = {}; +// tc.alpha is an Id of type Vocab, w.r.t. a lexicographical order, all the +// citiy IDs (LocalVocab) should be greater than the ID (Vocab) of "alpha". +// Thus we expect that none of the blocks are relevant. +EXPECT_EQ(expectedResult, + LessThanExpression{tc.alpha}.evaluate(blocks.vocabBlocks, 1)); +EXPECT_EQ(expectedResult, + LessThanExpression{tc.berlin}.evaluate(blocks.vocabBlocks, 1)); +// All cities used within vocabBlocks should be smaller than city "munich" +// (given their respective LocalVocab IDs). +EXPECT_EQ(blocks.vocabBlocks, + LessThanExpression{tc.munich}.evaluate(blocks.vocabBlocks, 1)); +// All blocks from vocabBlocks should contain values less-than the (Vocab) +// ID for "zz". +EXPECT_EQ(blocks.vocabBlocks, + LessThanExpression{tc.zz}.evaluate(blocks.vocabBlocks, 1)); +expectedResult = {blocks.vb1, blocks.vb2}; +EXPECT_EQ(expectedResult, + LessThanExpression{tc.düsseldorf}.evaluate(blocks.vocabBlocks, 1)); +expectedResult = {blocks.vb1, blocks.vb2, blocks.vb3}; +EXPECT_EQ(expectedResult, LessThanExpression{tc.frankfurt_oder}.evaluate( + blocks.vocabBlocks, 1)); +expectedResult = {blocks.vb1, blocks.vb2, blocks.vb3, blocks.vb4}; +EXPECT_EQ(expectedResult, + LessThanExpression{tc.ingolstadt}.evaluate(blocks.vocabBlocks, 1)); } //______________________________________________________________________________ // Test LessEqualExpression TEST(RelationalExpression, testLessEqualExpressions) { - MetadataBlocks blocks{}; - std::vector expectedResult = {}; - // NUMERIC - EXPECT_EQ(expectedResult, - LessEqualExpression{IntId(10)}.evaluate(blocks.numericBlocks, 0)); - expectedResult = {blocks.nb1}; - EXPECT_EQ(expectedResult, - LessEqualExpression{IntId(16)}.evaluate(blocks.numericBlocks, 0)); - EXPECT_EQ(expectedResult, - LessEqualExpression{IntId(40)}.evaluate(blocks.numericBlocks, 0)); - expectedResult = {blocks.nb1, blocks.nb2, blocks.nb3}; - EXPECT_EQ(expectedResult, - LessEqualExpression{IntId(42)}.evaluate(blocks.numericBlocks, 0)); - expectedResult = {blocks.nb1, blocks.nb2, blocks.nb3, blocks.nb4}; - EXPECT_EQ(expectedResult, - LessEqualExpression{IntId(46)}.evaluate(blocks.numericBlocks, 0)); - EXPECT_EQ(blocks.numericBlocks, - LessEqualExpression{IntId(100)}.evaluate(blocks.numericBlocks, 0)); - // VOCAB - TestContext tc{}; - expectedResult = {}; - EXPECT_EQ(expectedResult, - LessEqualExpression{tc.alpha}.evaluate(blocks.vocabBlocks, 1)); - EXPECT_EQ(expectedResult, - LessEqualExpression{tc.berlin}.evaluate(blocks.vocabBlocks, 1)); - expectedResult = {blocks.vb1, blocks.vb2}; - EXPECT_EQ(expectedResult, - LessEqualExpression{tc.dortmund}.evaluate(blocks.vocabBlocks, 1)); - expectedResult = {blocks.vb1, blocks.vb2, blocks.vb3, blocks.vb4}; - EXPECT_EQ(expectedResult, - LessEqualExpression{tc.hannover}.evaluate(blocks.vocabBlocks, 1)); - EXPECT_EQ(blocks.vocabBlocks, - LessEqualExpression{tc.karlsruhe}.evaluate(blocks.vocabBlocks, 1)); +MetadataBlocks blocks{}; +std::vector expectedResult = {}; +// NUMERIC +EXPECT_EQ(expectedResult, + LessEqualExpression{IntId(10)}.evaluate(blocks.numericBlocks, 0)); +expectedResult = {blocks.nb1}; +EXPECT_EQ(expectedResult, + LessEqualExpression{IntId(16)}.evaluate(blocks.numericBlocks, 0)); +EXPECT_EQ(expectedResult, + LessEqualExpression{IntId(40)}.evaluate(blocks.numericBlocks, 0)); +expectedResult = {blocks.nb1, blocks.nb2, blocks.nb3}; +EXPECT_EQ(expectedResult, + LessEqualExpression{IntId(42)}.evaluate(blocks.numericBlocks, 0)); +expectedResult = {blocks.nb1, blocks.nb2, blocks.nb3, blocks.nb4}; +EXPECT_EQ(expectedResult, + LessEqualExpression{IntId(46)}.evaluate(blocks.numericBlocks, 0)); +EXPECT_EQ(blocks.numericBlocks, + LessEqualExpression{IntId(100)}.evaluate(blocks.numericBlocks, 0)); +// VOCAB +TestContext tc{}; +expectedResult = {}; +EXPECT_EQ(expectedResult, + LessEqualExpression{tc.alpha}.evaluate(blocks.vocabBlocks, 1)); +EXPECT_EQ(expectedResult, + LessEqualExpression{tc.berlin}.evaluate(blocks.vocabBlocks, 1)); +expectedResult = {blocks.vb1, blocks.vb2}; +EXPECT_EQ(expectedResult, + LessEqualExpression{tc.dortmund}.evaluate(blocks.vocabBlocks, 1)); +expectedResult = {blocks.vb1, blocks.vb2, blocks.vb3, blocks.vb4}; +EXPECT_EQ(expectedResult, + LessEqualExpression{tc.hannover}.evaluate(blocks.vocabBlocks, 1)); +EXPECT_EQ(blocks.vocabBlocks, + LessEqualExpression{tc.karlsruhe}.evaluate(blocks.vocabBlocks, 1)); } //______________________________________________________________________________ // Test GreaterThanExpression TEST(RelationalExpression, testGreaterThanExpression) { - MetadataBlocks blocks{}; - EXPECT_EQ(blocks.numericBlocks, - GreaterThanExpression{IntId(10)}.evaluate(blocks.numericBlocks, 0)); - EXPECT_EQ(blocks.numericBlocks, - GreaterThanExpression{IntId(16)}.evaluate(blocks.numericBlocks, 0)); - std::vector expectedResult = {blocks.nb2, blocks.nb3, - blocks.nb4, blocks.nb5}; - EXPECT_EQ(expectedResult, - GreaterThanExpression{IntId(38)}.evaluate(blocks.numericBlocks, 0)); - expectedResult = {blocks.nb3, blocks.nb4, blocks.nb5}; - EXPECT_EQ(expectedResult, - GreaterThanExpression{IntId(42)}.evaluate(blocks.numericBlocks, 0)); - expectedResult = {blocks.nb4, blocks.nb5}; - EXPECT_EQ(expectedResult, - GreaterThanExpression{IntId(46)}.evaluate(blocks.numericBlocks, 0)); - expectedResult = {}; - EXPECT_EQ(expectedResult, - GreaterThanExpression{IntId(52)}.evaluate(blocks.numericBlocks, 0)); - // VOCAB - TestContext tc{}; - expectedResult = {}; - EXPECT_EQ(expectedResult, - GreaterThanExpression{tc.munich}.evaluate(blocks.vocabBlocks, 1)); - EXPECT_EQ(expectedResult, GreaterThanExpression{tc.karlsruhe}.evaluate( - blocks.vocabBlocks, 1)); - EXPECT_EQ(blocks.vocabBlocks, - GreaterThanExpression{tc.alpha}.evaluate(blocks.vocabBlocks, 1)); - expectedResult = {blocks.vb4, blocks.vb5}; - EXPECT_EQ(expectedResult, - GreaterThanExpression{tc.hamburg}.evaluate(blocks.vocabBlocks, 1)); - expectedResult = {blocks.vb4, blocks.vb5}; - EXPECT_EQ(expectedResult, - GreaterThanExpression{tc.hannover}.evaluate(blocks.vocabBlocks, 1)); - expectedResult = {blocks.vb2, blocks.vb3, blocks.vb4, blocks.vb5}; - EXPECT_EQ(expectedResult, GreaterThanExpression{tc.düsseldorf}.evaluate( - blocks.vocabBlocks, 1)); +MetadataBlocks blocks{}; +EXPECT_EQ(blocks.numericBlocks, + GreaterThanExpression{IntId(10)}.evaluate(blocks.numericBlocks, 0)); +EXPECT_EQ(blocks.numericBlocks, + GreaterThanExpression{IntId(16)}.evaluate(blocks.numericBlocks, 0)); +std::vector expectedResult = {blocks.nb2, blocks.nb3, + blocks.nb4, blocks.nb5}; +EXPECT_EQ(expectedResult, + GreaterThanExpression{IntId(38)}.evaluate(blocks.numericBlocks, 0)); +expectedResult = {blocks.nb3, blocks.nb4, blocks.nb5}; +EXPECT_EQ(expectedResult, + GreaterThanExpression{IntId(42)}.evaluate(blocks.numericBlocks, 0)); +expectedResult = {blocks.nb4, blocks.nb5}; +EXPECT_EQ(expectedResult, + GreaterThanExpression{IntId(46)}.evaluate(blocks.numericBlocks, 0)); +expectedResult = {}; +EXPECT_EQ(expectedResult, + GreaterThanExpression{IntId(52)}.evaluate(blocks.numericBlocks, 0)); +// VOCAB +TestContext tc{}; +expectedResult = {}; +EXPECT_EQ(expectedResult, + GreaterThanExpression{tc.munich}.evaluate(blocks.vocabBlocks, 1)); +EXPECT_EQ(expectedResult, GreaterThanExpression{tc.karlsruhe}.evaluate( + blocks.vocabBlocks, 1)); +EXPECT_EQ(blocks.vocabBlocks, + GreaterThanExpression{tc.alpha}.evaluate(blocks.vocabBlocks, 1)); +expectedResult = {blocks.vb4, blocks.vb5}; +EXPECT_EQ(expectedResult, + GreaterThanExpression{tc.hamburg}.evaluate(blocks.vocabBlocks, 1)); +expectedResult = {blocks.vb4, blocks.vb5}; +EXPECT_EQ(expectedResult, + GreaterThanExpression{tc.hannover}.evaluate(blocks.vocabBlocks, 1)); +expectedResult = {blocks.vb2, blocks.vb3, blocks.vb4, blocks.vb5}; +EXPECT_EQ(expectedResult, GreaterThanExpression{tc.düsseldorf}.evaluate( + blocks.vocabBlocks, 1)); } //______________________________________________________________________________ // Test GreaterEqualExpression TEST(RelationalExpression, testGreaterEqualExpression) { - MetadataBlocks blocks{}; - EXPECT_EQ(blocks.numericBlocks, GreaterEqualExpression{IntId(10)}.evaluate( - blocks.numericBlocks, 0)); - EXPECT_EQ(blocks.numericBlocks, GreaterEqualExpression{IntId(38)}.evaluate( - blocks.numericBlocks, 0)); - std::vector expectedResult = {blocks.nb2, blocks.nb3, - blocks.nb4, blocks.nb5}; - EXPECT_EQ(expectedResult, GreaterEqualExpression{IntId(40)}.evaluate( - blocks.numericBlocks, 0)); - EXPECT_EQ(expectedResult, GreaterEqualExpression{IntId(42)}.evaluate( - blocks.numericBlocks, 0)); - expectedResult = {blocks.nb3, blocks.nb4, blocks.nb5}; - EXPECT_EQ(expectedResult, GreaterEqualExpression{IntId(45)}.evaluate( - blocks.numericBlocks, 0)); - expectedResult = {blocks.nb4, blocks.nb5}; - EXPECT_EQ(expectedResult, GreaterEqualExpression{IntId(47)}.evaluate( - blocks.numericBlocks, 0)); - expectedResult = {}; - EXPECT_EQ(expectedResult, GreaterEqualExpression{IntId(100)}.evaluate( - blocks.numericBlocks, 0)); - // VOCAB - TestContext tc{}; - EXPECT_EQ(blocks.vocabBlocks, - GreaterEqualExpression{tc.alpha}.evaluate(blocks.vocabBlocks, 1)); - EXPECT_EQ(blocks.vocabBlocks, - GreaterEqualExpression{tc.bonn}.evaluate(blocks.vocabBlocks, 1)); - EXPECT_EQ(blocks.vocabBlocks, - GreaterEqualExpression{tc.cologne}.evaluate(blocks.vocabBlocks, 1)); - expectedResult = {blocks.vb2, blocks.vb3, blocks.vb4, blocks.vb5}; - EXPECT_EQ(expectedResult, GreaterEqualExpression{tc.düsseldorf}.evaluate( - blocks.vocabBlocks, 1)); - expectedResult = {blocks.vb4, blocks.vb5}; - EXPECT_EQ(expectedResult, GreaterEqualExpression{tc.frankfurt_oder}.evaluate( - blocks.vocabBlocks, 1)); - EXPECT_EQ(expectedResult, GreaterEqualExpression{tc.karlsruhe}.evaluate( - blocks.vocabBlocks, 1)); - expectedResult = {}; - EXPECT_EQ(expectedResult, - GreaterEqualExpression{tc.munich}.evaluate(blocks.vocabBlocks, 1)); - EXPECT_EQ(expectedResult, - GreaterEqualExpression{tc.zz}.evaluate(blocks.vocabBlocks, 1)); +MetadataBlocks blocks{}; +EXPECT_EQ(blocks.numericBlocks, GreaterEqualExpression{IntId(10)}.evaluate( + blocks.numericBlocks, 0)); +EXPECT_EQ(blocks.numericBlocks, GreaterEqualExpression{IntId(38)}.evaluate( + blocks.numericBlocks, 0)); +std::vector expectedResult = {blocks.nb2, blocks.nb3, + blocks.nb4, blocks.nb5}; +EXPECT_EQ(expectedResult, GreaterEqualExpression{IntId(40)}.evaluate( + blocks.numericBlocks, 0)); +EXPECT_EQ(expectedResult, GreaterEqualExpression{IntId(42)}.evaluate( + blocks.numericBlocks, 0)); +expectedResult = {blocks.nb3, blocks.nb4, blocks.nb5}; +EXPECT_EQ(expectedResult, GreaterEqualExpression{IntId(45)}.evaluate( + blocks.numericBlocks, 0)); +expectedResult = {blocks.nb4, blocks.nb5}; +EXPECT_EQ(expectedResult, GreaterEqualExpression{IntId(47)}.evaluate( + blocks.numericBlocks, 0)); +expectedResult = {}; +EXPECT_EQ(expectedResult, GreaterEqualExpression{IntId(100)}.evaluate( + blocks.numericBlocks, 0)); +// VOCAB +TestContext tc{}; +EXPECT_EQ(blocks.vocabBlocks, + GreaterEqualExpression{tc.alpha}.evaluate(blocks.vocabBlocks, 1)); +EXPECT_EQ(blocks.vocabBlocks, + GreaterEqualExpression{tc.bonn}.evaluate(blocks.vocabBlocks, 1)); +EXPECT_EQ(blocks.vocabBlocks, + GreaterEqualExpression{tc.cologne}.evaluate(blocks.vocabBlocks, 1)); +expectedResult = {blocks.vb2, blocks.vb3, blocks.vb4, blocks.vb5}; +EXPECT_EQ(expectedResult, GreaterEqualExpression{tc.düsseldorf}.evaluate( + blocks.vocabBlocks, 1)); +expectedResult = {blocks.vb4, blocks.vb5}; +EXPECT_EQ(expectedResult, GreaterEqualExpression{tc.frankfurt_oder}.evaluate( + blocks.vocabBlocks, 1)); +EXPECT_EQ(expectedResult, GreaterEqualExpression{tc.karlsruhe}.evaluate( + blocks.vocabBlocks, 1)); +expectedResult = {}; +EXPECT_EQ(expectedResult, + GreaterEqualExpression{tc.munich}.evaluate(blocks.vocabBlocks, 1)); +EXPECT_EQ(expectedResult, + GreaterEqualExpression{tc.zz}.evaluate(blocks.vocabBlocks, 1)); } //______________________________________________________________________________ // Test EqualExpression TEST(RelationalExpression, testEqualExpression) { - MetadataBlocks blocks{}; - std::vector expectedResult = {}; - EXPECT_EQ(expectedResult, - EqualExpression{IntId(10)}.evaluate(blocks.numericBlocks, 1)); - EXPECT_EQ(expectedResult, - EqualExpression{IntId(10)}.evaluate(blocks.numericBlocks, 0)); - expectedResult = {blocks.nb4}; - EXPECT_EQ(expectedResult, - EqualExpression{IntId(5)}.evaluate(blocks.numericBlocks, 1)); - expectedResult = {blocks.nb2, blocks.nb3, blocks.nb4}; - EXPECT_EQ(expectedResult, - EqualExpression{IntId(2)}.evaluate(blocks.numericBlocks, 1)); - expectedResult = {blocks.nb4, blocks.nb5}; - EXPECT_EQ(expectedResult, - EqualExpression{IntId(6)}.evaluate(blocks.numericBlocks, 1)); - expectedResult = {blocks.nb2, blocks.nb3}; - EXPECT_EQ(expectedResult, - EqualExpression{IntId(42)}.evaluate(blocks.numericBlocks, 0)); - expectedResult = {blocks.nb5}; - EXPECT_EQ(expectedResult, - EqualExpression{IntId(112)}.evaluate(blocks.numericBlocks, 2)); - // VOCAB - TestContext tc{}; - expectedResult = {}; - EXPECT_EQ(expectedResult, - EqualExpression{tc.zz}.evaluate(blocks.vocabBlocks, 1)); - EXPECT_EQ(expectedResult, - EqualExpression{tc.alpha}.evaluate(blocks.vocabBlocks, 1)); - EXPECT_EQ(expectedResult, - EqualExpression{tc.munich}.evaluate(blocks.vocabBlocks, 1)); - EXPECT_EQ(expectedResult, - EqualExpression{tc.frankfurt_oder}.evaluate(blocks.vocabBlocks, 1)); - expectedResult = {blocks.vb4, blocks.vb5}; - EXPECT_EQ(expectedResult, - EqualExpression{tc.karlsruhe}.evaluate(blocks.vocabBlocks, 1)); - expectedResult = {blocks.vb3}; - EXPECT_EQ(expectedResult, - EqualExpression{tc.frankfurt}.evaluate(blocks.vocabBlocks, 1)); - expectedResult = {blocks.vb2}; - EXPECT_EQ(expectedResult, - EqualExpression{tc.düsseldorf}.evaluate(blocks.vocabBlocks, 1)); - expectedResult = {blocks.vb1}; - EXPECT_EQ(expectedResult, - EqualExpression{tc.bonn}.evaluate(blocks.vocabBlocks, 1)); - EXPECT_EQ(expectedResult, - EqualExpression{tc.cologne}.evaluate(blocks.vocabBlocks, 1)); +MetadataBlocks blocks{}; +std::vector expectedResult = {}; +EXPECT_EQ(expectedResult, + EqualExpression{IntId(10)}.evaluate(blocks.numericBlocks, 1)); +EXPECT_EQ(expectedResult, + EqualExpression{IntId(10)}.evaluate(blocks.numericBlocks, 0)); +expectedResult = {blocks.nb4}; +EXPECT_EQ(expectedResult, + EqualExpression{IntId(5)}.evaluate(blocks.numericBlocks, 1)); +expectedResult = {blocks.nb2, blocks.nb3, blocks.nb4}; +EXPECT_EQ(expectedResult, + EqualExpression{IntId(2)}.evaluate(blocks.numericBlocks, 1)); +expectedResult = {blocks.nb4, blocks.nb5}; +EXPECT_EQ(expectedResult, + EqualExpression{IntId(6)}.evaluate(blocks.numericBlocks, 1)); +expectedResult = {blocks.nb2, blocks.nb3}; +EXPECT_EQ(expectedResult, + EqualExpression{IntId(42)}.evaluate(blocks.numericBlocks, 0)); +expectedResult = {blocks.nb5}; +EXPECT_EQ(expectedResult, + EqualExpression{IntId(112)}.evaluate(blocks.numericBlocks, 2)); +// VOCAB +TestContext tc{}; +expectedResult = {}; +EXPECT_EQ(expectedResult, + EqualExpression{tc.zz}.evaluate(blocks.vocabBlocks, 1)); +EXPECT_EQ(expectedResult, + EqualExpression{tc.alpha}.evaluate(blocks.vocabBlocks, 1)); +EXPECT_EQ(expectedResult, + EqualExpression{tc.munich}.evaluate(blocks.vocabBlocks, 1)); +EXPECT_EQ(expectedResult, + EqualExpression{tc.frankfurt_oder}.evaluate(blocks.vocabBlocks, 1)); +expectedResult = {blocks.vb4, blocks.vb5}; +EXPECT_EQ(expectedResult, + EqualExpression{tc.karlsruhe}.evaluate(blocks.vocabBlocks, 1)); +expectedResult = {blocks.vb3}; +EXPECT_EQ(expectedResult, + EqualExpression{tc.frankfurt}.evaluate(blocks.vocabBlocks, 1)); +expectedResult = {blocks.vb2}; +EXPECT_EQ(expectedResult, + EqualExpression{tc.düsseldorf}.evaluate(blocks.vocabBlocks, 1)); +expectedResult = {blocks.vb1}; +EXPECT_EQ(expectedResult, + EqualExpression{tc.bonn}.evaluate(blocks.vocabBlocks, 1)); +EXPECT_EQ(expectedResult, + EqualExpression{tc.cologne}.evaluate(blocks.vocabBlocks, 1)); } //______________________________________________________________________________ // Test NotEqualExpression TEST(RelationalExpression, testNotEqualExpression) { - MetadataBlocks blocks{}; - std::vector expectedResult{}; - EXPECT_EQ(blocks.numericBlocks, - NotEqualExpression{IntId(8)}.evaluate(blocks.numericBlocks, 0)); - EXPECT_EQ(blocks.numericBlocks, - NotEqualExpression{IntId(16)}.evaluate(blocks.numericBlocks, 0)); - EXPECT_EQ(blocks.numericBlocks, - NotEqualExpression{IntId(45)}.evaluate(blocks.numericBlocks, 0)); - EXPECT_EQ(blocks.numericBlocks, - NotEqualExpression{IntId(51)}.evaluate(blocks.numericBlocks, 0)); - EXPECT_EQ(blocks.numericBlocks, - NotEqualExpression{IntId(48)}.evaluate(blocks.numericBlocks, 0)); - EXPECT_EQ(blocks.numericBlocks, NotEqualExpression{DoubleId(18.32)}.evaluate( - blocks.numericBlocks, 2)); - EXPECT_EQ(blocks.numericBlocks, NotEqualExpression{DoubleId(22.33)}.evaluate( - blocks.numericBlocks, 2)); - EXPECT_EQ(blocks.numericBlocks, - NotEqualExpression{IntId(17)}.evaluate(blocks.numericBlocks, 0)); - expectedResult = {blocks.nb1, blocks.nb3, blocks.nb4, blocks.nb5}; - EXPECT_EQ(expectedResult, - NotEqualExpression{IntId(42)}.evaluate(blocks.numericBlocks, 0)); - expectedResult = {blocks.nb1, blocks.nb2, blocks.nb4, blocks.nb5}; - EXPECT_EQ(expectedResult, - NotEqualExpression{IntId(2)}.evaluate(blocks.numericBlocks, 1)); - expectedResult = {blocks.nb1, blocks.nb2, blocks.nb3, blocks.nb4}; - EXPECT_EQ(expectedResult, NotEqualExpression{DoubleId(6.00)}.evaluate( - blocks.numericBlocks, 1)); - expectedResult = {blocks.nb2, blocks.nb3, blocks.nb4, blocks.nb5}; - EXPECT_EQ(expectedResult, - NotEqualExpression{IntId(0)}.evaluate(blocks.numericBlocks, 1)); - // VOCAB - TestContext tc{}; - expectedResult = {}; - EXPECT_EQ(blocks.vocabBlocks, - NotEqualExpression{tc.zz}.evaluate(blocks.vocabBlocks, 1)); - EXPECT_EQ(blocks.vocabBlocks, - NotEqualExpression{tc.alpha}.evaluate(blocks.vocabBlocks, 1)); - EXPECT_EQ(blocks.vocabBlocks, NotEqualExpression{tc.frankfurt_oder}.evaluate( - blocks.vocabBlocks, 1)); - EXPECT_EQ(blocks.vocabBlocks, - NotEqualExpression{tc.munich}.evaluate(blocks.vocabBlocks, 1)); - EXPECT_EQ(blocks.vocabBlocks, - NotEqualExpression{tc.bonn}.evaluate(blocks.vocabBlocks, 1)); - EXPECT_EQ(blocks.vocabBlocks, - NotEqualExpression{tc.cologne}.evaluate(blocks.vocabBlocks, 1)); - EXPECT_EQ(blocks.vocabBlocks, - NotEqualExpression{tc.düsseldorf}.evaluate(blocks.vocabBlocks, 1)); - expectedResult = {blocks.vb1, blocks.vb2, blocks.vb4, blocks.vb5}; - EXPECT_EQ(expectedResult, - NotEqualExpression{tc.frankfurt}.evaluate(blocks.vocabBlocks, 1)); - expectedResult = {blocks.vb1, blocks.vb2, blocks.vb3, blocks.vb4}; - EXPECT_EQ(expectedResult, - NotEqualExpression{tc.karlsruhe}.evaluate(blocks.vocabBlocks, 1)); +MetadataBlocks blocks{}; +std::vector expectedResult{}; +EXPECT_EQ(blocks.numericBlocks, + NotEqualExpression{IntId(8)}.evaluate(blocks.numericBlocks, 0)); +EXPECT_EQ(blocks.numericBlocks, + NotEqualExpression{IntId(16)}.evaluate(blocks.numericBlocks, 0)); +EXPECT_EQ(blocks.numericBlocks, + NotEqualExpression{IntId(45)}.evaluate(blocks.numericBlocks, 0)); +EXPECT_EQ(blocks.numericBlocks, + NotEqualExpression{IntId(51)}.evaluate(blocks.numericBlocks, 0)); +EXPECT_EQ(blocks.numericBlocks, + NotEqualExpression{IntId(48)}.evaluate(blocks.numericBlocks, 0)); +EXPECT_EQ(blocks.numericBlocks, NotEqualExpression{DoubleId(18.32)}.evaluate( + blocks.numericBlocks, 2)); +EXPECT_EQ(blocks.numericBlocks, NotEqualExpression{DoubleId(22.33)}.evaluate( + blocks.numericBlocks, 2)); +EXPECT_EQ(blocks.numericBlocks, + NotEqualExpression{IntId(17)}.evaluate(blocks.numericBlocks, 0)); +expectedResult = {blocks.nb1, blocks.nb3, blocks.nb4, blocks.nb5}; +EXPECT_EQ(expectedResult, + NotEqualExpression{IntId(42)}.evaluate(blocks.numericBlocks, 0)); +expectedResult = {blocks.nb1, blocks.nb2, blocks.nb4, blocks.nb5}; +EXPECT_EQ(expectedResult, + NotEqualExpression{IntId(2)}.evaluate(blocks.numericBlocks, 1)); +expectedResult = {blocks.nb1, blocks.nb2, blocks.nb3, blocks.nb4}; +EXPECT_EQ(expectedResult, NotEqualExpression{DoubleId(6.00)}.evaluate( + blocks.numericBlocks, 1)); +expectedResult = {blocks.nb2, blocks.nb3, blocks.nb4, blocks.nb5}; +EXPECT_EQ(expectedResult, + NotEqualExpression{IntId(0)}.evaluate(blocks.numericBlocks, 1)); +// VOCAB +TestContext tc{}; +expectedResult = {}; +EXPECT_EQ(blocks.vocabBlocks, + NotEqualExpression{tc.zz}.evaluate(blocks.vocabBlocks, 1)); +EXPECT_EQ(blocks.vocabBlocks, + NotEqualExpression{tc.alpha}.evaluate(blocks.vocabBlocks, 1)); +EXPECT_EQ(blocks.vocabBlocks, NotEqualExpression{tc.frankfurt_oder}.evaluate( + blocks.vocabBlocks, 1)); +EXPECT_EQ(blocks.vocabBlocks, + NotEqualExpression{tc.munich}.evaluate(blocks.vocabBlocks, 1)); +EXPECT_EQ(blocks.vocabBlocks, + NotEqualExpression{tc.bonn}.evaluate(blocks.vocabBlocks, 1)); +EXPECT_EQ(blocks.vocabBlocks, + NotEqualExpression{tc.cologne}.evaluate(blocks.vocabBlocks, 1)); +EXPECT_EQ(blocks.vocabBlocks, + NotEqualExpression{tc.düsseldorf}.evaluate(blocks.vocabBlocks, 1)); +expectedResult = {blocks.vb1, blocks.vb2, blocks.vb4, blocks.vb5}; +EXPECT_EQ(expectedResult, + NotEqualExpression{tc.frankfurt}.evaluate(blocks.vocabBlocks, 1)); +expectedResult = {blocks.vb1, blocks.vb2, blocks.vb3, blocks.vb4}; +EXPECT_EQ(expectedResult, + NotEqualExpression{tc.karlsruhe}.evaluate(blocks.vocabBlocks, 1)); } //______________________________________________________________________________ @@ -355,223 +357,225 @@ TEST(RelationalExpression, testNotEqualExpression) { // Test AndExpression TEST(LogicalExpression, testAndExpression) { - MetadataBlocks blocks{}; - std::vector expectedResult{}; - EXPECT_EQ(expectedResult, - AndExpression(std::make_unique(IntId(42)), - std::make_unique(IntId(45))) - .evaluate(blocks.numericBlocks, 0)); - EXPECT_EQ( - expectedResult, - AndExpression(std::make_unique(IntId(42)), - std::make_unique(DoubleId(52.33))) - .evaluate(blocks.numericBlocks, 0)); - EXPECT_EQ( - expectedResult, - AndExpression(std::make_unique( - std::make_unique(IntId(42)), - std::make_unique(IntId(45))), - std::make_unique(IntId(49))) - .evaluate(blocks.numericBlocks, 0)); - expectedResult = {blocks.nb1, blocks.nb2}; - EXPECT_EQ(expectedResult, - AndExpression(std::make_unique( - std::make_unique(IntId(0)), - std::make_unique(IntId(0))), - std::make_unique(IntId(6))) - .evaluate(blocks.numericBlocks, 1)); - // !!! Reason that we don't expect an empty result here: - // Block blocks.nb3 contains value within the range [42, 45] on column 0. - // Thus, this block is a valid candidate for values unequal to 42 and values - // that are equal to 45. - expectedResult = {blocks.nb3}; - EXPECT_EQ(expectedResult, - AndExpression(std::make_unique(IntId(42)), - std::make_unique(DoubleId(42.00))) - .evaluate(blocks.numericBlocks, 0)); - expectedResult = {blocks.nb1, blocks.nb2, blocks.nb3, blocks.nb4}; - EXPECT_EQ(expectedResult, - AndExpression(std::make_unique(DoubleId(6.00)), - std::make_unique(IntId(2.00))) - .evaluate(blocks.numericBlocks, 1)); - expectedResult = {blocks.nb4, blocks.nb5}; - EXPECT_EQ( - expectedResult, - AndExpression(std::make_unique( - std::make_unique(IntId(7)), - std::make_unique(IntId(5))), - std::make_unique(IntId(0))) - .evaluate(blocks.numericBlocks, 1)); - expectedResult = {blocks.nb1, blocks.nb2}; - EXPECT_EQ( - expectedResult, - AndExpression(std::make_unique(DoubleId(14.575)), - std::make_unique(IntId(12))) - .evaluate(blocks.numericBlocks, 2)); - expectedResult = {blocks.nb2, blocks.nb3, blocks.nb4}; - EXPECT_EQ(expectedResult, - AndExpression(std::make_unique(DoubleId(0.00)), - std::make_unique(DoubleId(6.00))) - .evaluate(blocks.numericBlocks, 1)); - expectedResult = {blocks.nb1, blocks.nb2}; - EXPECT_EQ(expectedResult, - AndExpression(std::make_unique(DoubleId(1.99)), - std::make_unique(DoubleId(1.5))) - .evaluate(blocks.numericBlocks, 1)); +MetadataBlocks blocks{}; +std::vector expectedResult{}; +EXPECT_EQ(expectedResult, + AndExpression(std::make_unique(IntId(42)), + std::make_unique(IntId(45))) + .evaluate(blocks.numericBlocks, 0)); +EXPECT_EQ( + expectedResult, + AndExpression(std::make_unique(IntId(42)), + std::make_unique(DoubleId(52.33))) + .evaluate(blocks.numericBlocks, 0)); +EXPECT_EQ( + expectedResult, + AndExpression(std::make_unique( + std::make_unique(IntId(42)), + std::make_unique(IntId(45))), + std::make_unique(IntId(49))) + .evaluate(blocks.numericBlocks, 0)); +expectedResult = {blocks.nb1, blocks.nb2}; +EXPECT_EQ(expectedResult, + AndExpression(std::make_unique( + std::make_unique(IntId(0)), + std::make_unique(IntId(0))), + std::make_unique(IntId(6))) + .evaluate(blocks.numericBlocks, 1)); +// !!! Reason that we don't expect an empty result here: +// Block blocks.nb3 contains value within the range [42, 45] on column 0. +// Thus, this block is a valid candidate for values unequal to 42 and values +// that are equal to 45. +expectedResult = {blocks.nb3}; +EXPECT_EQ(expectedResult, + AndExpression(std::make_unique(IntId(42)), + std::make_unique(DoubleId(42.00))) + .evaluate(blocks.numericBlocks, 0)); +expectedResult = {blocks.nb1, blocks.nb2, blocks.nb3, blocks.nb4}; +EXPECT_EQ(expectedResult, + AndExpression(std::make_unique(DoubleId(6.00)), + std::make_unique(IntId(2.00))) + .evaluate(blocks.numericBlocks, 1)); +expectedResult = {blocks.nb4, blocks.nb5}; +EXPECT_EQ( + expectedResult, + AndExpression(std::make_unique( + std::make_unique(IntId(7)), + std::make_unique(IntId(5))), + std::make_unique(IntId(0))) + .evaluate(blocks.numericBlocks, 1)); +expectedResult = {blocks.nb1, blocks.nb2}; +EXPECT_EQ( + expectedResult, + AndExpression(std::make_unique(DoubleId(14.575)), + std::make_unique(IntId(12))) + .evaluate(blocks.numericBlocks, 2)); +expectedResult = {blocks.nb2, blocks.nb3, blocks.nb4}; +EXPECT_EQ(expectedResult, + AndExpression(std::make_unique(DoubleId(0.00)), + std::make_unique(DoubleId(6.00))) + .evaluate(blocks.numericBlocks, 1)); +expectedResult = {blocks.nb1, blocks.nb2}; +EXPECT_EQ(expectedResult, + AndExpression(std::make_unique(DoubleId(1.99)), + std::make_unique(DoubleId(1.5))) + .evaluate(blocks.numericBlocks, 1)); } //______________________________________________________________________________ // Test OrExpression TEST(LogicalExpression, testOrExpression) { - MetadataBlocks blocks{}; - std::vector expectedResult = {blocks.nb1, blocks.nb4, - blocks.nb5}; - EXPECT_EQ(expectedResult, - OrExpression(std::make_unique(IntId(42)), - std::make_unique(IntId(45))) - .evaluate(blocks.numericBlocks, 0)); - expectedResult = {}; - EXPECT_EQ(expectedResult, - OrExpression(std::make_unique(DoubleId(-14.23)), - std::make_unique(IntId(51))) - .evaluate(blocks.numericBlocks, 0)); - expectedResult = {blocks.nb2, blocks.nb3, blocks.nb4, blocks.nb5}; - EXPECT_EQ( - expectedResult, - OrExpression(std::make_unique(IntId(0)), - std::make_unique( - std::make_unique(IntId(5)), - std::make_unique(DoubleId(1.00)))) - .evaluate(blocks.numericBlocks, 1)); - expectedResult = {blocks.nb1, blocks.nb2, blocks.nb4, blocks.nb5}; - EXPECT_EQ(expectedResult, - OrExpression(std::make_unique(IntId(0)), - std::make_unique( - std::make_unique(IntId(3)), - std::make_unique(DoubleId(6)))) - .evaluate(blocks.numericBlocks, 1)); - expectedResult = {blocks.nb4, blocks.nb5}; - EXPECT_EQ( - expectedResult, - OrExpression(std::make_unique(IntId(20)), - std::make_unique(DoubleId(113.3))) - .evaluate(blocks.numericBlocks, 2)); - expectedResult = {blocks.nb1, blocks.nb5}; - EXPECT_EQ( - expectedResult, - OrExpression(std::make_unique(IntId(42)), - std::make_unique( - std::make_unique(IntId(49)), - std::make_unique(DoubleId(2.00)))) - .evaluate(blocks.numericBlocks, 0)); - expectedResult = {blocks.nb1, blocks.nb2, blocks.nb3, blocks.nb4}; - EXPECT_EQ(expectedResult, - OrExpression(std::make_unique( - std::make_unique(IntId(0)), - std::make_unique(DoubleId(2.00))), - std::make_unique(IntId(6))) - .evaluate(blocks.numericBlocks, 1)); - expectedResult = {blocks.nb1, blocks.nb2, blocks.nb3}; - EXPECT_EQ(expectedResult, - OrExpression(std::make_unique(DoubleId(17.00)), - std::make_unique(IntId(16.35))) - .evaluate(blocks.numericBlocks, 2)); +MetadataBlocks blocks{}; +std::vector expectedResult = {blocks.nb1, blocks.nb4, + blocks.nb5}; +EXPECT_EQ(expectedResult, + OrExpression(std::make_unique(IntId(42)), + std::make_unique(IntId(45))) + .evaluate(blocks.numericBlocks, 0)); +expectedResult = {}; +EXPECT_EQ(expectedResult, + OrExpression(std::make_unique(DoubleId(-14.23)), + std::make_unique(IntId(51))) + .evaluate(blocks.numericBlocks, 0)); +expectedResult = {blocks.nb2, blocks.nb3, blocks.nb4, blocks.nb5}; +EXPECT_EQ( + expectedResult, + OrExpression(std::make_unique(IntId(0)), + std::make_unique( + std::make_unique(IntId(5)), + std::make_unique(DoubleId(1.00)))) + .evaluate(blocks.numericBlocks, 1)); +expectedResult = {blocks.nb1, blocks.nb2, blocks.nb4, blocks.nb5}; +EXPECT_EQ(expectedResult, + OrExpression(std::make_unique(IntId(0)), + std::make_unique( + std::make_unique(IntId(3)), + std::make_unique(DoubleId(6)))) + .evaluate(blocks.numericBlocks, 1)); +expectedResult = {blocks.nb4, blocks.nb5}; +EXPECT_EQ( + expectedResult, + OrExpression(std::make_unique(IntId(20)), + std::make_unique(DoubleId(113.3))) + .evaluate(blocks.numericBlocks, 2)); +expectedResult = {blocks.nb1, blocks.nb5}; +EXPECT_EQ( + expectedResult, + OrExpression(std::make_unique(IntId(42)), + std::make_unique( + std::make_unique(IntId(49)), + std::make_unique(DoubleId(2.00)))) + .evaluate(blocks.numericBlocks, 0)); +expectedResult = {blocks.nb1, blocks.nb2, blocks.nb3, blocks.nb4}; +EXPECT_EQ(expectedResult, + OrExpression(std::make_unique( + std::make_unique(IntId(0)), + std::make_unique(DoubleId(2.00))), + std::make_unique(IntId(6))) + .evaluate(blocks.numericBlocks, 1)); +expectedResult = {blocks.nb1, blocks.nb2, blocks.nb3}; +EXPECT_EQ(expectedResult, + OrExpression(std::make_unique(DoubleId(17.00)), + std::make_unique(IntId(16.35))) + .evaluate(blocks.numericBlocks, 2)); } //______________________________________________________________________________ // Test NotExpression TEST(LogicalExpression, testNotExpression) { - MetadataBlocks blocks{}; - TestContext tc{}; - std::vector expectedResult = {}; - EXPECT_EQ(expectedResult, - NotExpression(std::make_unique(IntId(16))) - .evaluate(blocks.numericBlocks, 0)); - EXPECT_EQ( - expectedResult, - NotExpression(std::make_unique( - std::make_unique(tc.munich))) - .evaluate(blocks.vocabBlocks, 1)); - EXPECT_EQ(expectedResult, - NotExpression(std::make_unique( - std::make_unique(tc.zz))) - .evaluate(blocks.vocabBlocks, 1)); - expectedResult = {blocks.vb4, blocks.vb5}; - EXPECT_EQ( - expectedResult, - NotExpression(std::make_unique( - std::make_unique(tc.karlsruhe))) - .evaluate(blocks.vocabBlocks, 1)); - EXPECT_EQ( - blocks.numericBlocks, - NotExpression(std::make_unique( - std::make_unique(IntId(16)))) - .evaluate(blocks.numericBlocks, 0)); - expectedResult = {blocks.vb1, blocks.vb2, blocks.vb3}; - EXPECT_EQ(expectedResult, - NotExpression(std::make_unique(tc.frankfurt)) - .evaluate(blocks.vocabBlocks, 1)); - EXPECT_EQ(blocks.vocabBlocks, - NotExpression(std::make_unique(tc.berlin)) - .evaluate(blocks.vocabBlocks, 1)); - expectedResult = {blocks.vb1, blocks.vb2}; - EXPECT_EQ( - expectedResult, - NotExpression(std::make_unique(tc.düsseldorf)) - .evaluate(blocks.vocabBlocks, 1)); - EXPECT_EQ(blocks.numericBlocks, - NotExpression( - std::make_unique( - std::make_unique(IntId(13)), - std::make_unique(DoubleId(111.01)))) - .evaluate(blocks.numericBlocks, 2)); - expectedResult = {}; - EXPECT_EQ( - expectedResult, - NotExpression( - std::make_unique(std::make_unique( - std::make_unique(IntId(13)), - std::make_unique(DoubleId(111.01))))) - .evaluate(blocks.numericBlocks, 2)); - expectedResult = {blocks.vb4, blocks.vb5}; - EXPECT_EQ( - expectedResult, - NotExpression(std::make_unique( - std::make_unique(tc.munich), - std::make_unique(tc.ingolstadt))) - .evaluate(blocks.vocabBlocks, 1)); - expectedResult = {blocks.vb2, blocks.vb3, blocks.vb4}; - EXPECT_EQ( - expectedResult, - NotExpression(std::make_unique( - std::make_unique(tc.hamburg), - std::make_unique(tc.düsseldorf))) - .evaluate(blocks.vocabBlocks, 1)); - expectedResult = {blocks.nb2, blocks.nb3, blocks.nb4}; - EXPECT_EQ(expectedResult, - NotExpression(std::make_unique( - std::make_unique(DoubleId(0.0)), - std::make_unique(IntId(6)))) - .evaluate(blocks.numericBlocks, 1)); - expectedResult = {blocks.nb1, blocks.nb2, blocks.nb4, blocks.nb5}; - EXPECT_EQ( - expectedResult, - NotExpression(std::make_unique( - std::make_unique(DoubleId(0.0)), - std::make_unique(IntId(6)))) - .evaluate(blocks.numericBlocks, 1)); - expectedResult = {blocks.nb3, blocks.nb4}; - EXPECT_EQ( - expectedResult, - NotExpression(std::make_unique( - std::make_unique(DoubleId(42.0)), - std::make_unique(IntId(48)))) - .evaluate(blocks.numericBlocks, 0)); - expectedResult = {blocks.nb1, blocks.nb2, blocks.nb3, blocks.nb5}; - EXPECT_EQ(expectedResult, - NotExpression( - std::make_unique(std::make_unique( - std::make_unique(DoubleId(42.25)), - std::make_unique(IntId(51))))) - .evaluate(blocks.numericBlocks, 0)); +MetadataBlocks blocks{}; +TestContext tc{}; +std::vector expectedResult = {}; +EXPECT_EQ(expectedResult, + NotExpression(std::make_unique(IntId(16))) + .evaluate(blocks.numericBlocks, 0)); +EXPECT_EQ( + expectedResult, + NotExpression(std::make_unique( + std::make_unique(tc.munich))) + .evaluate(blocks.vocabBlocks, 1)); +EXPECT_EQ(expectedResult, + NotExpression(std::make_unique( + std::make_unique(tc.zz))) + .evaluate(blocks.vocabBlocks, 1)); +expectedResult = {blocks.vb4, blocks.vb5}; +EXPECT_EQ( + expectedResult, + NotExpression(std::make_unique( + std::make_unique(tc.karlsruhe))) + .evaluate(blocks.vocabBlocks, 1)); +EXPECT_EQ( + blocks.numericBlocks, + NotExpression(std::make_unique( + std::make_unique(IntId(16)))) + .evaluate(blocks.numericBlocks, 0)); +expectedResult = {blocks.vb1, blocks.vb2, blocks.vb3}; +EXPECT_EQ(expectedResult, + NotExpression(std::make_unique(tc.frankfurt)) + .evaluate(blocks.vocabBlocks, 1)); +EXPECT_EQ(blocks.vocabBlocks, + NotExpression(std::make_unique(tc.berlin)) + .evaluate(blocks.vocabBlocks, 1)); +expectedResult = {blocks.vb1, blocks.vb2}; +EXPECT_EQ( + expectedResult, + NotExpression(std::make_unique(tc.düsseldorf)) + .evaluate(blocks.vocabBlocks, 1)); +EXPECT_EQ(blocks.numericBlocks, + NotExpression( + std::make_unique( + std::make_unique(IntId(13)), + std::make_unique(DoubleId(111.01)))) + .evaluate(blocks.numericBlocks, 2)); +expectedResult = {}; +EXPECT_EQ( + expectedResult, + NotExpression( + std::make_unique(std::make_unique( + std::make_unique(IntId(13)), + std::make_unique(DoubleId(111.01))))) + .evaluate(blocks.numericBlocks, 2)); +expectedResult = {blocks.vb4, blocks.vb5}; +EXPECT_EQ( + expectedResult, + NotExpression(std::make_unique( + std::make_unique(tc.munich), + std::make_unique(tc.ingolstadt))) + .evaluate(blocks.vocabBlocks, 1)); +expectedResult = {blocks.vb2, blocks.vb3, blocks.vb4}; +EXPECT_EQ( + expectedResult, + NotExpression(std::make_unique( + std::make_unique(tc.hamburg), + std::make_unique(tc.düsseldorf))) + .evaluate(blocks.vocabBlocks, 1)); +expectedResult = {blocks.nb2, blocks.nb3, blocks.nb4}; +EXPECT_EQ(expectedResult, + NotExpression(std::make_unique( + std::make_unique(DoubleId(0.0)), + std::make_unique(IntId(6)))) + .evaluate(blocks.numericBlocks, 1)); +expectedResult = {blocks.nb1, blocks.nb2, blocks.nb4, blocks.nb5}; +EXPECT_EQ( + expectedResult, + NotExpression(std::make_unique( + std::make_unique(DoubleId(0.0)), + std::make_unique(IntId(6)))) + .evaluate(blocks.numericBlocks, 1)); +expectedResult = {blocks.nb3, blocks.nb4}; +EXPECT_EQ( + expectedResult, + NotExpression(std::make_unique( + std::make_unique(DoubleId(42.0)), + std::make_unique(IntId(48)))) + .evaluate(blocks.numericBlocks, 0)); +expectedResult = {blocks.nb1, blocks.nb2, blocks.nb3, blocks.nb5}; +EXPECT_EQ(expectedResult, + NotExpression( + std::make_unique(std::make_unique( + std::make_unique(DoubleId(42.25)), + std::make_unique(IntId(51))))) + .evaluate(blocks.numericBlocks, 0)); } + +*/ From af171811deea7320e829926348ec0a00c46f2056 Mon Sep 17 00:00:00 2001 From: realHannes Date: Thu, 26 Sep 2024 17:16:06 +0200 Subject: [PATCH 29/81] mark method with override --- src/index/CompressedBlockPrefiltering.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index/CompressedBlockPrefiltering.h b/src/index/CompressedBlockPrefiltering.h index 613fd5c9c2..60eed61d8d 100644 --- a/src/index/CompressedBlockPrefiltering.h +++ b/src/index/CompressedBlockPrefiltering.h @@ -118,7 +118,7 @@ class NotExpression : public PrefilterExpression { explicit NotExpression(std::unique_ptr child) : child_(child->logicalComplement()) {} - std::unique_ptr logicalComplement() const; + std::unique_ptr logicalComplement() const override; private: std::vector evaluateImpl( From 5af6138e8955896106c548fda5b10a819d503125 Mon Sep 17 00:00:00 2001 From: realHannes Date: Tue, 8 Oct 2024 10:59:39 +0200 Subject: [PATCH 30/81] make further changes to prefiltering procedure and its tests --- src/global/ValueId.h | 2 +- src/index/CompressedBlockPrefiltering.cpp | 140 ++- src/index/CompressedBlockPrefiltering.h | 5 - src/index/CompressedRelation.h | 9 + test/CompressedBlockPrefilteringTest.cpp | 1083 +++++++++++---------- 5 files changed, 681 insertions(+), 558 deletions(-) diff --git a/src/global/ValueId.h b/src/global/ValueId.h index fe429b98fa..e344fe4f52 100644 --- a/src/global/ValueId.h +++ b/src/global/ValueId.h @@ -29,9 +29,9 @@ enum struct Datatype { VocabIndex, LocalVocabIndex, TextRecordIndex, + WordVocabIndex, Date, GeoPoint, - WordVocabIndex, BlankNodeIndex, MaxValue = BlankNodeIndex // Note: Unfortunately we cannot easily get the size of an enum. diff --git a/src/index/CompressedBlockPrefiltering.cpp b/src/index/CompressedBlockPrefiltering.cpp index 73c341c06b..801d713e05 100644 --- a/src/index/CompressedBlockPrefiltering.cpp +++ b/src/index/CompressedBlockPrefiltering.cpp @@ -31,9 +31,9 @@ static auto getIdFromColumnIndex(const BlockMetadata::PermutedTriple& triple, // position (column index) defined by `ignoreIndex`. The ignored positions are // filled with Ids `Id::makeUndefined()`. `Id::makeUndefined()` is guaranteed // to be smaller than Ids of all other types. -static auto getTiedMaskedPermutedTriple( - const BlockMetadata::PermutedTriple& triple, size_t ignoreIndex = 3) { - const auto undefined = Id::makeUndefined(); +static auto getTiedMaskedTriple(const BlockMetadata::PermutedTriple& triple, + size_t ignoreIndex = 3) { + static const auto undefined = Id::makeUndefined(); switch (ignoreIndex) { case 3: return std::tie(triple.col0Id_, triple.col1Id_, triple.col2Id_); @@ -63,21 +63,74 @@ static auto checkEvalRequirements(const std::vector& input, if (block1 == block2) { throwRuntimeError("The provided data blocks must be unique."); } - const auto& triple1 = block1.lastTriple_; - const auto& triple2 = block2.firstTriple_; - if (getIdFromColumnIndex(triple1, evaluationColumn) > - getIdFromColumnIndex(triple2, evaluationColumn)) { - throwRuntimeError("The data blocks must be provided in sorted order."); - } - if (getTiedMaskedPermutedTriple(triple1, evaluationColumn) != - getTiedMaskedPermutedTriple(triple2, evaluationColumn)) { + const auto& triple1First = block1.firstTriple_; + const auto& triple1Last = block1.lastTriple_; + const auto& triple2First = block2.firstTriple_; + const auto& triple2Last = block2.lastTriple_; + + const auto checkInvalid = [triple1First, triple1Last, triple2First, + triple2Last](auto comp, size_t evalCol) { + const auto& maskedT1Last = getTiedMaskedTriple(triple1Last, evalCol); + const auto& maskedT2First = getTiedMaskedTriple(triple2First, evalCol); + return comp(getTiedMaskedTriple(triple1First, evalCol), maskedT1Last) || + comp(maskedT1Last, maskedT2First) || + comp(maskedT2First, getTiedMaskedTriple(triple2Last, evalCol)); + }; + // Check for equality of IDs up to the evaluation column. + if (checkInvalid(std::not_equal_to<>{}, evaluationColumn)) { throwRuntimeError( "The columns up to the evaluation column must contain the same " "values."); } + // Check for order of IDs on evaluation Column. + if (checkInvalid(std::greater<>{}, evaluationColumn + 1)) { + throwRuntimeError( + "The data blocks must be provided in sorted order regarding the " + "evaluation column."); + } } }; +//______________________________________________________________________________ +// Given two sorted `vector`s containing `BlockMetadata`, this function +// returns their merged `BlockMetadata` content in a `vector` which is free of +// duplicates and ordered. +static auto getSetUnion(const std::vector& blocks1, + const std::vector& blocks2, + size_t evalCol) { + evalCol += 1; + std::vector mergedVectors; + mergedVectors.reserve(blocks1.size() + blocks2.size()); + auto blockLessThanBlock = [evalCol](const BlockMetadata& block1, + const BlockMetadata& block2) { + const auto& lastTripleBlock1 = + getTiedMaskedTriple(block1.lastTriple_, evalCol); + const auto& firstTripleBlock2 = + getTiedMaskedTriple(block2.firstTriple_, evalCol); + // Given two blocks with the respective ranges [-4, -4] (block1) + // and [-4, 0.0] (block2) on the evaluation column, we see that [-4, -4] < + // [-4, 0.0] should hold true. To assess if block1 is less than block2, it + // is not enough to just compare -4 (ID from last triple of block1) to -4 + // (ID from first triple of block2). With this simple comparison [-4, -4] < + // [-4, 0.0] wouldn't hold, because the comparison -4 < -4 will yield false. + // Thus for proper implementation of the < operator, we have to check here + // on the IDs of the last triple. -4 < 0.0 will yield true => [-4, -4] < + // [-4, 0.0] holds true. + if (lastTripleBlock1 == firstTripleBlock2) { + return lastTripleBlock1 < + getTiedMaskedTriple(block2.lastTriple_, evalCol); + } else { + return lastTripleBlock1 < firstTripleBlock2; + } + }; + // 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); + mergedVectors.shrink_to_fit(); + return mergedVectors; +} + // SECTION PREFILTER EXPRESSION (BASE CLASS) //______________________________________________________________________________ std::vector PrefilterExpression::evaluate( @@ -127,13 +180,31 @@ std::vector RelationalExpression::evaluateImpl( // For each BlockMetadata value in vector input, we have a respective Id for // firstTriple and lastTriple valueIdsInput.reserve(2 * input.size()); + std::vector mixedDatatypeBlocks; for (const auto& block : input) { - valueIdsInput.push_back( - getIdFromColumnIndex(block.firstTriple_, evaluationColumn)); - valueIdsInput.push_back( - getIdFromColumnIndex(block.lastTriple_, evaluationColumn)); + const auto firstId = + getIdFromColumnIndex(block.firstTriple_, evaluationColumn); + const auto secondId = + getIdFromColumnIndex(block.lastTriple_, evaluationColumn); + const auto firstIdDatatype = firstId.getDatatype(); + const auto secondIdDatatype = secondId.getDatatype(); + const auto referenceIdDatatype = referenceId_.getDatatype(); + valueIdsInput.push_back(firstId); + valueIdsInput.push_back(secondId); + + if ((firstIdDatatype != secondIdDatatype) && + // The block is only potentially relevant if at least one of the + // respective bounding Ids is of similar (comparable) type to + // refernceId_. + (valueIdComparators::detail::areTypesCompatible(referenceIdDatatype, + firstIdDatatype) || + valueIdComparators::detail::areTypesCompatible(referenceIdDatatype, + secondIdDatatype))) { + mixedDatatypeBlocks.push_back(block); + } } + // Use getRangesForId (from valueIdComparators) to extract the ranges // containing the relevant ValueIds. // For pre-filtering with CompOp::EQ, we have to consider empty ranges. @@ -146,11 +217,13 @@ std::vector RelationalExpression::evaluateImpl( referenceId_, Comparison) : getRangesForId(valueIdsInput.begin(), valueIdsInput.end(), referenceId_, Comparison, false); - // The vector for relevant BlockMetadata values that contain ValueIds defined - // as relevant by relevantIdRanges. + + // The vector for relevant BlockMetadata values which contain ValueIds + // defined as relevant by relevantIdRanges. std::vector relevantBlocks; // Reserve memory, input.size() is upper bound. relevantBlocks.reserve(input.size()); + // Given the relevant Id ranges, retrieve the corresponding relevant // BlockMetadata values from vector input and add them to the relevantBlocks // vector. @@ -169,7 +242,9 @@ std::vector RelationalExpression::evaluateImpl( std::distance(valueIdsInput.begin(), secondIdAdjusted) / 2); } relevantBlocks.shrink_to_fit(); - return relevantBlocks; + // Merge mixedDatatypeBlocks into relevantBlocks while maintaing order and + // avoiding dublicates. + return getSetUnion(relevantBlocks, mixedDatatypeBlocks, evaluationColumn); }; // SECTION LOGICAL OPERATIONS @@ -200,31 +275,6 @@ std::unique_ptr NotExpression::logicalComplement() const { return child_->logicalComplement(); }; -//______________________________________________________________________________ -template -std::vector LogicalExpression::evaluateOrImpl( - const std::vector& input, size_t evaluationColumn) const - requires(Operation == LogicalOperators::OR) { - auto relevantBlocksChild1 = child1_->evaluate(input, evaluationColumn); - auto relevantBlocksChild2 = child2_->evaluate(input, evaluationColumn); - - std::vector unionedRelevantBlocks; - unionedRelevantBlocks.reserve(relevantBlocksChild1.size() + - relevantBlocksChild2.size()); - - auto blockLessThanBlock = [](const BlockMetadata& block1, - const BlockMetadata& block2) { - return getTiedMaskedPermutedTriple(block1.lastTriple_) < - getTiedMaskedPermutedTriple(block2.firstTriple_); - }; - // 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(relevantBlocksChild1, relevantBlocksChild2, - std::back_inserter(unionedRelevantBlocks), - blockLessThanBlock); - return unionedRelevantBlocks; -}; - //______________________________________________________________________________ template std::vector LogicalExpression::evaluateImpl( @@ -235,7 +285,9 @@ std::vector LogicalExpression::evaluateImpl( return child2_->evaluate(resultChild1, evaluationColumn); } else { static_assert(Operation == OR); - return evaluateOrImpl(input, evaluationColumn); + return getSetUnion(child1_->evaluate(input, evaluationColumn), + child2_->evaluate(input, evaluationColumn), + evaluationColumn); } }; diff --git a/src/index/CompressedBlockPrefiltering.h b/src/index/CompressedBlockPrefiltering.h index 60eed61d8d..75960c05ec 100644 --- a/src/index/CompressedBlockPrefiltering.h +++ b/src/index/CompressedBlockPrefiltering.h @@ -99,11 +99,6 @@ class LogicalExpression : public PrefilterExpression { std::unique_ptr logicalComplement() const override; private: - // Introduce a helper method for the specification LogicalOperators::OR - std::vector evaluateOrImpl( - const std::vector& input, size_t evaluationColumn) const - requires(Operation == LogicalOperators::OR); - std::vector evaluateImpl( const std::vector& input, size_t evaluationColumn) const override; diff --git a/src/index/CompressedRelation.h b/src/index/CompressedRelation.h index 231301e0ae..9038646de6 100644 --- a/src/index/CompressedRelation.h +++ b/src/index/CompressedRelation.h @@ -105,6 +105,15 @@ struct CompressedBlockMetadata { // Two of these are equal if all members are equal. bool operator==(const CompressedBlockMetadata&) const = default; + + // Format BlockMetadata contents for debugging. + friend std::ostream& operator<<( + std::ostream& str, const CompressedBlockMetadata& blockMetadata) { + str << "#BlockMetadata\n(first) " << blockMetadata.firstTriple_ << "(last) " + << blockMetadata.lastTriple_ << "num. rows: " << blockMetadata.numRows_ + << "." << std::endl; + return str; + } }; // Serialization of the `OffsetAndcompressedSize` subclass. diff --git a/test/CompressedBlockPrefilteringTest.cpp b/test/CompressedBlockPrefilteringTest.cpp index 69eea2c2c6..56edbe0684 100644 --- a/test/CompressedBlockPrefilteringTest.cpp +++ b/test/CompressedBlockPrefilteringTest.cpp @@ -12,343 +12,425 @@ using ad_utility::testing::DoubleId; using ad_utility::testing::IntId; +using ad_utility::testing::LocalVocabId; using ad_utility::testing::UndefId; +using ad_utility::testing::VocabId; using sparqlExpression::TestContext; using namespace prefilterExpressions; //______________________________________________________________________________ struct MetadataBlocks { - // Define five blocks, for convenience all of their columns are sorted on - // their data when appended in the given order to a vector. - // Columns indexed: COLUMN 0 | COLUMN 1 | COLUMN 2 - BlockMetadata nb1{{}, - 0, - {IntId(16), IntId(0), DoubleId(0.0)}, // firstTriple - {IntId(38), IntId(0), DoubleId(12.5)}}; // lastTriple - BlockMetadata nb2{{}, - 0, - {IntId(42), IntId(0), DoubleId(12.5)}, - {IntId(42), IntId(2), DoubleId(14.575)}}; - BlockMetadata nb3{{}, - 0, - {IntId(42), IntId(2), DoubleId(16.33)}, - {IntId(45), IntId(2), DoubleId(18.32)}}; - BlockMetadata nb4{{}, - 0, - {IntId(46), IntId(2), DoubleId(22.29)}, - {IntId(47), IntId(6), DoubleId(111.223)}}; - BlockMetadata nb5{{}, - 0, - {IntId(48), IntId(6), DoubleId(111.333)}, - {IntId(51), IntId(6), DoubleId(112.00)}}; - std::vector numericBlocks = {nb1, nb2, nb3, nb4, nb5}; -}; -/* - -//____________________________________________________________________________ -// Define Blocks containing VocabIds -// Use TestContext from SparqlExpressionTestHelpers.h -sparqlExpression::TestContext tc{}; -Id Undef = UndefId(); -// Columns indexed: COLUMN 0 | COLUMN 1 | COLUMN 2 -// undef. | LocalVocab | undef. -BlockMetadata vb1{{}, 0, {Undef, tc.bonn, Undef}, {Undef, tc.cologne, Undef}}; -BlockMetadata vb2{ - {}, 0, {Undef, tc.dortmund, Undef}, {Undef, tc.essen, Undef}}; -BlockMetadata vb3{ - {}, 0, {Undef, tc.frankfurt, Undef}, {Undef, tc.frankfurt, Undef}}; -BlockMetadata vb4{ - {}, 0, {Undef, tc.hamburg, Undef}, {Undef, tc.karlsruhe, Undef}}; -BlockMetadata vb5{ - {}, 0, {Undef, tc.karlsruhe, Undef}, {Undef, tc.karlsruhe, Undef}}; -std::vector vocabBlocks = {vb1, vb2, vb3, vb4, vb5}; + /* + Our pre-filtering procedure expects blocks that are in correct (ascending) + order w.r.t. their contained ValueIds given the first and last triple. + + The correct order of the ValueIds is dependent on their type and underlying + representation. + + Short overview on the ascending order logic for the underlying values: + Order ValueIds for (signed) integer values - [0... max, -max... -1] + Order ValueIds for (signed) doubles values - [0.0... max, -0.0... -max] + Order ValueIds for Vocab and LocalVocab values given the vocabulary with + indices (up to N) - [VocabId(0), .... VocabId(N)] + + COLUMN 0 and COLUMN 1 contain fixed values, this is a necessary condition + that is also checked during the pre-filtering procedure. The actual evaluation + column (we filter w.r.t. values of COLUMN 2) contains mixed types. + */ + + // Values for fixed columns + const Id VocabId10 = VocabId(10); + const Id DoubleId33 = DoubleId(33); + + auto makeBlock(const ValueId& firstId, const ValueId& lastId) + -> BlockMetadata { + return {{}, + 0, + // COLUMN 0 | COLUMN 1 | COLUMN 2 + {VocabId10, DoubleId33, firstId}, // firstTriple + {VocabId10, DoubleId33, lastId}, // lastTriple + {}, + false}; + }; + + BlockMetadata b1 = makeBlock(IntId(0), IntId(0)); + BlockMetadata b2 = makeBlock(IntId(0), IntId(5)); + BlockMetadata b3 = makeBlock(IntId(5), IntId(6)); + BlockMetadata b4 = makeBlock(IntId(8), IntId(9)); + BlockMetadata b5 = makeBlock(IntId(-10), IntId(-8)); + BlockMetadata b6 = makeBlock(IntId(-4), IntId(-4)); + // b7 contains mixed datatypes in COLUMN 2 + BlockMetadata b7 = makeBlock(IntId(-4), DoubleId(2)); + BlockMetadata b8 = makeBlock(DoubleId(2), DoubleId(2)); + BlockMetadata b9 = makeBlock(DoubleId(4), DoubleId(4)); + BlockMetadata b10 = makeBlock(DoubleId(4), DoubleId(10)); + BlockMetadata b11 = makeBlock(DoubleId(-1.23), DoubleId(-6.25)); + BlockMetadata b12 = makeBlock(DoubleId(-6.25), DoubleId(-6.25)); + BlockMetadata b13 = makeBlock(DoubleId(-10.42), DoubleId(-12.00)); + // b14 contains mixed datatypes + BlockMetadata b14 = makeBlock(DoubleId(-14.01), VocabId(0)); + BlockMetadata b15 = makeBlock(VocabId(10), VocabId(14)); + BlockMetadata b16 = makeBlock(VocabId(14), VocabId(14)); + BlockMetadata b17 = makeBlock(VocabId(14), VocabId(17)); + std::vector blocks = {b1, b2, b3, b4, b5, b6, + b7, b8, b9, b10, b11, b12, + b13, b14, b15, b16, b17}; + + // The following blocks will be swapped with their respective correct block, + // to test if the method evaluate checks the pre-conditions properly. + BlockMetadata b1_1{{}, + 0, + {VocabId10, DoubleId33, IntId(0)}, + {VocabId10, DoubleId(22), IntId(0)}, + {}, + false}; + std::vector blocksInvalidCol1 = {b1_1, b2, b3, b4, b5, b6, + b7, b8, b9, b10, b11, b12, + b13, b14, b15, b16, b17}; + BlockMetadata b5_1{{}, + 0, + {VocabId(11), DoubleId33, IntId(-10)}, + {VocabId10, DoubleId33, IntId(-8)}, + {}, + false}; + std::vector blocksInvalidCol02 = { + b1, b2, b3, b4, b5_1, b6, b7, b8, b9, + b10, b11, b12, b13, b14, b15, b16, b17}; + std::vector blocksInvalidOrder1 = { + b2, b1, b3, b4, b5, b6, b7, b8, b9, + b10, b11, b12, b13, b14, b15, b16, b17}; + std::vector blocksInvalidOrder2 = { + b1, b2, b3, b4, b5, b6, b7, b8, b9, + b10, b11, b12, b14, b13, b15, b16, b17}; + std::vector blocksWithDuplicate1 = { + b1, b1, b2, b3, b4, b5, b6, b7, b8, + b9, b10, b11, b12, b13, b14, b15, b16, b17}; + std::vector blocksWithDuplicate2 = { + b1, b2, b3, b4, b5, b6, b7, b8, b9, + b10, b11, b12, b13, b14, b15, b16, b17, b17}; }; // Static tests, they focus on corner case values for the given block triples. //______________________________________________________________________________ //______________________________________________________________________________ -// Test Relational Expressions +// Test PrefilterExpressions + +template +struct check_is : std::false_type {}; + +template +struct check_is> : std::true_type {}; +template +struct check_is> : std::true_type {}; + +template +constexpr bool check_is_logical_v = check_is::value; +template +constexpr bool check_is_relational_v = check_is::value; + +//______________________________________________________________________________ +static const auto testThrowError = + [](std::unique_ptr expression, size_t evaluationColumn, + const std::vector& input, + const std::string& expectedErrorMessage) { + try { + expression->evaluate(input, evaluationColumn); + FAIL() << "Expected thrown error message."; + } catch (const std::runtime_error& error) { + EXPECT_EQ(std::string(error.what()), expectedErrorMessage); + } catch (...) { + FAIL() << "Expected an error of type std::runtime_error, but a " + "different error was thrown."; + } + }; + +//______________________________________________________________________________ +template +struct TestRelationalExpression { + void operator()(size_t evaluationColumn, ValueId referenceId, + const std::vector& input, const ResT& expected) + requires check_is_relational_v { + auto expression = std::make_unique(referenceId); + if constexpr (std::is_same_v) { + testThrowError(std::move(expression), evaluationColumn, input, expected); + } else { + static_assert(std::is_same_v>); + EXPECT_EQ(expression->evaluate(input, evaluationColumn), expected); + } + } +}; + +//______________________________________________________________________________ +template +struct TestLogicalExpression { + template + void test(size_t evaluationColumn, ValueId referenceIdChild1, + ValueId referenceIdChild2, const std::vector& input, + const ResT& expected) + requires(check_is_logical_v && check_is_relational_v && + check_is_relational_v) { + auto expression = std::make_unique( + std::make_unique(referenceIdChild1), + std::make_unique(referenceIdChild2)); + if constexpr (std::is_same_v) { + testThrowError(std::move(expression), evaluationColumn, input, expected); + } else { + static_assert(std::is_same_v>); + EXPECT_EQ(expression->evaluate(input, evaluationColumn), expected); + } + } +}; + +//______________________________________________________________________________ +template +struct TestNotExpression { + template + void test(size_t evaluationColumn, ValueId referenceIdChild, + const std::vector& input, const ResT& expected) + requires check_is_relational_v { + auto expression = std::make_unique( + std::make_unique(referenceIdChild)); + if constexpr (std::is_same_v) { + testThrowError(std::move(expression), evaluationColumn, input, expected); + } else { + static_assert(std::is_same_v>); + EXPECT_EQ(expression->evaluate(input, evaluationColumn), expected); + } + } +}; +//______________________________________________________________________________ // Test LessThanExpression TEST(RelationalExpression, testLessThanExpressions) { -MetadataBlocks blocks{}; -std::vector expectedResult = {}; -// NUMERIC -EXPECT_EQ(expectedResult, - LessThanExpression{IntId(10)}.evaluate(blocks.numericBlocks, 0)); -EXPECT_EQ(expectedResult, - LessThanExpression{IntId(16)}.evaluate(blocks.numericBlocks, 0)); -expectedResult = {blocks.nb1}; -EXPECT_EQ(expectedResult, - LessThanExpression{IntId(40)}.evaluate(blocks.numericBlocks, 0)); -EXPECT_EQ(expectedResult, - LessThanExpression{IntId(42)}.evaluate(blocks.numericBlocks, 0)); -expectedResult = {blocks.nb1, blocks.nb2, blocks.nb3}; -EXPECT_EQ(expectedResult, - LessThanExpression{IntId(46)}.evaluate(blocks.numericBlocks, 0)); -EXPECT_EQ(blocks.numericBlocks, - LessThanExpression{IntId(100)}.evaluate(blocks.numericBlocks, 0)); -// VOCAB -TestContext tc{}; -expectedResult = {}; -// tc.alpha is an Id of type Vocab, w.r.t. a lexicographical order, all the -// citiy IDs (LocalVocab) should be greater than the ID (Vocab) of "alpha". -// Thus we expect that none of the blocks are relevant. -EXPECT_EQ(expectedResult, - LessThanExpression{tc.alpha}.evaluate(blocks.vocabBlocks, 1)); -EXPECT_EQ(expectedResult, - LessThanExpression{tc.berlin}.evaluate(blocks.vocabBlocks, 1)); -// All cities used within vocabBlocks should be smaller than city "munich" -// (given their respective LocalVocab IDs). -EXPECT_EQ(blocks.vocabBlocks, - LessThanExpression{tc.munich}.evaluate(blocks.vocabBlocks, 1)); -// All blocks from vocabBlocks should contain values less-than the (Vocab) -// ID for "zz". -EXPECT_EQ(blocks.vocabBlocks, - LessThanExpression{tc.zz}.evaluate(blocks.vocabBlocks, 1)); -expectedResult = {blocks.vb1, blocks.vb2}; -EXPECT_EQ(expectedResult, - LessThanExpression{tc.düsseldorf}.evaluate(blocks.vocabBlocks, 1)); -expectedResult = {blocks.vb1, blocks.vb2, blocks.vb3}; -EXPECT_EQ(expectedResult, LessThanExpression{tc.frankfurt_oder}.evaluate( - blocks.vocabBlocks, 1)); -expectedResult = {blocks.vb1, blocks.vb2, blocks.vb3, blocks.vb4}; -EXPECT_EQ(expectedResult, - LessThanExpression{tc.ingolstadt}.evaluate(blocks.vocabBlocks, 1)); + MetadataBlocks blocks{}; + TestRelationalExpression> + testExpression; + testExpression( + 2, IntId(5), blocks.blocks, + {blocks.b1, blocks.b2, blocks.b5, blocks.b6, blocks.b7, blocks.b8, + blocks.b9, blocks.b10, blocks.b11, blocks.b12, blocks.b13, blocks.b14}); + + testExpression(2, IntId(-12), blocks.blocks, {blocks.b7, blocks.b14}); + testExpression(2, IntId(0), blocks.blocks, + {blocks.b5, blocks.b6, blocks.b7, blocks.b11, blocks.b12, + blocks.b13, blocks.b14}); + testExpression(2, IntId(100), blocks.blocks, + {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b5, + blocks.b6, blocks.b7, blocks.b8, blocks.b9, blocks.b10, + blocks.b11, blocks.b12, blocks.b13, blocks.b14}); + testExpression(2, DoubleId(-3), blocks.blocks, + {blocks.b5, blocks.b6, blocks.b7, blocks.b11, blocks.b12, + blocks.b13, blocks.b14}); + testExpression(2, DoubleId(-14.01), blocks.blocks, {blocks.b7, blocks.b14}); + testExpression(2, DoubleId(-11.22), blocks.blocks, + {blocks.b7, blocks.b13, blocks.b14}); + testExpression( + 2, DoubleId(-4.121), blocks.blocks, + {blocks.b5, blocks.b7, blocks.b11, blocks.b12, blocks.b13, blocks.b14}); + testExpression(2, VocabId(0), blocks.blocks, {blocks.b14}); + testExpression(2, VocabId(12), blocks.blocks, {blocks.b14, blocks.b15}); + testExpression(2, VocabId(14), blocks.blocks, {blocks.b14, blocks.b15}); + testExpression(2, VocabId(16), blocks.blocks, + {blocks.b14, blocks.b15, blocks.b16, blocks.b17}); } //______________________________________________________________________________ // Test LessEqualExpression TEST(RelationalExpression, testLessEqualExpressions) { -MetadataBlocks blocks{}; -std::vector expectedResult = {}; -// NUMERIC -EXPECT_EQ(expectedResult, - LessEqualExpression{IntId(10)}.evaluate(blocks.numericBlocks, 0)); -expectedResult = {blocks.nb1}; -EXPECT_EQ(expectedResult, - LessEqualExpression{IntId(16)}.evaluate(blocks.numericBlocks, 0)); -EXPECT_EQ(expectedResult, - LessEqualExpression{IntId(40)}.evaluate(blocks.numericBlocks, 0)); -expectedResult = {blocks.nb1, blocks.nb2, blocks.nb3}; -EXPECT_EQ(expectedResult, - LessEqualExpression{IntId(42)}.evaluate(blocks.numericBlocks, 0)); -expectedResult = {blocks.nb1, blocks.nb2, blocks.nb3, blocks.nb4}; -EXPECT_EQ(expectedResult, - LessEqualExpression{IntId(46)}.evaluate(blocks.numericBlocks, 0)); -EXPECT_EQ(blocks.numericBlocks, - LessEqualExpression{IntId(100)}.evaluate(blocks.numericBlocks, 0)); -// VOCAB -TestContext tc{}; -expectedResult = {}; -EXPECT_EQ(expectedResult, - LessEqualExpression{tc.alpha}.evaluate(blocks.vocabBlocks, 1)); -EXPECT_EQ(expectedResult, - LessEqualExpression{tc.berlin}.evaluate(blocks.vocabBlocks, 1)); -expectedResult = {blocks.vb1, blocks.vb2}; -EXPECT_EQ(expectedResult, - LessEqualExpression{tc.dortmund}.evaluate(blocks.vocabBlocks, 1)); -expectedResult = {blocks.vb1, blocks.vb2, blocks.vb3, blocks.vb4}; -EXPECT_EQ(expectedResult, - LessEqualExpression{tc.hannover}.evaluate(blocks.vocabBlocks, 1)); -EXPECT_EQ(blocks.vocabBlocks, - LessEqualExpression{tc.karlsruhe}.evaluate(blocks.vocabBlocks, 1)); + MetadataBlocks blocks{}; + TestRelationalExpression> + testExpression; + testExpression(2, IntId(0), blocks.blocks, + {blocks.b1, blocks.b2, blocks.b5, blocks.b6, blocks.b7, + blocks.b11, blocks.b12, blocks.b13, blocks.b14}); + testExpression( + 2, IntId(-6), blocks.blocks, + {blocks.b5, blocks.b7, blocks.b11, blocks.b12, blocks.b13, blocks.b14}); + testExpression(2, IntId(7), blocks.blocks, + {blocks.b1, blocks.b2, blocks.b3, blocks.b5, blocks.b6, + blocks.b7, blocks.b8, blocks.b9, blocks.b10, blocks.b11, + blocks.b12, blocks.b13, blocks.b14}); + testExpression(2, IntId(-9), blocks.blocks, + {blocks.b5, blocks.b7, blocks.b13, blocks.b14}); + testExpression(2, DoubleId(-9.131), blocks.blocks, + {blocks.b5, blocks.b7, blocks.b13, blocks.b14}); + testExpression(2, DoubleId(1.1415), blocks.blocks, + {blocks.b1, blocks.b2, blocks.b5, blocks.b6, blocks.b7, + blocks.b11, blocks.b12, blocks.b13, blocks.b14}); + testExpression(2, DoubleId(3.1415), blocks.blocks, + {blocks.b1, blocks.b2, blocks.b5, blocks.b6, blocks.b7, + blocks.b8, blocks.b11, blocks.b12, blocks.b13, blocks.b14}); + testExpression(2, DoubleId(-11.99999999999999), blocks.blocks, + {blocks.b7, blocks.b13, blocks.b14}); + testExpression(2, DoubleId(-14.03), blocks.blocks, {blocks.b7, blocks.b14}); + testExpression(2, VocabId(0), blocks.blocks, {blocks.b14}); + testExpression(2, VocabId(11), blocks.blocks, {blocks.b14, blocks.b15}); + testExpression(2, VocabId(14), blocks.blocks, + {blocks.b14, blocks.b15, blocks.b16, blocks.b17}); } //______________________________________________________________________________ // Test GreaterThanExpression TEST(RelationalExpression, testGreaterThanExpression) { -MetadataBlocks blocks{}; -EXPECT_EQ(blocks.numericBlocks, - GreaterThanExpression{IntId(10)}.evaluate(blocks.numericBlocks, 0)); -EXPECT_EQ(blocks.numericBlocks, - GreaterThanExpression{IntId(16)}.evaluate(blocks.numericBlocks, 0)); -std::vector expectedResult = {blocks.nb2, blocks.nb3, - blocks.nb4, blocks.nb5}; -EXPECT_EQ(expectedResult, - GreaterThanExpression{IntId(38)}.evaluate(blocks.numericBlocks, 0)); -expectedResult = {blocks.nb3, blocks.nb4, blocks.nb5}; -EXPECT_EQ(expectedResult, - GreaterThanExpression{IntId(42)}.evaluate(blocks.numericBlocks, 0)); -expectedResult = {blocks.nb4, blocks.nb5}; -EXPECT_EQ(expectedResult, - GreaterThanExpression{IntId(46)}.evaluate(blocks.numericBlocks, 0)); -expectedResult = {}; -EXPECT_EQ(expectedResult, - GreaterThanExpression{IntId(52)}.evaluate(blocks.numericBlocks, 0)); -// VOCAB -TestContext tc{}; -expectedResult = {}; -EXPECT_EQ(expectedResult, - GreaterThanExpression{tc.munich}.evaluate(blocks.vocabBlocks, 1)); -EXPECT_EQ(expectedResult, GreaterThanExpression{tc.karlsruhe}.evaluate( - blocks.vocabBlocks, 1)); -EXPECT_EQ(blocks.vocabBlocks, - GreaterThanExpression{tc.alpha}.evaluate(blocks.vocabBlocks, 1)); -expectedResult = {blocks.vb4, blocks.vb5}; -EXPECT_EQ(expectedResult, - GreaterThanExpression{tc.hamburg}.evaluate(blocks.vocabBlocks, 1)); -expectedResult = {blocks.vb4, blocks.vb5}; -EXPECT_EQ(expectedResult, - GreaterThanExpression{tc.hannover}.evaluate(blocks.vocabBlocks, 1)); -expectedResult = {blocks.vb2, blocks.vb3, blocks.vb4, blocks.vb5}; -EXPECT_EQ(expectedResult, GreaterThanExpression{tc.düsseldorf}.evaluate( - blocks.vocabBlocks, 1)); + MetadataBlocks blocks{}; + TestRelationalExpression> + testExpression; + testExpression(2, DoubleId(5.5375), blocks.blocks, + {blocks.b3, blocks.b4, blocks.b7, blocks.b10, blocks.b14}); + testExpression(2, DoubleId(9.9994), blocks.blocks, + {blocks.b7, blocks.b10, blocks.b14}); + testExpression( + 2, IntId(-5), blocks.blocks, + {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b6, blocks.b7, + blocks.b8, blocks.b9, blocks.b10, blocks.b11, blocks.b14}); + testExpression( + 2, DoubleId(-5.5375), blocks.blocks, + {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b6, blocks.b7, + blocks.b8, blocks.b9, blocks.b10, blocks.b11, blocks.b14}); + testExpression( + 2, DoubleId(-6.2499999), blocks.blocks, + {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b6, blocks.b7, + blocks.b8, blocks.b9, blocks.b10, blocks.b11, blocks.b14}); + testExpression(2, IntId(1), blocks.blocks, + {blocks.b2, blocks.b3, blocks.b4, blocks.b7, blocks.b8, + blocks.b9, blocks.b10, blocks.b14}); + testExpression(2, IntId(3), blocks.blocks, + {blocks.b2, blocks.b3, blocks.b4, blocks.b7, blocks.b9, + blocks.b10, blocks.b14}); + testExpression( + 2, IntId(4), blocks.blocks, + {blocks.b2, blocks.b3, blocks.b4, blocks.b7, blocks.b10, blocks.b14}); + testExpression(2, IntId(-4), blocks.blocks, + {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b7, + blocks.b8, blocks.b9, blocks.b10, blocks.b11, blocks.b14}); + testExpression(2, IntId(33), blocks.blocks, {blocks.b7, blocks.b14}); + testExpression(2, VocabId(22), blocks.blocks, {blocks.b14}); + testExpression(2, VocabId(14), blocks.blocks, {blocks.b14, blocks.b17}); + testExpression(2, VocabId(12), blocks.blocks, + {blocks.b14, blocks.b15, blocks.b16, blocks.b17}); } //______________________________________________________________________________ // Test GreaterEqualExpression TEST(RelationalExpression, testGreaterEqualExpression) { -MetadataBlocks blocks{}; -EXPECT_EQ(blocks.numericBlocks, GreaterEqualExpression{IntId(10)}.evaluate( - blocks.numericBlocks, 0)); -EXPECT_EQ(blocks.numericBlocks, GreaterEqualExpression{IntId(38)}.evaluate( - blocks.numericBlocks, 0)); -std::vector expectedResult = {blocks.nb2, blocks.nb3, - blocks.nb4, blocks.nb5}; -EXPECT_EQ(expectedResult, GreaterEqualExpression{IntId(40)}.evaluate( - blocks.numericBlocks, 0)); -EXPECT_EQ(expectedResult, GreaterEqualExpression{IntId(42)}.evaluate( - blocks.numericBlocks, 0)); -expectedResult = {blocks.nb3, blocks.nb4, blocks.nb5}; -EXPECT_EQ(expectedResult, GreaterEqualExpression{IntId(45)}.evaluate( - blocks.numericBlocks, 0)); -expectedResult = {blocks.nb4, blocks.nb5}; -EXPECT_EQ(expectedResult, GreaterEqualExpression{IntId(47)}.evaluate( - blocks.numericBlocks, 0)); -expectedResult = {}; -EXPECT_EQ(expectedResult, GreaterEqualExpression{IntId(100)}.evaluate( - blocks.numericBlocks, 0)); -// VOCAB -TestContext tc{}; -EXPECT_EQ(blocks.vocabBlocks, - GreaterEqualExpression{tc.alpha}.evaluate(blocks.vocabBlocks, 1)); -EXPECT_EQ(blocks.vocabBlocks, - GreaterEqualExpression{tc.bonn}.evaluate(blocks.vocabBlocks, 1)); -EXPECT_EQ(blocks.vocabBlocks, - GreaterEqualExpression{tc.cologne}.evaluate(blocks.vocabBlocks, 1)); -expectedResult = {blocks.vb2, blocks.vb3, blocks.vb4, blocks.vb5}; -EXPECT_EQ(expectedResult, GreaterEqualExpression{tc.düsseldorf}.evaluate( - blocks.vocabBlocks, 1)); -expectedResult = {blocks.vb4, blocks.vb5}; -EXPECT_EQ(expectedResult, GreaterEqualExpression{tc.frankfurt_oder}.evaluate( - blocks.vocabBlocks, 1)); -EXPECT_EQ(expectedResult, GreaterEqualExpression{tc.karlsruhe}.evaluate( - blocks.vocabBlocks, 1)); -expectedResult = {}; -EXPECT_EQ(expectedResult, - GreaterEqualExpression{tc.munich}.evaluate(blocks.vocabBlocks, 1)); -EXPECT_EQ(expectedResult, - GreaterEqualExpression{tc.zz}.evaluate(blocks.vocabBlocks, 1)); + MetadataBlocks blocks{}; + TestRelationalExpression> + testExpression; + testExpression(2, IntId(0), blocks.blocks, + {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b7, + blocks.b8, blocks.b9, blocks.b10, blocks.b14}); + testExpression(2, IntId(8), blocks.blocks, + {blocks.b4, blocks.b7, blocks.b10, blocks.b14}); + testExpression(2, DoubleId(9.98), blocks.blocks, + {blocks.b7, blocks.b10, blocks.b14}); + testExpression(2, IntId(-3), blocks.blocks, + {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b7, + blocks.b8, blocks.b9, blocks.b10, blocks.b11, blocks.b14}); + testExpression(2, IntId(-10), blocks.blocks, + {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b5, + blocks.b6, blocks.b7, blocks.b8, blocks.b9, blocks.b10, + blocks.b11, blocks.b12, blocks.b14}); + testExpression(2, DoubleId(-3.1415), blocks.blocks, + {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b7, + blocks.b8, blocks.b9, blocks.b10, blocks.b11, blocks.b14}); + testExpression( + 2, DoubleId(-4.000001), blocks.blocks, + {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b6, blocks.b7, + blocks.b8, blocks.b9, blocks.b10, blocks.b11, blocks.b14}); + testExpression(2, DoubleId(10.000), blocks.blocks, + {blocks.b7, blocks.b10, blocks.b14}); + testExpression(2, DoubleId(-15.22), blocks.blocks, + {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b5, + blocks.b6, blocks.b7, blocks.b8, blocks.b9, blocks.b10, + blocks.b11, blocks.b12, blocks.b13, blocks.b14}); + testExpression(2, DoubleId(7.999999), blocks.blocks, + {blocks.b4, blocks.b7, blocks.b10, blocks.b14}); + testExpression(2, DoubleId(10.0001), blocks.blocks, {blocks.b7, blocks.b14}); + testExpression(2, VocabId(14), blocks.blocks, + {blocks.b14, blocks.b15, blocks.b16, blocks.b17}); + testExpression(2, VocabId(10), blocks.blocks, + {blocks.b14, blocks.b15, blocks.b16, blocks.b17}); + testExpression(2, VocabId(17), blocks.blocks, {blocks.b14, blocks.b17}); } //______________________________________________________________________________ // Test EqualExpression TEST(RelationalExpression, testEqualExpression) { -MetadataBlocks blocks{}; -std::vector expectedResult = {}; -EXPECT_EQ(expectedResult, - EqualExpression{IntId(10)}.evaluate(blocks.numericBlocks, 1)); -EXPECT_EQ(expectedResult, - EqualExpression{IntId(10)}.evaluate(blocks.numericBlocks, 0)); -expectedResult = {blocks.nb4}; -EXPECT_EQ(expectedResult, - EqualExpression{IntId(5)}.evaluate(blocks.numericBlocks, 1)); -expectedResult = {blocks.nb2, blocks.nb3, blocks.nb4}; -EXPECT_EQ(expectedResult, - EqualExpression{IntId(2)}.evaluate(blocks.numericBlocks, 1)); -expectedResult = {blocks.nb4, blocks.nb5}; -EXPECT_EQ(expectedResult, - EqualExpression{IntId(6)}.evaluate(blocks.numericBlocks, 1)); -expectedResult = {blocks.nb2, blocks.nb3}; -EXPECT_EQ(expectedResult, - EqualExpression{IntId(42)}.evaluate(blocks.numericBlocks, 0)); -expectedResult = {blocks.nb5}; -EXPECT_EQ(expectedResult, - EqualExpression{IntId(112)}.evaluate(blocks.numericBlocks, 2)); -// VOCAB -TestContext tc{}; -expectedResult = {}; -EXPECT_EQ(expectedResult, - EqualExpression{tc.zz}.evaluate(blocks.vocabBlocks, 1)); -EXPECT_EQ(expectedResult, - EqualExpression{tc.alpha}.evaluate(blocks.vocabBlocks, 1)); -EXPECT_EQ(expectedResult, - EqualExpression{tc.munich}.evaluate(blocks.vocabBlocks, 1)); -EXPECT_EQ(expectedResult, - EqualExpression{tc.frankfurt_oder}.evaluate(blocks.vocabBlocks, 1)); -expectedResult = {blocks.vb4, blocks.vb5}; -EXPECT_EQ(expectedResult, - EqualExpression{tc.karlsruhe}.evaluate(blocks.vocabBlocks, 1)); -expectedResult = {blocks.vb3}; -EXPECT_EQ(expectedResult, - EqualExpression{tc.frankfurt}.evaluate(blocks.vocabBlocks, 1)); -expectedResult = {blocks.vb2}; -EXPECT_EQ(expectedResult, - EqualExpression{tc.düsseldorf}.evaluate(blocks.vocabBlocks, 1)); -expectedResult = {blocks.vb1}; -EXPECT_EQ(expectedResult, - EqualExpression{tc.bonn}.evaluate(blocks.vocabBlocks, 1)); -EXPECT_EQ(expectedResult, - EqualExpression{tc.cologne}.evaluate(blocks.vocabBlocks, 1)); + MetadataBlocks blocks{}; + TestRelationalExpression> + testExpression; + testExpression(2, IntId(0), blocks.blocks, + {blocks.b1, blocks.b2, blocks.b7, blocks.b14}); + testExpression(2, IntId(5), blocks.blocks, + {blocks.b2, blocks.b3, blocks.b7, blocks.b10, blocks.b14}); + testExpression(2, IntId(22), blocks.blocks, {blocks.b7, blocks.b14}); + testExpression(2, IntId(-10), blocks.blocks, + {blocks.b5, blocks.b7, blocks.b14}); + testExpression(2, DoubleId(-6.25), blocks.blocks, + {blocks.b7, blocks.b11, blocks.b12, blocks.b14}); + testExpression(2, IntId(-11), blocks.blocks, + {blocks.b7, blocks.b13, blocks.b14}); + testExpression(2, DoubleId(-14.02), blocks.blocks, {blocks.b7, blocks.b14}); + testExpression(2, DoubleId(-0.001), blocks.blocks, {blocks.b7, blocks.b14}); + testExpression(2, DoubleId(0), blocks.blocks, + {blocks.b1, blocks.b2, blocks.b7, blocks.b14}); + testExpression(2, IntId(2), blocks.blocks, + {blocks.b2, blocks.b7, blocks.b8, blocks.b14}); + testExpression(2, DoubleId(5.5), blocks.blocks, + {blocks.b3, blocks.b7, blocks.b10, blocks.b14}); + testExpression(2, DoubleId(1.5), blocks.blocks, + {blocks.b2, blocks.b7, blocks.b14}); + testExpression(2, VocabId(1), blocks.blocks, {blocks.b14}); + testExpression(2, VocabId(14), blocks.blocks, + {blocks.b14, blocks.b15, blocks.b16, blocks.b17}); + testExpression(2, VocabId(11), blocks.blocks, {blocks.b14, blocks.b15}); + testExpression(2, VocabId(17), blocks.blocks, {blocks.b14, blocks.b17}); + testExpression(2, IntId(-4), blocks.blocks, + {blocks.b6, blocks.b7, blocks.b11, blocks.b14}); } //______________________________________________________________________________ // Test NotEqualExpression TEST(RelationalExpression, testNotEqualExpression) { -MetadataBlocks blocks{}; -std::vector expectedResult{}; -EXPECT_EQ(blocks.numericBlocks, - NotEqualExpression{IntId(8)}.evaluate(blocks.numericBlocks, 0)); -EXPECT_EQ(blocks.numericBlocks, - NotEqualExpression{IntId(16)}.evaluate(blocks.numericBlocks, 0)); -EXPECT_EQ(blocks.numericBlocks, - NotEqualExpression{IntId(45)}.evaluate(blocks.numericBlocks, 0)); -EXPECT_EQ(blocks.numericBlocks, - NotEqualExpression{IntId(51)}.evaluate(blocks.numericBlocks, 0)); -EXPECT_EQ(blocks.numericBlocks, - NotEqualExpression{IntId(48)}.evaluate(blocks.numericBlocks, 0)); -EXPECT_EQ(blocks.numericBlocks, NotEqualExpression{DoubleId(18.32)}.evaluate( - blocks.numericBlocks, 2)); -EXPECT_EQ(blocks.numericBlocks, NotEqualExpression{DoubleId(22.33)}.evaluate( - blocks.numericBlocks, 2)); -EXPECT_EQ(blocks.numericBlocks, - NotEqualExpression{IntId(17)}.evaluate(blocks.numericBlocks, 0)); -expectedResult = {blocks.nb1, blocks.nb3, blocks.nb4, blocks.nb5}; -EXPECT_EQ(expectedResult, - NotEqualExpression{IntId(42)}.evaluate(blocks.numericBlocks, 0)); -expectedResult = {blocks.nb1, blocks.nb2, blocks.nb4, blocks.nb5}; -EXPECT_EQ(expectedResult, - NotEqualExpression{IntId(2)}.evaluate(blocks.numericBlocks, 1)); -expectedResult = {blocks.nb1, blocks.nb2, blocks.nb3, blocks.nb4}; -EXPECT_EQ(expectedResult, NotEqualExpression{DoubleId(6.00)}.evaluate( - blocks.numericBlocks, 1)); -expectedResult = {blocks.nb2, blocks.nb3, blocks.nb4, blocks.nb5}; -EXPECT_EQ(expectedResult, - NotEqualExpression{IntId(0)}.evaluate(blocks.numericBlocks, 1)); -// VOCAB -TestContext tc{}; -expectedResult = {}; -EXPECT_EQ(blocks.vocabBlocks, - NotEqualExpression{tc.zz}.evaluate(blocks.vocabBlocks, 1)); -EXPECT_EQ(blocks.vocabBlocks, - NotEqualExpression{tc.alpha}.evaluate(blocks.vocabBlocks, 1)); -EXPECT_EQ(blocks.vocabBlocks, NotEqualExpression{tc.frankfurt_oder}.evaluate( - blocks.vocabBlocks, 1)); -EXPECT_EQ(blocks.vocabBlocks, - NotEqualExpression{tc.munich}.evaluate(blocks.vocabBlocks, 1)); -EXPECT_EQ(blocks.vocabBlocks, - NotEqualExpression{tc.bonn}.evaluate(blocks.vocabBlocks, 1)); -EXPECT_EQ(blocks.vocabBlocks, - NotEqualExpression{tc.cologne}.evaluate(blocks.vocabBlocks, 1)); -EXPECT_EQ(blocks.vocabBlocks, - NotEqualExpression{tc.düsseldorf}.evaluate(blocks.vocabBlocks, 1)); -expectedResult = {blocks.vb1, blocks.vb2, blocks.vb4, blocks.vb5}; -EXPECT_EQ(expectedResult, - NotEqualExpression{tc.frankfurt}.evaluate(blocks.vocabBlocks, 1)); -expectedResult = {blocks.vb1, blocks.vb2, blocks.vb3, blocks.vb4}; -EXPECT_EQ(expectedResult, - NotEqualExpression{tc.karlsruhe}.evaluate(blocks.vocabBlocks, 1)); + MetadataBlocks blocks{}; + TestRelationalExpression> + testExpression; + testExpression(2, DoubleId(0.00), blocks.blocks, + {blocks.b2, blocks.b3, blocks.b4, blocks.b5, blocks.b6, + blocks.b7, blocks.b8, blocks.b9, blocks.b10, blocks.b11, + blocks.b12, blocks.b13, blocks.b14}); + testExpression(2, IntId(-4), blocks.blocks, + {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b5, + blocks.b7, blocks.b8, blocks.b9, blocks.b10, blocks.b11, + blocks.b12, blocks.b13, blocks.b14}); + testExpression(2, DoubleId(0.001), blocks.blocks, + {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b5, + blocks.b6, blocks.b7, blocks.b8, blocks.b9, blocks.b10, + blocks.b11, blocks.b12, blocks.b13, blocks.b14}); + testExpression(2, IntId(2), blocks.blocks, + {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b5, + blocks.b6, blocks.b7, blocks.b9, blocks.b10, blocks.b11, + blocks.b12, blocks.b13, blocks.b14}); + testExpression(2, DoubleId(-6.2500), blocks.blocks, + {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b5, + blocks.b6, blocks.b7, blocks.b8, blocks.b9, blocks.b10, + blocks.b11, blocks.b13, blocks.b14}); + testExpression(2, IntId(5), blocks.blocks, + {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b5, + blocks.b6, blocks.b7, blocks.b8, blocks.b9, blocks.b10, + blocks.b11, blocks.b12, blocks.b13, blocks.b14}); + testExpression(2, DoubleId(-101.23), blocks.blocks, + {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b5, + blocks.b6, blocks.b7, blocks.b8, blocks.b9, blocks.b10, + blocks.b11, blocks.b12, blocks.b13, blocks.b14}); + testExpression(2, VocabId(0), blocks.blocks, + {blocks.b14, blocks.b15, blocks.b16, blocks.b17}); + testExpression(2, VocabId(7), blocks.blocks, + {blocks.b14, blocks.b15, blocks.b16, blocks.b17}); + testExpression(2, VocabId(14), blocks.blocks, + {blocks.b14, blocks.b15, blocks.b17}); + testExpression(2, VocabId(17), blocks.blocks, + {blocks.b14, blocks.b15, blocks.b16, blocks.b17}); } //______________________________________________________________________________ @@ -357,225 +439,210 @@ EXPECT_EQ(expectedResult, // Test AndExpression TEST(LogicalExpression, testAndExpression) { -MetadataBlocks blocks{}; -std::vector expectedResult{}; -EXPECT_EQ(expectedResult, - AndExpression(std::make_unique(IntId(42)), - std::make_unique(IntId(45))) - .evaluate(blocks.numericBlocks, 0)); -EXPECT_EQ( - expectedResult, - AndExpression(std::make_unique(IntId(42)), - std::make_unique(DoubleId(52.33))) - .evaluate(blocks.numericBlocks, 0)); -EXPECT_EQ( - expectedResult, - AndExpression(std::make_unique( - std::make_unique(IntId(42)), - std::make_unique(IntId(45))), - std::make_unique(IntId(49))) - .evaluate(blocks.numericBlocks, 0)); -expectedResult = {blocks.nb1, blocks.nb2}; -EXPECT_EQ(expectedResult, - AndExpression(std::make_unique( - std::make_unique(IntId(0)), - std::make_unique(IntId(0))), - std::make_unique(IntId(6))) - .evaluate(blocks.numericBlocks, 1)); -// !!! Reason that we don't expect an empty result here: -// Block blocks.nb3 contains value within the range [42, 45] on column 0. -// Thus, this block is a valid candidate for values unequal to 42 and values -// that are equal to 45. -expectedResult = {blocks.nb3}; -EXPECT_EQ(expectedResult, - AndExpression(std::make_unique(IntId(42)), - std::make_unique(DoubleId(42.00))) - .evaluate(blocks.numericBlocks, 0)); -expectedResult = {blocks.nb1, blocks.nb2, blocks.nb3, blocks.nb4}; -EXPECT_EQ(expectedResult, - AndExpression(std::make_unique(DoubleId(6.00)), - std::make_unique(IntId(2.00))) - .evaluate(blocks.numericBlocks, 1)); -expectedResult = {blocks.nb4, blocks.nb5}; -EXPECT_EQ( - expectedResult, - AndExpression(std::make_unique( - std::make_unique(IntId(7)), - std::make_unique(IntId(5))), - std::make_unique(IntId(0))) - .evaluate(blocks.numericBlocks, 1)); -expectedResult = {blocks.nb1, blocks.nb2}; -EXPECT_EQ( - expectedResult, - AndExpression(std::make_unique(DoubleId(14.575)), - std::make_unique(IntId(12))) - .evaluate(blocks.numericBlocks, 2)); -expectedResult = {blocks.nb2, blocks.nb3, blocks.nb4}; -EXPECT_EQ(expectedResult, - AndExpression(std::make_unique(DoubleId(0.00)), - std::make_unique(DoubleId(6.00))) - .evaluate(blocks.numericBlocks, 1)); -expectedResult = {blocks.nb1, blocks.nb2}; -EXPECT_EQ(expectedResult, - AndExpression(std::make_unique(DoubleId(1.99)), - std::make_unique(DoubleId(1.5))) - .evaluate(blocks.numericBlocks, 1)); + MetadataBlocks blocks{}; + TestLogicalExpression> + testExpression; + testExpression.test( + 2, VocabId(10), VocabId(10), blocks.blocks, + {blocks.b14, blocks.b15, blocks.b16, blocks.b17}); + testExpression.test( + 2, VocabId(0), VocabId(17), blocks.blocks, {blocks.b14, blocks.b17}); + testExpression.test( + 2, VocabId(12), VocabId(17), blocks.blocks, {blocks.b14}); + testExpression.test( + 2, VocabId(10), VocabId(14), blocks.blocks, {blocks.b14, blocks.b15}); + testExpression.test( + 2, VocabId(0), VocabId(10), blocks.blocks, {blocks.b14}); + testExpression.test( + 2, VocabId(17), VocabId(17), blocks.blocks, + {blocks.b14, blocks.b15, blocks.b16, blocks.b17}); + testExpression.test( + 2, DoubleId(-6.25), IntId(-7), blocks.blocks, {blocks.b7, blocks.b14}); + testExpression.test( + 2, DoubleId(-6.25), DoubleId(-6.25), blocks.blocks, + {blocks.b7, blocks.b14}); + // Corner case: Logically it is impossible to satisfy (x > 0) and (x < 0) at + // the same time. But given that we evaluate on block boundaries and their + // possible values (Ids) in between, block b7 satisfies both conditions over + // its range [IntId(-4)... DoubleId(2)] for column 2. + testExpression.test( + 2, IntId(0), IntId(0), blocks.blocks, {blocks.b7, blocks.b14}); + testExpression.test( + 2, IntId(-10), DoubleId(0.00), blocks.blocks, + {blocks.b5, blocks.b6, blocks.b7, blocks.b11, blocks.b12, blocks.b14}); + // Also a corner case. + testExpression.test( + 2, IntId(0), DoubleId(0), blocks.blocks, + {blocks.b2, blocks.b7, blocks.b14}); + testExpression.test( + 2, IntId(0), IntId(0), blocks.blocks, + {blocks.b1, blocks.b2, blocks.b7, blocks.b14}); + testExpression.test( + 2, DoubleId(-34.23), DoubleId(15.1), blocks.blocks, + {blocks.b7, blocks.b14}); + testExpression.test( + 2, IntId(0), DoubleId(-4), blocks.blocks, + {blocks.b5, blocks.b6, blocks.b7, blocks.b11, blocks.b12, blocks.b13, + blocks.b14}); + testExpression.test( + 2, IntId(0), IntId(-4), blocks.blocks, + {blocks.b2, blocks.b3, blocks.b4, blocks.b5, blocks.b7, blocks.b8, + blocks.b9, blocks.b10, blocks.b11, blocks.b12, blocks.b13, blocks.b14}); + testExpression.test( + 2, DoubleId(-3.1415), DoubleId(4.5), blocks.blocks, + {blocks.b2, blocks.b7, blocks.b10, blocks.b14}); + testExpression.test( + 2, DoubleId(-6.25), IntId(0), blocks.blocks, + {blocks.b5, blocks.b6, blocks.b7, blocks.b11, blocks.b13, blocks.b14}); + testExpression.test( + 2, DoubleId(-4), DoubleId(1), blocks.blocks, {blocks.b7, blocks.b14}); + testExpression.test( + 2, DoubleId(-2), IntId(-3), blocks.blocks, + {blocks.b7, blocks.b11, blocks.b14}); } //______________________________________________________________________________ // Test OrExpression TEST(LogicalExpression, testOrExpression) { -MetadataBlocks blocks{}; -std::vector expectedResult = {blocks.nb1, blocks.nb4, - blocks.nb5}; -EXPECT_EQ(expectedResult, - OrExpression(std::make_unique(IntId(42)), - std::make_unique(IntId(45))) - .evaluate(blocks.numericBlocks, 0)); -expectedResult = {}; -EXPECT_EQ(expectedResult, - OrExpression(std::make_unique(DoubleId(-14.23)), - std::make_unique(IntId(51))) - .evaluate(blocks.numericBlocks, 0)); -expectedResult = {blocks.nb2, blocks.nb3, blocks.nb4, blocks.nb5}; -EXPECT_EQ( - expectedResult, - OrExpression(std::make_unique(IntId(0)), - std::make_unique( - std::make_unique(IntId(5)), - std::make_unique(DoubleId(1.00)))) - .evaluate(blocks.numericBlocks, 1)); -expectedResult = {blocks.nb1, blocks.nb2, blocks.nb4, blocks.nb5}; -EXPECT_EQ(expectedResult, - OrExpression(std::make_unique(IntId(0)), - std::make_unique( - std::make_unique(IntId(3)), - std::make_unique(DoubleId(6)))) - .evaluate(blocks.numericBlocks, 1)); -expectedResult = {blocks.nb4, blocks.nb5}; -EXPECT_EQ( - expectedResult, - OrExpression(std::make_unique(IntId(20)), - std::make_unique(DoubleId(113.3))) - .evaluate(blocks.numericBlocks, 2)); -expectedResult = {blocks.nb1, blocks.nb5}; -EXPECT_EQ( - expectedResult, - OrExpression(std::make_unique(IntId(42)), - std::make_unique( - std::make_unique(IntId(49)), - std::make_unique(DoubleId(2.00)))) - .evaluate(blocks.numericBlocks, 0)); -expectedResult = {blocks.nb1, blocks.nb2, blocks.nb3, blocks.nb4}; -EXPECT_EQ(expectedResult, - OrExpression(std::make_unique( - std::make_unique(IntId(0)), - std::make_unique(DoubleId(2.00))), - std::make_unique(IntId(6))) - .evaluate(blocks.numericBlocks, 1)); -expectedResult = {blocks.nb1, blocks.nb2, blocks.nb3}; -EXPECT_EQ(expectedResult, - OrExpression(std::make_unique(DoubleId(17.00)), - std::make_unique(IntId(16.35))) - .evaluate(blocks.numericBlocks, 2)); + MetadataBlocks blocks{}; + TestLogicalExpression> + testExpression; + testExpression.test( + 2, VocabId(22), VocabId(0), blocks.blocks, + {blocks.b14, blocks.b15, blocks.b16, blocks.b17}); + testExpression.test( + 2, VocabId(0), VocabId(16), blocks.blocks, {blocks.b14, blocks.b17}); + testExpression.test( + 2, VocabId(17), VocabId(242), blocks.blocks, {blocks.b14}); + testExpression.test( + 2, DoubleId(-5.95), VocabId(14), blocks.blocks, + {blocks.b5, blocks.b7, blocks.b11, blocks.b12, blocks.b13, blocks.b14, + blocks.b15, blocks.b16, blocks.b17}); + testExpression.test( + 2, DoubleId(0), VocabId(14), blocks.blocks, + {blocks.b1, blocks.b2, blocks.b7, blocks.b14, blocks.b15, blocks.b17}); + testExpression.test( + 2, DoubleId(0.0), DoubleId(-6.25), blocks.blocks, + {blocks.b1, blocks.b2, blocks.b7, blocks.b11, blocks.b12, blocks.b14}); + testExpression.test( + 2, DoubleId(-11.99), DoubleId(-15.22), blocks.blocks, + {blocks.b7, blocks.b13, blocks.b14}); + testExpression.test( + 2, DoubleId(7.99), DoubleId(-7.99), blocks.blocks, + {blocks.b4, blocks.b5, blocks.b7, blocks.b10, blocks.b13, blocks.b14}); + testExpression.test( + 2, IntId(-15), IntId(2), blocks.blocks, + {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b5, blocks.b6, + blocks.b7, blocks.b8, blocks.b9, blocks.b10, blocks.b11, blocks.b12, + blocks.b13, blocks.b14}); + testExpression.test( + 2, IntId(0), IntId(-4), blocks.blocks, + {blocks.b1, blocks.b2, blocks.b6, blocks.b7, blocks.b11, blocks.b14}); + testExpression.test( + 2, VocabId(14), IntId(2), blocks.blocks, + {blocks.b2, blocks.b7, blocks.b8, blocks.b14, blocks.b15, blocks.b17}); + testExpression.test( + 2, DoubleId(-1), IntId(1), blocks.blocks, + {blocks.b2, blocks.b3, blocks.b4, blocks.b5, blocks.b6, blocks.b7, + blocks.b8, blocks.b9, blocks.b10, blocks.b11, blocks.b12, blocks.b13, + blocks.b14}); + testExpression.test( + 2, DoubleId(-4), IntId(-4), blocks.blocks, + {blocks.b5, blocks.b6, blocks.b7, blocks.b11, blocks.b12, blocks.b13, + blocks.b14}); } //______________________________________________________________________________ // Test NotExpression TEST(LogicalExpression, testNotExpression) { -MetadataBlocks blocks{}; -TestContext tc{}; -std::vector expectedResult = {}; -EXPECT_EQ(expectedResult, - NotExpression(std::make_unique(IntId(16))) - .evaluate(blocks.numericBlocks, 0)); -EXPECT_EQ( - expectedResult, - NotExpression(std::make_unique( - std::make_unique(tc.munich))) - .evaluate(blocks.vocabBlocks, 1)); -EXPECT_EQ(expectedResult, - NotExpression(std::make_unique( - std::make_unique(tc.zz))) - .evaluate(blocks.vocabBlocks, 1)); -expectedResult = {blocks.vb4, blocks.vb5}; -EXPECT_EQ( - expectedResult, - NotExpression(std::make_unique( - std::make_unique(tc.karlsruhe))) - .evaluate(blocks.vocabBlocks, 1)); -EXPECT_EQ( - blocks.numericBlocks, - NotExpression(std::make_unique( - std::make_unique(IntId(16)))) - .evaluate(blocks.numericBlocks, 0)); -expectedResult = {blocks.vb1, blocks.vb2, blocks.vb3}; -EXPECT_EQ(expectedResult, - NotExpression(std::make_unique(tc.frankfurt)) - .evaluate(blocks.vocabBlocks, 1)); -EXPECT_EQ(blocks.vocabBlocks, - NotExpression(std::make_unique(tc.berlin)) - .evaluate(blocks.vocabBlocks, 1)); -expectedResult = {blocks.vb1, blocks.vb2}; -EXPECT_EQ( - expectedResult, - NotExpression(std::make_unique(tc.düsseldorf)) - .evaluate(blocks.vocabBlocks, 1)); -EXPECT_EQ(blocks.numericBlocks, - NotExpression( - std::make_unique( - std::make_unique(IntId(13)), - std::make_unique(DoubleId(111.01)))) - .evaluate(blocks.numericBlocks, 2)); -expectedResult = {}; -EXPECT_EQ( - expectedResult, - NotExpression( - std::make_unique(std::make_unique( - std::make_unique(IntId(13)), - std::make_unique(DoubleId(111.01))))) - .evaluate(blocks.numericBlocks, 2)); -expectedResult = {blocks.vb4, blocks.vb5}; -EXPECT_EQ( - expectedResult, - NotExpression(std::make_unique( - std::make_unique(tc.munich), - std::make_unique(tc.ingolstadt))) - .evaluate(blocks.vocabBlocks, 1)); -expectedResult = {blocks.vb2, blocks.vb3, blocks.vb4}; -EXPECT_EQ( - expectedResult, - NotExpression(std::make_unique( - std::make_unique(tc.hamburg), - std::make_unique(tc.düsseldorf))) - .evaluate(blocks.vocabBlocks, 1)); -expectedResult = {blocks.nb2, blocks.nb3, blocks.nb4}; -EXPECT_EQ(expectedResult, - NotExpression(std::make_unique( - std::make_unique(DoubleId(0.0)), - std::make_unique(IntId(6)))) - .evaluate(blocks.numericBlocks, 1)); -expectedResult = {blocks.nb1, blocks.nb2, blocks.nb4, blocks.nb5}; -EXPECT_EQ( - expectedResult, - NotExpression(std::make_unique( - std::make_unique(DoubleId(0.0)), - std::make_unique(IntId(6)))) - .evaluate(blocks.numericBlocks, 1)); -expectedResult = {blocks.nb3, blocks.nb4}; -EXPECT_EQ( - expectedResult, - NotExpression(std::make_unique( - std::make_unique(DoubleId(42.0)), - std::make_unique(IntId(48)))) - .evaluate(blocks.numericBlocks, 0)); -expectedResult = {blocks.nb1, blocks.nb2, blocks.nb3, blocks.nb5}; -EXPECT_EQ(expectedResult, - NotExpression( - std::make_unique(std::make_unique( - std::make_unique(DoubleId(42.25)), - std::make_unique(IntId(51))))) - .evaluate(blocks.numericBlocks, 0)); + MetadataBlocks blocks{}; + TestNotExpression> testExpression{}; + testExpression.test( + 2, VocabId(2), blocks.blocks, + {blocks.b14, blocks.b15, blocks.b16, blocks.b17}); + testExpression.test(2, VocabId(14), blocks.blocks, + {blocks.b14, blocks.b15, blocks.b17}); + testExpression.test( + 2, VocabId(14), blocks.blocks, + {blocks.b14, blocks.b15, blocks.b16, blocks.b17}); + testExpression.test( + 2, VocabId(0), blocks.blocks, + {blocks.b14, blocks.b15, blocks.b16, blocks.b17}); + testExpression.test( + 2, DoubleId(-14.01), blocks.blocks, + {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b5, blocks.b6, + blocks.b7, blocks.b8, blocks.b9, blocks.b10, blocks.b11, blocks.b12, + blocks.b13, blocks.b14}); + testExpression.test( + 2, DoubleId(-14.01), blocks.blocks, {blocks.b7, blocks.b14}); + testExpression.test( + 2, DoubleId(-4.00), blocks.blocks, + {blocks.b5, blocks.b6, blocks.b7, blocks.b11, blocks.b12, blocks.b13, + blocks.b14}); + testExpression.test(2, DoubleId(-24.4), blocks.blocks, + {blocks.b7, blocks.b14}); + testExpression.test( + 2, IntId(0), blocks.blocks, + {blocks.b2, blocks.b3, blocks.b4, blocks.b7, blocks.b8, blocks.b9, + blocks.b10, blocks.b14}); + testExpression.test( + 2, DoubleId(-6.25), blocks.blocks, + {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b5, blocks.b6, + blocks.b7, blocks.b8, blocks.b9, blocks.b10, blocks.b11, blocks.b13, + blocks.b14}); + testExpression.test( + 2, DoubleId(4), blocks.blocks, + {blocks.b2, blocks.b7, blocks.b9, blocks.b10, blocks.b14}); + testExpression.test( + 2, DoubleId(0), blocks.blocks, + {blocks.b1, blocks.b2, blocks.b5, blocks.b6, blocks.b7, blocks.b11, + blocks.b12, blocks.b13, blocks.b14}); } -*/ +//______________________________________________________________________________ +// Test that correct errors are thrown for invalid input (condition checking). +TEST(PrefilterExpression, testInputConditionCheck) { + MetadataBlocks blocks{}; + TestRelationalExpression + testRelationalExpression{}; + testRelationalExpression( + 2, DoubleId(10), blocks.blocksInvalidCol1, + "The columns up to the evaluation column must contain the same values."); + testRelationalExpression( + 1, DoubleId(10), blocks.blocksInvalidCol1, + "The data blocks must be provided in sorted order regarding the " + "evaluation column."); + testRelationalExpression(2, DoubleId(10), blocks.blocksWithDuplicate2, + "The provided data blocks must be unique."); + + TestNotExpression testNotExpression{}; + testNotExpression.test( + 2, VocabId(2), blocks.blocksWithDuplicate1, + "The provided data blocks must be unique."); + testNotExpression.test( + 2, DoubleId(-14.1), blocks.blocksInvalidOrder1, + "The data blocks must be provided in sorted order regarding the " + "evaluation column."); + testNotExpression.test( + 0, IntId(0), blocks.blocksInvalidCol02, + "The data blocks must be provided in sorted order regarding the " + "evaluation column."); + testNotExpression.test( + 1, IntId(0), blocks.blocksInvalidCol02, + "The columns up to the evaluation column must contain the same values."); + testNotExpression.test( + 2, IntId(0), blocks.blocksInvalidCol02, + "The columns up to the evaluation column must contain the same values."); + + TestLogicalExpression testAndExpression{}; + testAndExpression.test( + 2, DoubleId(-4.24), IntId(5), blocks.blocksWithDuplicate2, + "The provided data blocks must be unique."); + testAndExpression.test( + 2, DoubleId(-4.24), IntId(5), blocks.blocksInvalidOrder1, + "The data blocks must be provided in sorted order regarding the " + "evaluation column."); + testAndExpression.test( + 2, DoubleId(-4.24), IntId(5), blocks.blocksInvalidCol02, + "The columns up to the evaluation column must contain the same values."); +} From b02dc57ffb3d3fd8f6438d6b4e72f2262f1de2f3 Mon Sep 17 00:00:00 2001 From: realHannes Date: Tue, 8 Oct 2024 17:36:09 +0200 Subject: [PATCH 31/81] add additional testing on IDs from other datatypes --- src/index/CompressedBlockPrefiltering.cpp | 4 +- test/CompressedBlockPrefilteringTest.cpp | 177 +++++++++++++++++----- test/util/IdTestHelpers.h | 4 + 3 files changed, 146 insertions(+), 39 deletions(-) diff --git a/src/index/CompressedBlockPrefiltering.cpp b/src/index/CompressedBlockPrefiltering.cpp index 801d713e05..69e3696807 100644 --- a/src/index/CompressedBlockPrefiltering.cpp +++ b/src/index/CompressedBlockPrefiltering.cpp @@ -242,8 +242,8 @@ std::vector RelationalExpression::evaluateImpl( std::distance(valueIdsInput.begin(), secondIdAdjusted) / 2); } relevantBlocks.shrink_to_fit(); - // Merge mixedDatatypeBlocks into relevantBlocks while maintaing order and - // avoiding dublicates. + // Merge mixedDatatypeBlocks into relevantBlocks while maintaining order and + // avoiding duplicates. return getSetUnion(relevantBlocks, mixedDatatypeBlocks, evaluationColumn); }; diff --git a/test/CompressedBlockPrefilteringTest.cpp b/test/CompressedBlockPrefilteringTest.cpp index 56edbe0684..13e0c7ce9b 100644 --- a/test/CompressedBlockPrefilteringTest.cpp +++ b/test/CompressedBlockPrefilteringTest.cpp @@ -8,11 +8,14 @@ #include "./SparqlExpressionTestHelpers.h" #include "index/CompressedBlockPrefiltering.h" +#include "util/DateYearDuration.h" #include "util/IdTestHelpers.h" +using ad_utility::testing::BlankNodeId; +using ad_utility::testing::BoolId; +using ad_utility::testing::DateId; using ad_utility::testing::DoubleId; using ad_utility::testing::IntId; -using ad_utility::testing::LocalVocabId; using ad_utility::testing::UndefId; using ad_utility::testing::VocabId; using sparqlExpression::TestContext; @@ -53,49 +56,49 @@ struct MetadataBlocks { false}; }; - BlockMetadata b1 = makeBlock(IntId(0), IntId(0)); - BlockMetadata b2 = makeBlock(IntId(0), IntId(5)); - BlockMetadata b3 = makeBlock(IntId(5), IntId(6)); - BlockMetadata b4 = makeBlock(IntId(8), IntId(9)); - BlockMetadata b5 = makeBlock(IntId(-10), IntId(-8)); - BlockMetadata b6 = makeBlock(IntId(-4), IntId(-4)); + const BlockMetadata b1 = makeBlock(IntId(0), IntId(0)); + const BlockMetadata b2 = makeBlock(IntId(0), IntId(5)); + const BlockMetadata b3 = makeBlock(IntId(5), IntId(6)); + const BlockMetadata b4 = makeBlock(IntId(8), IntId(9)); + const BlockMetadata b5 = makeBlock(IntId(-10), IntId(-8)); + const BlockMetadata b6 = makeBlock(IntId(-4), IntId(-4)); // b7 contains mixed datatypes in COLUMN 2 - BlockMetadata b7 = makeBlock(IntId(-4), DoubleId(2)); - BlockMetadata b8 = makeBlock(DoubleId(2), DoubleId(2)); - BlockMetadata b9 = makeBlock(DoubleId(4), DoubleId(4)); - BlockMetadata b10 = makeBlock(DoubleId(4), DoubleId(10)); - BlockMetadata b11 = makeBlock(DoubleId(-1.23), DoubleId(-6.25)); - BlockMetadata b12 = makeBlock(DoubleId(-6.25), DoubleId(-6.25)); - BlockMetadata b13 = makeBlock(DoubleId(-10.42), DoubleId(-12.00)); + const BlockMetadata b7 = makeBlock(IntId(-4), DoubleId(2)); + const BlockMetadata b8 = makeBlock(DoubleId(2), DoubleId(2)); + const BlockMetadata b9 = makeBlock(DoubleId(4), DoubleId(4)); + const BlockMetadata b10 = makeBlock(DoubleId(4), DoubleId(10)); + const BlockMetadata b11 = makeBlock(DoubleId(-1.23), DoubleId(-6.25)); + const BlockMetadata b12 = makeBlock(DoubleId(-6.25), DoubleId(-6.25)); + const BlockMetadata b13 = makeBlock(DoubleId(-10.42), DoubleId(-12.00)); // b14 contains mixed datatypes - BlockMetadata b14 = makeBlock(DoubleId(-14.01), VocabId(0)); - BlockMetadata b15 = makeBlock(VocabId(10), VocabId(14)); - BlockMetadata b16 = makeBlock(VocabId(14), VocabId(14)); - BlockMetadata b17 = makeBlock(VocabId(14), VocabId(17)); + const BlockMetadata b14 = makeBlock(DoubleId(-14.01), VocabId(0)); + const BlockMetadata b15 = makeBlock(VocabId(10), VocabId(14)); + const BlockMetadata b16 = makeBlock(VocabId(14), VocabId(14)); + const BlockMetadata b17 = makeBlock(VocabId(14), VocabId(17)); std::vector blocks = {b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17}; // The following blocks will be swapped with their respective correct block, // to test if the method evaluate checks the pre-conditions properly. - BlockMetadata b1_1{{}, - 0, - {VocabId10, DoubleId33, IntId(0)}, - {VocabId10, DoubleId(22), IntId(0)}, - {}, - false}; + const BlockMetadata b1_1{{}, + 0, + {VocabId10, DoubleId33, IntId(0)}, + {VocabId10, DoubleId(22), IntId(0)}, + {}, + false}; std::vector blocksInvalidCol1 = {b1_1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17}; - BlockMetadata b5_1{{}, - 0, - {VocabId(11), DoubleId33, IntId(-10)}, - {VocabId10, DoubleId33, IntId(-8)}, - {}, - false}; - std::vector blocksInvalidCol02 = { - b1, b2, b3, b4, b5_1, b6, b7, b8, b9, - b10, b11, b12, b13, b14, b15, b16, b17}; + const BlockMetadata b5_1{{}, + 0, + {VocabId(11), DoubleId33, IntId(-10)}, + {VocabId10, DoubleId33, IntId(-8)}, + {}, + false}; + std::vector blocksInvalidCol2 = {b1, b2, b3, b4, b5_1, b6, + b7, b8, b9, b10, b11, b12, + b13, b14, b15, b16, b17}; std::vector blocksInvalidOrder1 = { b2, b1, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17}; @@ -108,6 +111,30 @@ struct MetadataBlocks { std::vector blocksWithDuplicate2 = { b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b17}; + + //____________________________________________________________________________ + // Additional blocks for different datatype mix. + const Id undef = Id::makeUndefined(); + const Id falseId = BoolId(false); + const Id trueId = BoolId(true); + const Id referenceDate1 = + DateId(DateYearOrDuration::parseXsdDate, "1999-11-11"); + const Id referenceDate2 = + DateId(DateYearOrDuration::parseXsdDate, "2005-02-27"); + const Id referenceDateEqual = + DateId(DateYearOrDuration::parseXsdDate, "2000-01-01"); + const BlockMetadata bd1 = makeBlock(undef, undef); + const BlockMetadata bd2 = makeBlock(undef, falseId); + const BlockMetadata bd3 = makeBlock(falseId, falseId); + const BlockMetadata bd4 = makeBlock(trueId, trueId); + const BlockMetadata bd5 = + makeBlock(trueId, DateId(DateYearOrDuration::parseXsdDate, "1999-12-12")); + const BlockMetadata bd6 = + makeBlock(DateId(DateYearOrDuration::parseXsdDate, "2000-01-01"), + DateId(DateYearOrDuration::parseXsdDate, "2000-01-01")); + const BlockMetadata bd7 = makeBlock( + DateId(DateYearOrDuration::parseXsdDate, "2024-10-08"), BlankNodeId(10)); + std::vector otherBlocks = {bd1, bd2, bd3, bd4, bd5, bd6, bd7}; }; // Static tests, they focus on corner case values for the given block triples. @@ -199,6 +226,23 @@ struct TestNotExpression { } }; +//______________________________________________________________________________ +TEST(BlockMetadata, testBlockFormatForDebugging) { + MetadataBlocks blocks{}; + EXPECT_EQ( + "#BlockMetadata\n(first) Triple: V:10 D:33.000000 I:0\n(last) Triple: " + "V:10 D:33.000000 I:0\nnum. rows: 0.\n", + (std::stringstream() << blocks.b1).str()); + EXPECT_EQ( + "#BlockMetadata\n(first) Triple: V:10 D:33.000000 I:-4\n(last) Triple: " + "V:10 D:33.000000 D:2.000000\nnum. rows: 0.\n", + (std::stringstream() << blocks.b7).str()); + EXPECT_EQ( + "#BlockMetadata\n(first) Triple: V:10 D:33.000000 V:14\n(last) Triple: " + "V:10 D:33.000000 V:17\nnum. rows: 0.\n", + (std::stringstream() << blocks.b17).str()); +} + //______________________________________________________________________________ // Test LessThanExpression TEST(RelationalExpression, testLessThanExpressions) { @@ -232,6 +276,18 @@ TEST(RelationalExpression, testLessThanExpressions) { testExpression(2, VocabId(14), blocks.blocks, {blocks.b14, blocks.b15}); testExpression(2, VocabId(16), blocks.blocks, {blocks.b14, blocks.b15, blocks.b16, blocks.b17}); + testExpression(2, blocks.undef, blocks.otherBlocks, {}); + testExpression(2, blocks.falseId, blocks.otherBlocks, + {blocks.bd2, blocks.bd5}); + testExpression(2, blocks.trueId, blocks.otherBlocks, + {blocks.bd2, blocks.bd3, blocks.bd5}); + testExpression(2, blocks.referenceDate1, blocks.otherBlocks, + {blocks.bd5, blocks.bd7}); + testExpression(2, blocks.referenceDateEqual, blocks.otherBlocks, + {blocks.bd5, blocks.bd7}); + testExpression(2, blocks.referenceDate2, blocks.otherBlocks, + {blocks.bd5, blocks.bd6, blocks.bd7}); + testExpression(2, BlankNodeId(11), blocks.otherBlocks, {blocks.bd7}); } //______________________________________________________________________________ @@ -267,6 +323,14 @@ TEST(RelationalExpression, testLessEqualExpressions) { testExpression(2, VocabId(11), blocks.blocks, {blocks.b14, blocks.b15}); testExpression(2, VocabId(14), blocks.blocks, {blocks.b14, blocks.b15, blocks.b16, blocks.b17}); + testExpression(2, blocks.undef, blocks.otherBlocks, {}); + testExpression(2, blocks.falseId, blocks.otherBlocks, + {blocks.bd2, blocks.bd3, blocks.bd5}); + testExpression(2, blocks.trueId, blocks.otherBlocks, + {blocks.bd2, blocks.bd3, blocks.bd4, blocks.bd5}); + testExpression(2, blocks.referenceDateEqual, blocks.otherBlocks, + {blocks.bd5, blocks.bd6, blocks.bd7}); + testExpression(2, BlankNodeId(11), blocks.otherBlocks, {blocks.bd7}); } //______________________________________________________________________________ @@ -308,6 +372,16 @@ TEST(RelationalExpression, testGreaterThanExpression) { testExpression(2, VocabId(14), blocks.blocks, {blocks.b14, blocks.b17}); testExpression(2, VocabId(12), blocks.blocks, {blocks.b14, blocks.b15, blocks.b16, blocks.b17}); + testExpression(2, blocks.undef, blocks.otherBlocks, {}); + testExpression(2, blocks.falseId, blocks.otherBlocks, + {blocks.bd2, blocks.bd4, blocks.bd5}); + testExpression(2, blocks.trueId, blocks.otherBlocks, + {blocks.bd2, blocks.bd5}); + testExpression(2, blocks.referenceDateEqual, blocks.otherBlocks, + {blocks.bd5, blocks.bd7}); + testExpression(2, blocks.referenceDate1, blocks.otherBlocks, + {blocks.bd5, blocks.bd6, blocks.bd7}); + testExpression(2, IntId(5), blocks.otherBlocks, {}); } //______________________________________________________________________________ @@ -351,6 +425,14 @@ TEST(RelationalExpression, testGreaterEqualExpression) { testExpression(2, VocabId(10), blocks.blocks, {blocks.b14, blocks.b15, blocks.b16, blocks.b17}); testExpression(2, VocabId(17), blocks.blocks, {blocks.b14, blocks.b17}); + testExpression(2, blocks.undef, blocks.otherBlocks, {}); + testExpression(2, blocks.falseId, blocks.otherBlocks, + {blocks.bd2, blocks.bd3, blocks.bd4, blocks.bd5}); + testExpression(2, blocks.trueId, blocks.otherBlocks, + {blocks.bd2, blocks.bd4, blocks.bd5}); + testExpression(2, blocks.referenceDateEqual, blocks.otherBlocks, + {blocks.bd5, blocks.bd6, blocks.bd7}); + testExpression(2, VocabId(0), blocks.otherBlocks, {}); } //______________________________________________________________________________ @@ -387,6 +469,14 @@ TEST(RelationalExpression, testEqualExpression) { testExpression(2, VocabId(17), blocks.blocks, {blocks.b14, blocks.b17}); testExpression(2, IntId(-4), blocks.blocks, {blocks.b6, blocks.b7, blocks.b11, blocks.b14}); + testExpression(2, blocks.trueId, blocks.otherBlocks, + {blocks.bd2, blocks.bd4, blocks.bd5}); + testExpression(2, blocks.referenceDate1, blocks.otherBlocks, + {blocks.bd5, blocks.bd7}); + testExpression(2, blocks.referenceDateEqual, blocks.otherBlocks, + {blocks.bd5, blocks.bd6, blocks.bd7}); + testExpression(2, blocks.referenceDate2, blocks.otherBlocks, + {blocks.bd5, blocks.bd7}); } //______________________________________________________________________________ @@ -431,6 +521,13 @@ TEST(RelationalExpression, testNotEqualExpression) { {blocks.b14, blocks.b15, blocks.b17}); testExpression(2, VocabId(17), blocks.blocks, {blocks.b14, blocks.b15, blocks.b16, blocks.b17}); + testExpression(2, blocks.undef, blocks.otherBlocks, {}); + testExpression(2, blocks.falseId, blocks.otherBlocks, + {blocks.bd2, blocks.bd4, blocks.bd5}); + testExpression(2, blocks.referenceDateEqual, blocks.otherBlocks, + {blocks.bd5, blocks.bd7}); + testExpression(2, blocks.referenceDate1, blocks.otherBlocks, + {blocks.bd5, blocks.bd6, blocks.bd7}); } //______________________________________________________________________________ @@ -597,6 +694,12 @@ TEST(LogicalExpression, testNotExpression) { 2, DoubleId(0), blocks.blocks, {blocks.b1, blocks.b2, blocks.b5, blocks.b6, blocks.b7, blocks.b11, blocks.b12, blocks.b13, blocks.b14}); + testExpression.test(0, blocks.VocabId10, blocks.blocks, {}); + testExpression.test(1, blocks.DoubleId33, blocks.blocks, {}); + testExpression.test(0, blocks.VocabId10, blocks.blocks, + blocks.blocks); + testExpression.test(1, blocks.DoubleId33, + blocks.blocks, {}); } //______________________________________________________________________________ @@ -624,14 +727,14 @@ TEST(PrefilterExpression, testInputConditionCheck) { "The data blocks must be provided in sorted order regarding the " "evaluation column."); testNotExpression.test( - 0, IntId(0), blocks.blocksInvalidCol02, + 0, IntId(0), blocks.blocksInvalidCol2, "The data blocks must be provided in sorted order regarding the " "evaluation column."); testNotExpression.test( - 1, IntId(0), blocks.blocksInvalidCol02, + 1, IntId(0), blocks.blocksInvalidCol2, "The columns up to the evaluation column must contain the same values."); testNotExpression.test( - 2, IntId(0), blocks.blocksInvalidCol02, + 2, IntId(0), blocks.blocksInvalidCol2, "The columns up to the evaluation column must contain the same values."); TestLogicalExpression testAndExpression{}; @@ -643,6 +746,6 @@ TEST(PrefilterExpression, testInputConditionCheck) { "The data blocks must be provided in sorted order regarding the " "evaluation column."); testAndExpression.test( - 2, DoubleId(-4.24), IntId(5), blocks.blocksInvalidCol02, + 2, DoubleId(-4.24), IntId(5), blocks.blocksInvalidCol2, "The columns up to the evaluation column must contain the same values."); } diff --git a/test/util/IdTestHelpers.h b/test/util/IdTestHelpers.h index a8818ba296..dd5dfbd715 100644 --- a/test/util/IdTestHelpers.h +++ b/test/util/IdTestHelpers.h @@ -25,6 +25,10 @@ inline auto VocabId = [](const auto& v) { return Id::makeFromVocabIndex(VocabIndex::make(v)); }; +inline auto BlankNodeId = [](const auto& v) { + return Id::makeFromBlankNodeIndex(BlankNodeIndex::make(v)); +}; + inline auto LocalVocabId = [](std::integral auto v) { static ad_utility::Synchronized localVocab; using namespace ad_utility::triple_component; From eb86f1b8c1b057680572495990bfe8e75996c56c Mon Sep 17 00:00:00 2001 From: realHannes Date: Tue, 8 Oct 2024 19:53:06 +0200 Subject: [PATCH 32/81] addition to test Not Expression with And and Or --- test/CompressedBlockPrefilteringTest.cpp | 144 ++++++++++++++--------- 1 file changed, 91 insertions(+), 53 deletions(-) diff --git a/test/CompressedBlockPrefilteringTest.cpp b/test/CompressedBlockPrefilteringTest.cpp index 13e0c7ce9b..ade741f92b 100644 --- a/test/CompressedBlockPrefilteringTest.cpp +++ b/test/CompressedBlockPrefilteringTest.cpp @@ -172,12 +172,40 @@ static const auto testThrowError = }; //______________________________________________________________________________ -template +template +constexpr std::unique_ptr makeRelExpr( + const ValueId& referenceId) { + return std::make_unique(referenceId); +} + +//______________________________________________________________________________ +template +constexpr std::unique_ptr makeLogExpr( + const ValueId& referenceId1, const ValueId& referenceId2) { + return std::make_unique(makeRelExpr(referenceId1), + makeRelExpr(referenceId2)); +} + +//______________________________________________________________________________ +template +constexpr std::unique_ptr makeNotExpression( + const ValueId& referenceId1, std::optional optId2) { + if constexpr (check_is_relational_v && + check_is_logical_v && optId2.has_value()) { + return std::make_unique( + makeLogExpr(referenceId1, optId2.value())); + } else { + return std::make_unique(makeRelExpr(referenceId1)); + } +} + +//______________________________________________________________________________ +template struct TestRelationalExpression { - void operator()(size_t evaluationColumn, ValueId referenceId, + void operator()(size_t evaluationColumn, const ValueId& referenceId, const std::vector& input, const ResT& expected) - requires check_is_relational_v { - auto expression = std::make_unique(referenceId); + requires check_is_relational_v { + auto expression = makeRelExpr(referenceId); if constexpr (std::is_same_v) { testThrowError(std::move(expression), evaluationColumn, input, expected); } else { @@ -191,14 +219,13 @@ struct TestRelationalExpression { template struct TestLogicalExpression { template - void test(size_t evaluationColumn, ValueId referenceIdChild1, + void test(size_t evaluationColumn, const ValueId& referenceIdChild1, ValueId referenceIdChild2, const std::vector& input, const ResT& expected) requires(check_is_logical_v && check_is_relational_v && check_is_relational_v) { - auto expression = std::make_unique( - std::make_unique(referenceIdChild1), - std::make_unique(referenceIdChild2)); + auto expression = makeLogExpr( + referenceIdChild1, referenceIdChild2); if constexpr (std::is_same_v) { testThrowError(std::move(expression), evaluationColumn, input, expected); } else { @@ -211,12 +238,14 @@ struct TestLogicalExpression { //______________________________________________________________________________ template struct TestNotExpression { - template - void test(size_t evaluationColumn, ValueId referenceIdChild, - const std::vector& input, const ResT& expected) - requires check_is_relational_v { - auto expression = std::make_unique( - std::make_unique(referenceIdChild)); + template + void test(size_t evaluationColumn, const std::vector& input, + const ResT& expected, const ValueId& referenceId1, + std::optional optId2 = std::nullopt) + requires check_is_relational_v { + auto expression = + makeNotExpression(referenceId1, optId2); if constexpr (std::is_same_v) { testThrowError(std::move(expression), evaluationColumn, input, expected); } else { @@ -655,51 +684,56 @@ TEST(LogicalExpression, testNotExpression) { MetadataBlocks blocks{}; TestNotExpression> testExpression{}; testExpression.test( - 2, VocabId(2), blocks.blocks, - {blocks.b14, blocks.b15, blocks.b16, blocks.b17}); - testExpression.test(2, VocabId(14), blocks.blocks, - {blocks.b14, blocks.b15, blocks.b17}); + 2, blocks.blocks, {blocks.b14, blocks.b15, blocks.b16, blocks.b17}, + VocabId(2)); + testExpression.test( + 2, blocks.blocks, {blocks.b14, blocks.b15, blocks.b17}, VocabId(14)); testExpression.test( - 2, VocabId(14), blocks.blocks, - {blocks.b14, blocks.b15, blocks.b16, blocks.b17}); + 2, blocks.blocks, {blocks.b14, blocks.b15, blocks.b16, blocks.b17}, + VocabId(14)); testExpression.test( - 2, VocabId(0), blocks.blocks, - {blocks.b14, blocks.b15, blocks.b16, blocks.b17}); + 2, blocks.blocks, {blocks.b14, blocks.b15, blocks.b16, blocks.b17}, + VocabId(0)); testExpression.test( - 2, DoubleId(-14.01), blocks.blocks, + 2, blocks.blocks, {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b5, blocks.b6, blocks.b7, blocks.b8, blocks.b9, blocks.b10, blocks.b11, blocks.b12, - blocks.b13, blocks.b14}); + blocks.b13, blocks.b14}, + DoubleId(-14.01)); testExpression.test( - 2, DoubleId(-14.01), blocks.blocks, {blocks.b7, blocks.b14}); + 2, blocks.blocks, {blocks.b7, blocks.b14}, DoubleId(-14.01)); testExpression.test( - 2, DoubleId(-4.00), blocks.blocks, + 2, blocks.blocks, {blocks.b5, blocks.b6, blocks.b7, blocks.b11, blocks.b12, blocks.b13, - blocks.b14}); - testExpression.test(2, DoubleId(-24.4), blocks.blocks, - {blocks.b7, blocks.b14}); + blocks.b14}, + DoubleId(-4.00)); + testExpression.test( + 2, blocks.blocks, {blocks.b7, blocks.b14}, DoubleId(-24.4)); testExpression.test( - 2, IntId(0), blocks.blocks, + 2, blocks.blocks, {blocks.b2, blocks.b3, blocks.b4, blocks.b7, blocks.b8, blocks.b9, - blocks.b10, blocks.b14}); + blocks.b10, blocks.b14}, + IntId(0)); testExpression.test( - 2, DoubleId(-6.25), blocks.blocks, + 2, blocks.blocks, {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b5, blocks.b6, blocks.b7, blocks.b8, blocks.b9, blocks.b10, blocks.b11, blocks.b13, - blocks.b14}); + blocks.b14}, + DoubleId(-6.25)); testExpression.test( - 2, DoubleId(4), blocks.blocks, - {blocks.b2, blocks.b7, blocks.b9, blocks.b10, blocks.b14}); + 2, blocks.blocks, + {blocks.b2, blocks.b7, blocks.b9, blocks.b10, blocks.b14}, DoubleId(4)); testExpression.test( - 2, DoubleId(0), blocks.blocks, + 2, blocks.blocks, {blocks.b1, blocks.b2, blocks.b5, blocks.b6, blocks.b7, blocks.b11, - blocks.b12, blocks.b13, blocks.b14}); - testExpression.test(0, blocks.VocabId10, blocks.blocks, {}); - testExpression.test(1, blocks.DoubleId33, blocks.blocks, {}); - testExpression.test(0, blocks.VocabId10, blocks.blocks, - blocks.blocks); - testExpression.test(1, blocks.DoubleId33, - blocks.blocks, {}); + blocks.b12, blocks.b13, blocks.b14}, + DoubleId(0)); + testExpression.test(0, blocks.blocks, {}, blocks.VocabId10); + testExpression.test(1, blocks.blocks, {}, blocks.DoubleId33); + testExpression.test(0, blocks.blocks, blocks.blocks, + blocks.VocabId10); + testExpression.test(1, blocks.blocks, {}, + blocks.DoubleId33); } //______________________________________________________________________________ @@ -720,22 +754,26 @@ TEST(PrefilterExpression, testInputConditionCheck) { TestNotExpression testNotExpression{}; testNotExpression.test( - 2, VocabId(2), blocks.blocksWithDuplicate1, - "The provided data blocks must be unique."); + 2, blocks.blocksWithDuplicate1, + "The provided data blocks must be unique.", VocabId(2)); testNotExpression.test( - 2, DoubleId(-14.1), blocks.blocksInvalidOrder1, + 2, blocks.blocksInvalidOrder1, "The data blocks must be provided in sorted order regarding the " - "evaluation column."); + "evaluation column.", + DoubleId(-14.1)); testNotExpression.test( - 0, IntId(0), blocks.blocksInvalidCol2, + 0, blocks.blocksInvalidCol2, "The data blocks must be provided in sorted order regarding the " - "evaluation column."); + "evaluation column.", + IntId(0)); testNotExpression.test( - 1, IntId(0), blocks.blocksInvalidCol2, - "The columns up to the evaluation column must contain the same values."); + 1, blocks.blocksInvalidCol2, + "The columns up to the evaluation column must contain the same values.", + IntId(0)); testNotExpression.test( - 2, IntId(0), blocks.blocksInvalidCol2, - "The columns up to the evaluation column must contain the same values."); + 2, blocks.blocksInvalidCol2, + "The columns up to the evaluation column must contain the same values.", + IntId(0)); TestLogicalExpression testAndExpression{}; testAndExpression.test( From 69ed0195a83b380d7e868203b9db0f29e8b8f65d Mon Sep 17 00:00:00 2001 From: realHannes Date: Wed, 9 Oct 2024 11:06:04 +0200 Subject: [PATCH 33/81] changes to test and fix index check fail --- src/global/ValueId.h | 2 +- src/index/CompressedBlockPrefiltering.cpp | 6 +++-- test/CompressedBlockPrefilteringTest.cpp | 31 ++++++++++++++--------- 3 files changed, 24 insertions(+), 15 deletions(-) diff --git a/src/global/ValueId.h b/src/global/ValueId.h index e344fe4f52..fe429b98fa 100644 --- a/src/global/ValueId.h +++ b/src/global/ValueId.h @@ -29,9 +29,9 @@ enum struct Datatype { VocabIndex, LocalVocabIndex, TextRecordIndex, - WordVocabIndex, Date, GeoPoint, + WordVocabIndex, BlankNodeIndex, MaxValue = BlankNodeIndex // Note: Unfortunately we cannot easily get the size of an enum. diff --git a/src/index/CompressedBlockPrefiltering.cpp b/src/index/CompressedBlockPrefiltering.cpp index 69e3696807..2f0b3b8956 100644 --- a/src/index/CompressedBlockPrefiltering.cpp +++ b/src/index/CompressedBlockPrefiltering.cpp @@ -8,7 +8,7 @@ namespace prefilterExpressions { -// HELPER FUNCTION +// HELPER FUNCTIONS //______________________________________________________________________________ // Given a PermutedTriple retrieve the suitable Id w.r.t. a column (index). static auto getIdFromColumnIndex(const BlockMetadata::PermutedTriple& triple, @@ -196,7 +196,9 @@ std::vector RelationalExpression::evaluateImpl( if ((firstIdDatatype != secondIdDatatype) && // The block is only potentially relevant if at least one of the // respective bounding Ids is of similar (comparable) type to - // refernceId_. + // refernceId_. However, this requires that the comparable datatypes + // are grouped/stand next to each other given the ValueId ordering + // on datatype (most relevant) bits. (valueIdComparators::detail::areTypesCompatible(referenceIdDatatype, firstIdDatatype) || valueIdComparators::detail::areTypesCompatible(referenceIdDatatype, diff --git a/test/CompressedBlockPrefilteringTest.cpp b/test/CompressedBlockPrefilteringTest.cpp index ade741f92b..5c88477ccc 100644 --- a/test/CompressedBlockPrefilteringTest.cpp +++ b/test/CompressedBlockPrefilteringTest.cpp @@ -62,7 +62,7 @@ struct MetadataBlocks { const BlockMetadata b4 = makeBlock(IntId(8), IntId(9)); const BlockMetadata b5 = makeBlock(IntId(-10), IntId(-8)); const BlockMetadata b6 = makeBlock(IntId(-4), IntId(-4)); - // b7 contains mixed datatypes in COLUMN 2 + // b7 contains mixed datatypes (COLUMN 2) const BlockMetadata b7 = makeBlock(IntId(-4), DoubleId(2)); const BlockMetadata b8 = makeBlock(DoubleId(2), DoubleId(2)); const BlockMetadata b9 = makeBlock(DoubleId(4), DoubleId(4)); @@ -70,7 +70,7 @@ struct MetadataBlocks { const BlockMetadata b11 = makeBlock(DoubleId(-1.23), DoubleId(-6.25)); const BlockMetadata b12 = makeBlock(DoubleId(-6.25), DoubleId(-6.25)); const BlockMetadata b13 = makeBlock(DoubleId(-10.42), DoubleId(-12.00)); - // b14 contains mixed datatypes + // b14 contains mixed datatypes (COLUMN 2) const BlockMetadata b14 = makeBlock(DoubleId(-14.01), VocabId(0)); const BlockMetadata b15 = makeBlock(VocabId(10), VocabId(14)); const BlockMetadata b16 = makeBlock(VocabId(14), VocabId(14)); @@ -173,27 +173,28 @@ static const auto testThrowError = //______________________________________________________________________________ template -constexpr std::unique_ptr makeRelExpr( - const ValueId& referenceId) { +auto makeRelExpr(const ValueId& referenceId) { return std::make_unique(referenceId); } //______________________________________________________________________________ template -constexpr std::unique_ptr makeLogExpr( - const ValueId& referenceId1, const ValueId& referenceId2) { +auto makeLogExpr(const ValueId& referenceId1, const ValueId& referenceId2) { return std::make_unique(makeRelExpr(referenceId1), makeRelExpr(referenceId2)); } //______________________________________________________________________________ -template -constexpr std::unique_ptr makeNotExpression( - const ValueId& referenceId1, std::optional optId2) { +template +auto makeNotExpression(const ValueId& referenceId1, + std::optional optId2) { if constexpr (check_is_relational_v && check_is_logical_v && optId2.has_value()) { return std::make_unique( makeLogExpr(referenceId1, optId2.value())); + } else if constexpr (std::is_same_v) { + return std::make_unique( + makeNotExpression(referenceId1)); } else { return std::make_unique(makeRelExpr(referenceId1)); } @@ -238,14 +239,14 @@ struct TestLogicalExpression { //______________________________________________________________________________ template struct TestNotExpression { - template + template void test(size_t evaluationColumn, const std::vector& input, const ResT& expected, const ValueId& referenceId1, std::optional optId2 = std::nullopt) requires check_is_relational_v { auto expression = - makeNotExpression(referenceId1, optId2); + makeNotExpression(referenceId1, optId2); if constexpr (std::is_same_v) { testThrowError(std::move(expression), evaluationColumn, input, expected); } else { @@ -305,6 +306,7 @@ TEST(RelationalExpression, testLessThanExpressions) { testExpression(2, VocabId(14), blocks.blocks, {blocks.b14, blocks.b15}); testExpression(2, VocabId(16), blocks.blocks, {blocks.b14, blocks.b15, blocks.b16, blocks.b17}); + // test blocks.otherBlocks testExpression(2, blocks.undef, blocks.otherBlocks, {}); testExpression(2, blocks.falseId, blocks.otherBlocks, {blocks.bd2, blocks.bd5}); @@ -352,6 +354,7 @@ TEST(RelationalExpression, testLessEqualExpressions) { testExpression(2, VocabId(11), blocks.blocks, {blocks.b14, blocks.b15}); testExpression(2, VocabId(14), blocks.blocks, {blocks.b14, blocks.b15, blocks.b16, blocks.b17}); + // test block.otherBlocks testExpression(2, blocks.undef, blocks.otherBlocks, {}); testExpression(2, blocks.falseId, blocks.otherBlocks, {blocks.bd2, blocks.bd3, blocks.bd5}); @@ -401,6 +404,7 @@ TEST(RelationalExpression, testGreaterThanExpression) { testExpression(2, VocabId(14), blocks.blocks, {blocks.b14, blocks.b17}); testExpression(2, VocabId(12), blocks.blocks, {blocks.b14, blocks.b15, blocks.b16, blocks.b17}); + // test blocks.otherBlocks testExpression(2, blocks.undef, blocks.otherBlocks, {}); testExpression(2, blocks.falseId, blocks.otherBlocks, {blocks.bd2, blocks.bd4, blocks.bd5}); @@ -454,6 +458,7 @@ TEST(RelationalExpression, testGreaterEqualExpression) { testExpression(2, VocabId(10), blocks.blocks, {blocks.b14, blocks.b15, blocks.b16, blocks.b17}); testExpression(2, VocabId(17), blocks.blocks, {blocks.b14, blocks.b17}); + // test blocks.otherBlocks testExpression(2, blocks.undef, blocks.otherBlocks, {}); testExpression(2, blocks.falseId, blocks.otherBlocks, {blocks.bd2, blocks.bd3, blocks.bd4, blocks.bd5}); @@ -498,6 +503,7 @@ TEST(RelationalExpression, testEqualExpression) { testExpression(2, VocabId(17), blocks.blocks, {blocks.b14, blocks.b17}); testExpression(2, IntId(-4), blocks.blocks, {blocks.b6, blocks.b7, blocks.b11, blocks.b14}); + // test blocks.otherBlocks testExpression(2, blocks.trueId, blocks.otherBlocks, {blocks.bd2, blocks.bd4, blocks.bd5}); testExpression(2, blocks.referenceDate1, blocks.otherBlocks, @@ -550,6 +556,7 @@ TEST(RelationalExpression, testNotEqualExpression) { {blocks.b14, blocks.b15, blocks.b17}); testExpression(2, VocabId(17), blocks.blocks, {blocks.b14, blocks.b15, blocks.b16, blocks.b17}); + // test blocks.otherBlocks testExpression(2, blocks.undef, blocks.otherBlocks, {}); testExpression(2, blocks.falseId, blocks.otherBlocks, {blocks.bd2, blocks.bd4, blocks.bd5}); From 854322b91e444406e2afad588474358fdd128438 Mon Sep 17 00:00:00 2001 From: realHannes Date: Wed, 9 Oct 2024 13:48:01 +0200 Subject: [PATCH 34/81] increase test coverage for Not expression --- test/CompressedBlockPrefilteringTest.cpp | 87 ++++++++++++++++++++++-- 1 file changed, 80 insertions(+), 7 deletions(-) diff --git a/test/CompressedBlockPrefilteringTest.cpp b/test/CompressedBlockPrefilteringTest.cpp index 5c88477ccc..c9c7d5f9b8 100644 --- a/test/CompressedBlockPrefilteringTest.cpp +++ b/test/CompressedBlockPrefilteringTest.cpp @@ -173,28 +173,31 @@ static const auto testThrowError = //______________________________________________________________________________ template -auto makeRelExpr(const ValueId& referenceId) { +std::unique_ptr makeRelExpr(const ValueId& referenceId) { return std::make_unique(referenceId); } //______________________________________________________________________________ template -auto makeLogExpr(const ValueId& referenceId1, const ValueId& referenceId2) { +std::unique_ptr makeLogExpr(const ValueId& referenceId1, + const ValueId& referenceId2) { return std::make_unique(makeRelExpr(referenceId1), makeRelExpr(referenceId2)); } //______________________________________________________________________________ -template -auto makeNotExpression(const ValueId& referenceId1, - std::optional optId2) { +template +std::unique_ptr makeNotExpression( + const ValueId& referenceId1, const std::optional optId2) { if constexpr (check_is_relational_v && - check_is_logical_v && optId2.has_value()) { + check_is_logical_v) { + assert(optId2.has_value() && "Logical Expressions require two ValueIds"); return std::make_unique( makeLogExpr(referenceId1, optId2.value())); } else if constexpr (std::is_same_v) { return std::make_unique( - makeNotExpression(referenceId1)); + makeNotExpression(referenceId1, optId2)); } else { return std::make_unique(makeRelExpr(referenceId1)); } @@ -741,6 +744,76 @@ TEST(LogicalExpression, testNotExpression) { blocks.VocabId10); testExpression.test(1, blocks.blocks, {}, blocks.DoubleId33); + testExpression.test( + 2, blocks.blocks, {blocks.b1, blocks.b2, blocks.b7, blocks.b14}, + IntId(0)); + testExpression.test( + 2, blocks.blocks, + {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b5, blocks.b6, + blocks.b7, blocks.b8, blocks.b9, blocks.b10, blocks.b11, blocks.b13, + blocks.b14}, + DoubleId(-6.25)); + testExpression.test( + 2, blocks.blocks, {blocks.b14}, VocabId(10)); + testExpression.test( + 2, blocks.blocks, + {blocks.b2, blocks.b3, blocks.b4, blocks.b7, blocks.b9, blocks.b10, + blocks.b14}, + DoubleId(3.99)); + testExpression + .test( + 2, blocks.blocks, + {blocks.b2, blocks.b3, blocks.b4, blocks.b5, blocks.b6, blocks.b7, + blocks.b8, blocks.b9, blocks.b10, blocks.b11, blocks.b12, blocks.b13, + blocks.b14}, + IntId(0), IntId(0)); + testExpression.test( + 2, blocks.blocks, {blocks.b5, blocks.b7, blocks.b14}, IntId(-10), + DoubleId(-14.02)); + testExpression + .test( + 2, blocks.blocks, + {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b5, blocks.b6, + blocks.b7, blocks.b8, blocks.b9, blocks.b10, blocks.b11, blocks.b12, + blocks.b13, blocks.b14}, + IntId(10), DoubleId(-6.25)); + testExpression + .test( + 2, blocks.blocks, + {blocks.b5, blocks.b6, blocks.b7, blocks.b11, blocks.b12, blocks.b13, + blocks.b14}, + IntId(-4), DoubleId(-6.25)); + testExpression + .test( + 2, blocks.blocks, + {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b5, blocks.b6, + blocks.b7, blocks.b8, blocks.b9, blocks.b10, blocks.b11, blocks.b12, + blocks.b13, blocks.b14}, + DoubleId(-7), IntId(6)); + testExpression + .test( + 2, blocks.blocks, + {blocks.b2, blocks.b3, blocks.b7, blocks.b8, blocks.b9, blocks.b10, + blocks.b14}, + IntId(0), DoubleId(6)); + testExpression + .test( + 2, blocks.blocks, {blocks.b5, blocks.b7, blocks.b13, blocks.b14}, + DoubleId(0), IntId(-10)); + testExpression.test( + 2, blocks.blocks, {blocks.b14, blocks.b15}, VocabId(10), VocabId(10)); + testExpression.test( + 2, blocks.blocks, {blocks.b6, blocks.b7, blocks.b11, blocks.b14}, + DoubleId(-4), IntId(-4)); + testExpression + .test( + 2, blocks.blocks, {blocks.b14}, IntId(-42), VocabId(0)); + testExpression + .test( + 2, blocks.blocks, {blocks.b14, blocks.b15}, VocabId(14), VocabId(15)); + testExpression.test( + 2, blocks.blocks, {blocks.b7, blocks.b11, blocks.b12, blocks.b14}, + DoubleId(-7.25), DoubleId(-6.25)); } //______________________________________________________________________________ From b2aa68930f3fdf365296a7335b74dbd59487e4e5 Mon Sep 17 00:00:00 2001 From: realHannes Date: Wed, 9 Oct 2024 16:24:25 +0200 Subject: [PATCH 35/81] just add all mixed blocks --- src/index/CompressedBlockPrefiltering.cpp | 14 +- test/CompressedBlockPrefilteringTest.cpp | 170 ++++++++++++---------- 2 files changed, 95 insertions(+), 89 deletions(-) diff --git a/src/index/CompressedBlockPrefiltering.cpp b/src/index/CompressedBlockPrefiltering.cpp index 2f0b3b8956..85a4cf13b0 100644 --- a/src/index/CompressedBlockPrefiltering.cpp +++ b/src/index/CompressedBlockPrefiltering.cpp @@ -187,22 +187,10 @@ std::vector RelationalExpression::evaluateImpl( getIdFromColumnIndex(block.firstTriple_, evaluationColumn); const auto secondId = getIdFromColumnIndex(block.lastTriple_, evaluationColumn); - const auto firstIdDatatype = firstId.getDatatype(); - const auto secondIdDatatype = secondId.getDatatype(); - const auto referenceIdDatatype = referenceId_.getDatatype(); valueIdsInput.push_back(firstId); valueIdsInput.push_back(secondId); - if ((firstIdDatatype != secondIdDatatype) && - // The block is only potentially relevant if at least one of the - // respective bounding Ids is of similar (comparable) type to - // refernceId_. However, this requires that the comparable datatypes - // are grouped/stand next to each other given the ValueId ordering - // on datatype (most relevant) bits. - (valueIdComparators::detail::areTypesCompatible(referenceIdDatatype, - firstIdDatatype) || - valueIdComparators::detail::areTypesCompatible(referenceIdDatatype, - secondIdDatatype))) { + if ((firstId.getDatatype() != secondId.getDatatype())) { mixedDatatypeBlocks.push_back(block); } } diff --git a/test/CompressedBlockPrefilteringTest.cpp b/test/CompressedBlockPrefilteringTest.cpp index c9c7d5f9b8..8caab1c0ff 100644 --- a/test/CompressedBlockPrefilteringTest.cpp +++ b/test/CompressedBlockPrefilteringTest.cpp @@ -304,24 +304,28 @@ TEST(RelationalExpression, testLessThanExpressions) { testExpression( 2, DoubleId(-4.121), blocks.blocks, {blocks.b5, blocks.b7, blocks.b11, blocks.b12, blocks.b13, blocks.b14}); - testExpression(2, VocabId(0), blocks.blocks, {blocks.b14}); - testExpression(2, VocabId(12), blocks.blocks, {blocks.b14, blocks.b15}); - testExpression(2, VocabId(14), blocks.blocks, {blocks.b14, blocks.b15}); + testExpression(2, VocabId(0), blocks.blocks, {blocks.b7, blocks.b14}); + testExpression(2, VocabId(12), blocks.blocks, + {blocks.b7, blocks.b14, blocks.b15}); + testExpression(2, VocabId(14), blocks.blocks, + {blocks.b7, blocks.b14, blocks.b15}); testExpression(2, VocabId(16), blocks.blocks, - {blocks.b14, blocks.b15, blocks.b16, blocks.b17}); + {blocks.b7, blocks.b14, blocks.b15, blocks.b16, blocks.b17}); // test blocks.otherBlocks - testExpression(2, blocks.undef, blocks.otherBlocks, {}); + testExpression(2, blocks.undef, blocks.otherBlocks, + {blocks.bd2, blocks.bd5, blocks.bd7}); testExpression(2, blocks.falseId, blocks.otherBlocks, - {blocks.bd2, blocks.bd5}); + {blocks.bd2, blocks.bd5, blocks.bd7}); testExpression(2, blocks.trueId, blocks.otherBlocks, - {blocks.bd2, blocks.bd3, blocks.bd5}); + {blocks.bd2, blocks.bd3, blocks.bd5, blocks.bd7}); testExpression(2, blocks.referenceDate1, blocks.otherBlocks, - {blocks.bd5, blocks.bd7}); + {blocks.bd2, blocks.bd5, blocks.bd7}); testExpression(2, blocks.referenceDateEqual, blocks.otherBlocks, - {blocks.bd5, blocks.bd7}); + {blocks.bd2, blocks.bd5, blocks.bd7}); testExpression(2, blocks.referenceDate2, blocks.otherBlocks, - {blocks.bd5, blocks.bd6, blocks.bd7}); - testExpression(2, BlankNodeId(11), blocks.otherBlocks, {blocks.bd7}); + {blocks.bd2, blocks.bd5, blocks.bd6, blocks.bd7}); + testExpression(2, BlankNodeId(11), blocks.otherBlocks, + {blocks.bd2, blocks.bd5, blocks.bd7}); } //______________________________________________________________________________ @@ -353,19 +357,22 @@ TEST(RelationalExpression, testLessEqualExpressions) { testExpression(2, DoubleId(-11.99999999999999), blocks.blocks, {blocks.b7, blocks.b13, blocks.b14}); testExpression(2, DoubleId(-14.03), blocks.blocks, {blocks.b7, blocks.b14}); - testExpression(2, VocabId(0), blocks.blocks, {blocks.b14}); - testExpression(2, VocabId(11), blocks.blocks, {blocks.b14, blocks.b15}); + testExpression(2, VocabId(0), blocks.blocks, {blocks.b7, blocks.b14}); + testExpression(2, VocabId(11), blocks.blocks, + {blocks.b7, blocks.b14, blocks.b15}); testExpression(2, VocabId(14), blocks.blocks, - {blocks.b14, blocks.b15, blocks.b16, blocks.b17}); + {blocks.b7, blocks.b14, blocks.b15, blocks.b16, blocks.b17}); // test block.otherBlocks - testExpression(2, blocks.undef, blocks.otherBlocks, {}); + testExpression(2, blocks.undef, blocks.otherBlocks, + {blocks.bd2, blocks.bd5, blocks.bd7}); testExpression(2, blocks.falseId, blocks.otherBlocks, - {blocks.bd2, blocks.bd3, blocks.bd5}); + {blocks.bd2, blocks.bd3, blocks.bd5, blocks.bd7}); testExpression(2, blocks.trueId, blocks.otherBlocks, - {blocks.bd2, blocks.bd3, blocks.bd4, blocks.bd5}); + {blocks.bd2, blocks.bd3, blocks.bd4, blocks.bd5, blocks.bd7}); testExpression(2, blocks.referenceDateEqual, blocks.otherBlocks, - {blocks.bd5, blocks.bd6, blocks.bd7}); - testExpression(2, BlankNodeId(11), blocks.otherBlocks, {blocks.bd7}); + {blocks.bd2, blocks.bd5, blocks.bd6, blocks.bd7}); + testExpression(2, BlankNodeId(11), blocks.otherBlocks, + {blocks.bd2, blocks.bd5, blocks.bd7}); } //______________________________________________________________________________ @@ -403,21 +410,24 @@ TEST(RelationalExpression, testGreaterThanExpression) { {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b7, blocks.b8, blocks.b9, blocks.b10, blocks.b11, blocks.b14}); testExpression(2, IntId(33), blocks.blocks, {blocks.b7, blocks.b14}); - testExpression(2, VocabId(22), blocks.blocks, {blocks.b14}); - testExpression(2, VocabId(14), blocks.blocks, {blocks.b14, blocks.b17}); + testExpression(2, VocabId(22), blocks.blocks, {blocks.b7, blocks.b14}); + testExpression(2, VocabId(14), blocks.blocks, + {blocks.b7, blocks.b14, blocks.b17}); testExpression(2, VocabId(12), blocks.blocks, - {blocks.b14, blocks.b15, blocks.b16, blocks.b17}); + {blocks.b7, blocks.b14, blocks.b15, blocks.b16, blocks.b17}); // test blocks.otherBlocks - testExpression(2, blocks.undef, blocks.otherBlocks, {}); + testExpression(2, blocks.undef, blocks.otherBlocks, + {blocks.bd2, blocks.bd5, blocks.bd7}); testExpression(2, blocks.falseId, blocks.otherBlocks, - {blocks.bd2, blocks.bd4, blocks.bd5}); + {blocks.bd2, blocks.bd4, blocks.bd5, blocks.bd7}); testExpression(2, blocks.trueId, blocks.otherBlocks, - {blocks.bd2, blocks.bd5}); + {blocks.bd2, blocks.bd5, blocks.bd7}); testExpression(2, blocks.referenceDateEqual, blocks.otherBlocks, - {blocks.bd5, blocks.bd7}); + {blocks.bd2, blocks.bd5, blocks.bd7}); testExpression(2, blocks.referenceDate1, blocks.otherBlocks, - {blocks.bd5, blocks.bd6, blocks.bd7}); - testExpression(2, IntId(5), blocks.otherBlocks, {}); + {blocks.bd2, blocks.bd5, blocks.bd6, blocks.bd7}); + testExpression(2, IntId(5), blocks.otherBlocks, + {blocks.bd2, blocks.bd5, blocks.bd7}); } //______________________________________________________________________________ @@ -457,19 +467,22 @@ TEST(RelationalExpression, testGreaterEqualExpression) { {blocks.b4, blocks.b7, blocks.b10, blocks.b14}); testExpression(2, DoubleId(10.0001), blocks.blocks, {blocks.b7, blocks.b14}); testExpression(2, VocabId(14), blocks.blocks, - {blocks.b14, blocks.b15, blocks.b16, blocks.b17}); + {blocks.b7, blocks.b14, blocks.b15, blocks.b16, blocks.b17}); testExpression(2, VocabId(10), blocks.blocks, - {blocks.b14, blocks.b15, blocks.b16, blocks.b17}); - testExpression(2, VocabId(17), blocks.blocks, {blocks.b14, blocks.b17}); + {blocks.b7, blocks.b14, blocks.b15, blocks.b16, blocks.b17}); + testExpression(2, VocabId(17), blocks.blocks, + {blocks.b7, blocks.b14, blocks.b17}); // test blocks.otherBlocks - testExpression(2, blocks.undef, blocks.otherBlocks, {}); + testExpression(2, blocks.undef, blocks.otherBlocks, + {blocks.bd2, blocks.bd5, blocks.bd7}); testExpression(2, blocks.falseId, blocks.otherBlocks, - {blocks.bd2, blocks.bd3, blocks.bd4, blocks.bd5}); + {blocks.bd2, blocks.bd3, blocks.bd4, blocks.bd5, blocks.bd7}); testExpression(2, blocks.trueId, blocks.otherBlocks, - {blocks.bd2, blocks.bd4, blocks.bd5}); + {blocks.bd2, blocks.bd4, blocks.bd5, blocks.bd7}); testExpression(2, blocks.referenceDateEqual, blocks.otherBlocks, - {blocks.bd5, blocks.bd6, blocks.bd7}); - testExpression(2, VocabId(0), blocks.otherBlocks, {}); + {blocks.bd2, blocks.bd5, blocks.bd6, blocks.bd7}); + testExpression(2, VocabId(0), blocks.otherBlocks, + {blocks.bd2, blocks.bd5, blocks.bd7}); } //______________________________________________________________________________ @@ -499,22 +512,24 @@ TEST(RelationalExpression, testEqualExpression) { {blocks.b3, blocks.b7, blocks.b10, blocks.b14}); testExpression(2, DoubleId(1.5), blocks.blocks, {blocks.b2, blocks.b7, blocks.b14}); - testExpression(2, VocabId(1), blocks.blocks, {blocks.b14}); + testExpression(2, VocabId(1), blocks.blocks, {blocks.b7, blocks.b14}); testExpression(2, VocabId(14), blocks.blocks, - {blocks.b14, blocks.b15, blocks.b16, blocks.b17}); - testExpression(2, VocabId(11), blocks.blocks, {blocks.b14, blocks.b15}); - testExpression(2, VocabId(17), blocks.blocks, {blocks.b14, blocks.b17}); + {blocks.b7, blocks.b14, blocks.b15, blocks.b16, blocks.b17}); + testExpression(2, VocabId(11), blocks.blocks, + {blocks.b7, blocks.b14, blocks.b15}); + testExpression(2, VocabId(17), blocks.blocks, + {blocks.b7, blocks.b14, blocks.b17}); testExpression(2, IntId(-4), blocks.blocks, {blocks.b6, blocks.b7, blocks.b11, blocks.b14}); // test blocks.otherBlocks testExpression(2, blocks.trueId, blocks.otherBlocks, - {blocks.bd2, blocks.bd4, blocks.bd5}); + {blocks.bd2, blocks.bd4, blocks.bd5, blocks.bd7}); testExpression(2, blocks.referenceDate1, blocks.otherBlocks, - {blocks.bd5, blocks.bd7}); + {blocks.bd2, blocks.bd5, blocks.bd7}); testExpression(2, blocks.referenceDateEqual, blocks.otherBlocks, - {blocks.bd5, blocks.bd6, blocks.bd7}); + {blocks.bd2, blocks.bd5, blocks.bd6, blocks.bd7}); testExpression(2, blocks.referenceDate2, blocks.otherBlocks, - {blocks.bd5, blocks.bd7}); + {blocks.bd2, blocks.bd5, blocks.bd7}); } //______________________________________________________________________________ @@ -552,21 +567,22 @@ TEST(RelationalExpression, testNotEqualExpression) { blocks.b6, blocks.b7, blocks.b8, blocks.b9, blocks.b10, blocks.b11, blocks.b12, blocks.b13, blocks.b14}); testExpression(2, VocabId(0), blocks.blocks, - {blocks.b14, blocks.b15, blocks.b16, blocks.b17}); + {blocks.b7, blocks.b14, blocks.b15, blocks.b16, blocks.b17}); testExpression(2, VocabId(7), blocks.blocks, - {blocks.b14, blocks.b15, blocks.b16, blocks.b17}); + {blocks.b7, blocks.b14, blocks.b15, blocks.b16, blocks.b17}); testExpression(2, VocabId(14), blocks.blocks, - {blocks.b14, blocks.b15, blocks.b17}); + {blocks.b7, blocks.b14, blocks.b15, blocks.b17}); testExpression(2, VocabId(17), blocks.blocks, - {blocks.b14, blocks.b15, blocks.b16, blocks.b17}); + {blocks.b7, blocks.b14, blocks.b15, blocks.b16, blocks.b17}); // test blocks.otherBlocks - testExpression(2, blocks.undef, blocks.otherBlocks, {}); + testExpression(2, blocks.undef, blocks.otherBlocks, + {blocks.bd2, blocks.bd5, blocks.bd7}); testExpression(2, blocks.falseId, blocks.otherBlocks, - {blocks.bd2, blocks.bd4, blocks.bd5}); + {blocks.bd2, blocks.bd4, blocks.bd5, blocks.bd7}); testExpression(2, blocks.referenceDateEqual, blocks.otherBlocks, - {blocks.bd5, blocks.bd7}); + {blocks.bd2, blocks.bd5, blocks.bd7}); testExpression(2, blocks.referenceDate1, blocks.otherBlocks, - {blocks.bd5, blocks.bd6, blocks.bd7}); + {blocks.bd2, blocks.bd5, blocks.bd6, blocks.bd7}); } //______________________________________________________________________________ @@ -580,27 +596,25 @@ TEST(LogicalExpression, testAndExpression) { testExpression; testExpression.test( 2, VocabId(10), VocabId(10), blocks.blocks, - {blocks.b14, blocks.b15, blocks.b16, blocks.b17}); + {blocks.b7, blocks.b14, blocks.b15, blocks.b16, blocks.b17}); testExpression.test( - 2, VocabId(0), VocabId(17), blocks.blocks, {blocks.b14, blocks.b17}); + 2, VocabId(0), VocabId(17), blocks.blocks, + {blocks.b7, blocks.b14, blocks.b17}); testExpression.test( - 2, VocabId(12), VocabId(17), blocks.blocks, {blocks.b14}); + 2, VocabId(12), VocabId(17), blocks.blocks, {blocks.b7, blocks.b14}); testExpression.test( - 2, VocabId(10), VocabId(14), blocks.blocks, {blocks.b14, blocks.b15}); + 2, VocabId(10), VocabId(14), blocks.blocks, + {blocks.b7, blocks.b14, blocks.b15}); testExpression.test( - 2, VocabId(0), VocabId(10), blocks.blocks, {blocks.b14}); + 2, VocabId(0), VocabId(10), blocks.blocks, {blocks.b7, blocks.b14}); testExpression.test( 2, VocabId(17), VocabId(17), blocks.blocks, - {blocks.b14, blocks.b15, blocks.b16, blocks.b17}); + {blocks.b7, blocks.b14, blocks.b15, blocks.b16, blocks.b17}); testExpression.test( 2, DoubleId(-6.25), IntId(-7), blocks.blocks, {blocks.b7, blocks.b14}); testExpression.test( 2, DoubleId(-6.25), DoubleId(-6.25), blocks.blocks, {blocks.b7, blocks.b14}); - // Corner case: Logically it is impossible to satisfy (x > 0) and (x < 0) at - // the same time. But given that we evaluate on block boundaries and their - // possible values (Ids) in between, block b7 satisfies both conditions over - // its range [IntId(-4)... DoubleId(2)] for column 2. testExpression.test( 2, IntId(0), IntId(0), blocks.blocks, {blocks.b7, blocks.b14}); testExpression.test( @@ -645,11 +659,12 @@ TEST(LogicalExpression, testOrExpression) { testExpression; testExpression.test( 2, VocabId(22), VocabId(0), blocks.blocks, - {blocks.b14, blocks.b15, blocks.b16, blocks.b17}); + {blocks.b7, blocks.b14, blocks.b15, blocks.b16, blocks.b17}); testExpression.test( - 2, VocabId(0), VocabId(16), blocks.blocks, {blocks.b14, blocks.b17}); + 2, VocabId(0), VocabId(16), blocks.blocks, + {blocks.b7, blocks.b14, blocks.b17}); testExpression.test( - 2, VocabId(17), VocabId(242), blocks.blocks, {blocks.b14}); + 2, VocabId(17), VocabId(242), blocks.blocks, {blocks.b7, blocks.b14}); testExpression.test( 2, DoubleId(-5.95), VocabId(14), blocks.blocks, {blocks.b5, blocks.b7, blocks.b11, blocks.b12, blocks.b13, blocks.b14, @@ -694,16 +709,17 @@ TEST(LogicalExpression, testNotExpression) { MetadataBlocks blocks{}; TestNotExpression> testExpression{}; testExpression.test( - 2, blocks.blocks, {blocks.b14, blocks.b15, blocks.b16, blocks.b17}, - VocabId(2)); + 2, blocks.blocks, + {blocks.b7, blocks.b14, blocks.b15, blocks.b16, blocks.b17}, VocabId(2)); testExpression.test( - 2, blocks.blocks, {blocks.b14, blocks.b15, blocks.b17}, VocabId(14)); - testExpression.test( - 2, blocks.blocks, {blocks.b14, blocks.b15, blocks.b16, blocks.b17}, + 2, blocks.blocks, {blocks.b7, blocks.b14, blocks.b15, blocks.b17}, VocabId(14)); + testExpression.test( + 2, blocks.blocks, + {blocks.b7, blocks.b14, blocks.b15, blocks.b16, blocks.b17}, VocabId(14)); testExpression.test( - 2, blocks.blocks, {blocks.b14, blocks.b15, blocks.b16, blocks.b17}, - VocabId(0)); + 2, blocks.blocks, + {blocks.b7, blocks.b14, blocks.b15, blocks.b16, blocks.b17}, VocabId(0)); testExpression.test( 2, blocks.blocks, {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b5, blocks.b6, @@ -754,7 +770,7 @@ TEST(LogicalExpression, testNotExpression) { blocks.b14}, DoubleId(-6.25)); testExpression.test( - 2, blocks.blocks, {blocks.b14}, VocabId(10)); + 2, blocks.blocks, {blocks.b7, blocks.b14}, VocabId(10)); testExpression.test( 2, blocks.blocks, {blocks.b2, blocks.b3, blocks.b4, blocks.b7, blocks.b9, blocks.b10, @@ -801,16 +817,18 @@ TEST(LogicalExpression, testNotExpression) { 2, blocks.blocks, {blocks.b5, blocks.b7, blocks.b13, blocks.b14}, DoubleId(0), IntId(-10)); testExpression.test( - 2, blocks.blocks, {blocks.b14, blocks.b15}, VocabId(10), VocabId(10)); + 2, blocks.blocks, {blocks.b7, blocks.b14, blocks.b15}, VocabId(10), + VocabId(10)); testExpression.test( 2, blocks.blocks, {blocks.b6, blocks.b7, blocks.b11, blocks.b14}, DoubleId(-4), IntId(-4)); testExpression .test( - 2, blocks.blocks, {blocks.b14}, IntId(-42), VocabId(0)); + 2, blocks.blocks, {blocks.b7, blocks.b14}, IntId(-42), VocabId(0)); testExpression .test( - 2, blocks.blocks, {blocks.b14, blocks.b15}, VocabId(14), VocabId(15)); + 2, blocks.blocks, {blocks.b7, blocks.b14, blocks.b15}, VocabId(14), + VocabId(15)); testExpression.test( 2, blocks.blocks, {blocks.b7, blocks.b11, blocks.b12, blocks.b14}, DoubleId(-7.25), DoubleId(-6.25)); From 7e61cb18baa272c077e70dc0ed5c3319557a97c9 Mon Sep 17 00:00:00 2001 From: realHannes Date: Wed, 9 Oct 2024 18:37:38 +0200 Subject: [PATCH 36/81] remove unnecessary values in test context --- test/SparqlExpressionTestHelpers.h | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/test/SparqlExpressionTestHelpers.h b/test/SparqlExpressionTestHelpers.h index 80cdae8a12..55a6734b51 100644 --- a/test/SparqlExpressionTestHelpers.h +++ b/test/SparqlExpressionTestHelpers.h @@ -61,10 +61,6 @@ struct TestContext { // vocab. Id notInVocabA, notInVocabB, notInVocabC, notInVocabD, notInVocabAelpha, notInVocabIri, notInVocabIriLit; - // LocalVocab IDs used for testing the block pre-filtering procedure in - // CompressedBlockPrefilteringTest - Id berlin, bonn, cologne, dortmund, düsseldorf, essen, frankfurt, - frankfurt_oder, hamburg, hannover, ingolstadt, karlsruhe, munich; TestContext() { // First get some IDs for strings from the vocabulary to later reuse them. // Note the `u_` inserted for the blank node (see 'BlankNode.cpp'). @@ -99,19 +95,6 @@ struct TestContext { addLocalIri(""); notInVocabIriLit = addLocalLiteral( "http://www.w3.org/1999/02/22-rdf-syntax-ns#langString"); - berlin = addLocalLiteral("berlin"); - bonn = addLocalLiteral("bonn"); - cologne = addLocalLiteral("cologne"); - düsseldorf = addLocalLiteral("düsseldorf"); - dortmund = addLocalLiteral("dortmund"); - essen = addLocalLiteral("essen"); - frankfurt = addLocalLiteral("frankfurt"); - frankfurt_oder = addLocalLiteral("frankfurt (FFO)"); - hamburg = addLocalLiteral("hamburg"); - hannover = addLocalLiteral("hannover"); - ingolstadt = addLocalLiteral("ingolstadt"); - karlsruhe = addLocalLiteral("karlsruhe"); - munich = addLocalLiteral("munich"); // Set up the `table` that represents the previous partial query results. It // has five columns/variables: ?ints (only integers), ?doubles (only From 009ebed90c0d98d8758ec65c210941641537f035 Mon Sep 17 00:00:00 2001 From: realHannes Date: Thu, 10 Oct 2024 07:44:08 +0200 Subject: [PATCH 37/81] for sonar check --- src/index/CompressedBlockPrefiltering.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index/CompressedBlockPrefiltering.cpp b/src/index/CompressedBlockPrefiltering.cpp index 85a4cf13b0..47d503e514 100644 --- a/src/index/CompressedBlockPrefiltering.cpp +++ b/src/index/CompressedBlockPrefiltering.cpp @@ -190,7 +190,7 @@ std::vector RelationalExpression::evaluateImpl( valueIdsInput.push_back(firstId); valueIdsInput.push_back(secondId); - if ((firstId.getDatatype() != secondId.getDatatype())) { + if (firstId.getDatatype() != secondId.getDatatype()) { mixedDatatypeBlocks.push_back(block); } } From 5b42c1f91126c6780ed532b0e0dab6f30feeafcf Mon Sep 17 00:00:00 2001 From: realHannes Date: Sat, 12 Oct 2024 14:02:36 +0200 Subject: [PATCH 38/81] methods to get PrefilterExpresssions from RelationalExpressions (SparqlExpression) --- .../sparqlExpressions/LiteralExpression.h | 10 ++-- .../RelationalExpressions.cpp | 50 +++++++++++++++++++ .../sparqlExpressions/RelationalExpressions.h | 8 +++ .../sparqlExpressions/SparqlExpression.cpp | 7 +++ .../sparqlExpressions/SparqlExpression.h | 8 +++ .../SparqlExpressionPimpl.cpp | 7 +++ .../sparqlExpressions/SparqlExpressionPimpl.h | 5 ++ src/index/CompressedBlockPrefiltering.cpp | 12 ----- src/index/CompressedBlockPrefiltering.h | 12 +++++ 9 files changed, 102 insertions(+), 17 deletions(-) diff --git a/src/engine/sparqlExpressions/LiteralExpression.h b/src/engine/sparqlExpressions/LiteralExpression.h index 9bd27b3406..9e913a0b30 100644 --- a/src/engine/sparqlExpressions/LiteralExpression.h +++ b/src/engine/sparqlExpressions/LiteralExpression.h @@ -24,7 +24,7 @@ class LiteralExpression : public SparqlExpression { mutable std::atomic cachedResult_ = nullptr; public: - // _________________________________________________________________________ + // ___________________________________________________________________________ explicit LiteralExpression(T _value) : _value{std::move(_value)} {} ~LiteralExpression() override { delete cachedResult_.load(std::memory_order_relaxed); @@ -83,7 +83,7 @@ class LiteralExpression : public SparqlExpression { } } - // _________________________________________________________________________ + // ___________________________________________________________________________ vector getUnaggregatedVariables() const override { if constexpr (std::is_same_v) { return {_value}; @@ -92,7 +92,7 @@ class LiteralExpression : public SparqlExpression { } } - // ______________________________________________________________________ + // ___________________________________________________________________________ string getCacheKey(const VariableToColumnMap& varColMap) const override { if constexpr (std::is_same_v) { if (!varColMap.contains(_value)) { @@ -118,13 +118,13 @@ class LiteralExpression : public SparqlExpression { } } - // ______________________________________________________________________ + // ___________________________________________________________________________ bool isConstantExpression() const override { return !std::is_same_v; } protected: - // _________________________________________________________________________ + // ___________________________________________________________________________ std::optional<::Variable> getVariableOrNullopt() const override { if constexpr (std::is_same_v) { return _value; diff --git a/src/engine/sparqlExpressions/RelationalExpressions.cpp b/src/engine/sparqlExpressions/RelationalExpressions.cpp index 91effef243..c60d49909c 100644 --- a/src/engine/sparqlExpressions/RelationalExpressions.cpp +++ b/src/engine/sparqlExpressions/RelationalExpressions.cpp @@ -409,6 +409,56 @@ ExpressionResult InExpression::evaluate( return result; } +// _____________________________________________________________________________ +template +std::optional, Variable>> +RelationalExpression::getPrefilterExpressionForMetadata() const { + AD_CORRECTNESS_CHECK(children_.size() == 2); + const SparqlExpression::Ptr& child0 = children_.at(0); + const SparqlExpression::Ptr& child1 = children_.at(1); + + auto checkBinarySearchEvaluable = [](const SparqlExpression::Ptr& child0, + const SparqlExpression::Ptr& child1) + -> std::optional> { + if (const auto* literalValueId = + dynamic_cast(child0.get())) { + if (const auto* literalVariable = + dynamic_cast(child1.get())) { + return std::make_pair(literalValueId->value(), + literalVariable->value()); + } + } + return std::nullopt; + }; + + auto createPairFilterExpressionVariable = + [this](std::optional> optValuePair) + -> std::pair, + Variable> { + const auto& [valueId, variable] = optValuePair.value(); + return std::make_pair( + std::make_unique>( + valueId), + variable); + }; + + // Option 1: child0 is a (constant) reference ValueId, while child1 contains a + // Variable to the respective column on which we want to filter. + const auto& optionalPair1 = checkBinarySearchEvaluable(child0, child1); + if (optionalPair1.has_value()) { + return createPairFilterExpressionVariable(optionalPair1); + } + + // Option 2: child1 is a (constant) reference ValueId, while child0 contains a + // Variable to the respective column on which we want to filter. + const auto& optionalPair2 = checkBinarySearchEvaluable(child1, child0); + if (optionalPair2.has_value()) { + return createPairFilterExpressionVariable(optionalPair2); + } + return std::nullopt; +} + // _____________________________________________________________________________ std::span InExpression::childrenImpl() { return children_; diff --git a/src/engine/sparqlExpressions/RelationalExpressions.h b/src/engine/sparqlExpressions/RelationalExpressions.h index f8364f0137..4eae15531c 100644 --- a/src/engine/sparqlExpressions/RelationalExpressions.h +++ b/src/engine/sparqlExpressions/RelationalExpressions.h @@ -38,6 +38,14 @@ class RelationalExpression : public SparqlExpression { // the appropriate data. std::optional getLanguageFilterExpression() const override; + // If this `RelationalExpression` is binary evaluable, return the + // corresponding `PrefilterExpression` for the pre-filtering procedure on + // `CompressedBlockMetadata`. In addition we return the `Variable` that + // corresponds to the sorted column. + std::optional, Variable>> + getPrefilterExpressionForMetadata() const override; + // These expressions are typically used inside `FILTER` clauses, so we need // proper estimates. Estimates getEstimatesForFilterExpression( diff --git a/src/engine/sparqlExpressions/SparqlExpression.cpp b/src/engine/sparqlExpressions/SparqlExpression.cpp index 7d1ad5dcf6..c62092498f 100644 --- a/src/engine/sparqlExpressions/SparqlExpression.cpp +++ b/src/engine/sparqlExpressions/SparqlExpression.cpp @@ -108,6 +108,13 @@ Estimates SparqlExpression::getEstimatesForFilterExpression( return {inputSizeEstimate, inputSizeEstimate}; } +// _____________________________________________________________________________ +std::optional, Variable>> +SparqlExpression::getPrefilterExpressionForMetadata() const { + return std::nullopt; +}; + // _____________________________________________________________________________ bool SparqlExpression::isConstantExpression() const { return false; } diff --git a/src/engine/sparqlExpressions/SparqlExpression.h b/src/engine/sparqlExpressions/SparqlExpression.h index 6a64c4551e..e82e86275b 100644 --- a/src/engine/sparqlExpressions/SparqlExpression.h +++ b/src/engine/sparqlExpressions/SparqlExpression.h @@ -96,6 +96,14 @@ class SparqlExpression { [[maybe_unused]] const std::optional& primarySortKeyVariable) const; + // Returns a `PrefilterExpression` with the respective `Variable` for the + // necessarily sorted column, if there are relational expressions (e.g. + // `>=` or `!=`) that are binary evaluable. Those expressions can also be + // linked via logical expressions (`&&`, `||` and `!`). + virtual std::optional, Variable>> + getPrefilterExpressionForMetadata() const; + // Returns true iff this expression is a simple constant. Default // implementation returns `false`. virtual bool isConstantExpression() const; diff --git a/src/engine/sparqlExpressions/SparqlExpressionPimpl.cpp b/src/engine/sparqlExpressions/SparqlExpressionPimpl.cpp index ed1e004bd0..92fa18cbab 100644 --- a/src/engine/sparqlExpressions/SparqlExpressionPimpl.cpp +++ b/src/engine/sparqlExpressions/SparqlExpressionPimpl.cpp @@ -92,6 +92,13 @@ auto SparqlExpressionPimpl::getEstimatesForFilterExpression( primarySortKeyVariable); } +//_____________________________________________________________________________ +std::optional, Variable>> +SparqlExpressionPimpl::getPrefilterExpressionForMetadata() const { + return _pimpl->getPrefilterExpressionForMetadata(); +} + // _____________________________________________________________________________ bool SparqlExpressionPimpl::containsLangExpression() const { return _pimpl->containsLangExpression(); diff --git a/src/engine/sparqlExpressions/SparqlExpressionPimpl.h b/src/engine/sparqlExpressions/SparqlExpressionPimpl.h index c9b023c020..f32f23203d 100644 --- a/src/engine/sparqlExpressions/SparqlExpressionPimpl.h +++ b/src/engine/sparqlExpressions/SparqlExpressionPimpl.h @@ -9,6 +9,7 @@ #include #include "engine/VariableToColumnMap.h" +#include "index/CompressedBlockPrefiltering.h" #include "parser/data/Variable.h" #include "util/HashMap.h" #include "util/HashSet.h" @@ -111,6 +112,10 @@ class SparqlExpressionPimpl { uint64_t inputSizeEstimate, const std::optional& primarySortKeyVariable); + std::optional, Variable>> + getPrefilterExpressionForMetadata() const; + SparqlExpression* getPimpl() { return _pimpl.get(); } [[nodiscard]] const SparqlExpression* getPimpl() const { return _pimpl.get(); diff --git a/src/index/CompressedBlockPrefiltering.cpp b/src/index/CompressedBlockPrefiltering.cpp index 47d503e514..6b7607af09 100644 --- a/src/index/CompressedBlockPrefiltering.cpp +++ b/src/index/CompressedBlockPrefiltering.cpp @@ -287,16 +287,4 @@ std::vector NotExpression::evaluateImpl( return child_->evaluate(input, evaluationColumn); }; -//______________________________________________________________________________ -// Necessary instantiation of template specializations -template class RelationalExpression; -template class RelationalExpression; -template class RelationalExpression; -template class RelationalExpression; -template class RelationalExpression; -template class RelationalExpression; - -template class LogicalExpression; -template class LogicalExpression; - } // namespace prefilterExpressions diff --git a/src/index/CompressedBlockPrefiltering.h b/src/index/CompressedBlockPrefiltering.h index 75960c05ec..70ff7fdfd7 100644 --- a/src/index/CompressedBlockPrefiltering.h +++ b/src/index/CompressedBlockPrefiltering.h @@ -121,6 +121,18 @@ class NotExpression : public PrefilterExpression { size_t evaluationColumn) const override; }; +//______________________________________________________________________________ +// Instanitate templates with specializations (for linking accessibility) +template class RelationalExpression; +template class RelationalExpression; +template class RelationalExpression; +template class RelationalExpression; +template class RelationalExpression; +template class RelationalExpression; + +template class LogicalExpression; +template class LogicalExpression; + //______________________________________________________________________________ // Definition of the RelationalExpression for LT, LE, EQ, NE, GE and GT. using LessThanExpression = prefilterExpressions::RelationalExpression< From 6cf66095085d5bdc9e6291f068657b6e165fc8a7 Mon Sep 17 00:00:00 2001 From: realHannes Date: Tue, 22 Oct 2024 10:52:39 +0200 Subject: [PATCH 39/81] Completed the implementation for constructing PrefilterExpressions from a given SparqlExpression. --- .../NumericBinaryExpressions.cpp | 206 ++++++- .../NumericUnaryExpressions.cpp | 37 +- .../RelationalExpressions.cpp | 33 +- .../sparqlExpressions/RelationalExpressions.h | 6 +- .../sparqlExpressions/SparqlExpression.cpp | 6 +- .../sparqlExpressions/SparqlExpression.h | 15 +- .../SparqlExpressionPimpl.cpp | 7 +- .../sparqlExpressions/SparqlExpressionPimpl.h | 11 +- src/index/CompressedBlockPrefiltering.cpp | 87 ++- src/index/CompressedBlockPrefiltering.h | 39 ++ src/parser/data/Variable.h | 5 + test/CMakeLists.txt | 2 +- test/CompressedBlockPrefilteringTest.cpp | 565 +++++++++++++++++- 13 files changed, 947 insertions(+), 72 deletions(-) diff --git a/src/engine/sparqlExpressions/NumericBinaryExpressions.cpp b/src/engine/sparqlExpressions/NumericBinaryExpressions.cpp index fca4da662d..a201cd0d35 100644 --- a/src/engine/sparqlExpressions/NumericBinaryExpressions.cpp +++ b/src/engine/sparqlExpressions/NumericBinaryExpressions.cpp @@ -33,7 +33,8 @@ inline auto subtract = makeNumericExpression>(); NARY_EXPRESSION(SubtractExpression, 2, FV); -// Or +// OR and AND +// _____________________________________________________________________________ inline auto orLambda = [](TernaryBool a, TernaryBool b) { using enum TernaryBool; if (a == True || b == True) { @@ -45,11 +46,7 @@ inline auto orLambda = [](TernaryBool a, TernaryBool b) { return Id::makeUndefined(); }; -NARY_EXPRESSION(OrExpression, 2, - FV, - SET); - -// And +// _____________________________________________________________________________ inline auto andLambda = [](TernaryBool a, TernaryBool b) { using enum TernaryBool; if (a == True && b == True) { @@ -60,9 +57,198 @@ inline auto andLambda = [](TernaryBool a, TernaryBool b) { } return Id::makeUndefined(); }; -NARY_EXPRESSION(AndExpression, 2, - FV, - SET); + +namespace constructPrefilterExpr { + +// _____________________________________________________________________________ +template +std::optional> mergeChildrenAndLogicImpl( + std::optional>&& leftChildExprs, + std::optional>&& rightChildExprs) { + if (!leftChildExprs.has_value() || !rightChildExprs.has_value()) { + // If we have two children that yield no PrefilterExpressions, then there + // is also no PrefilterExpression over the AND (&&) conjunction available. + if (!leftChildExprs.has_value() && !rightChildExprs.has_value()) { + return std::nullopt; + } + // Given that only one of the children returned a PrefilterExpression + // vector and the pre-filter construction logic of AND (&&), we can just + // return the corresponding (non-empty) vector. + // + // EXAMPLE why this works: + // Assume that left is - {(>=5, ?x), (<=6, ?y)} + // and right is - std::nullopt ({undefinable prefilter}) + // Remark: left represents the expression ?x >= 5 && ?y <= 6 + // + // For this AND conjunction {(>=5, ?x), (<=6, ?y)} && {undef. + // prefilter}, we can at least (partly) pre-define the plausible true + // evaluation range with the left child. This is because the respective + // expressions contained in left child must always yield true + // for making this AND conjunction expression true. + return leftChildExprs.has_value() ? std::move(leftChildExprs.value()) + : std::move(rightChildExprs.value()); + } + // Merge the PrefilterExpression vectors from the left and right child. + // Remark: The vectors contain std::pairs, sorted by their respective + // variable to the PrefilterExpression. + auto& expressionsLeft = leftChildExprs.value(); + auto& expressionsRight = rightChildExprs.value(); + auto itLeft = expressionsLeft.begin(); + auto itRight = expressionsRight.begin(); + std::vector resPairs; + while (itLeft != expressionsLeft.end() && itRight != expressionsRight.end()) { + auto& [exprLeft, varLeft] = *itLeft; + auto& [exprRight, varRight] = *itRight; + // For our pre-filtering logic over index scans, we need exactly one + // PrefilterExpression for each of the variables. Thus if the left and right + // child contain a PrefilterExpression w.r.t. the same variable, combine + // them here over a (new) prefilter AndExpression. + if (varLeft == varRight) { + resPairs.emplace_back(std::make_unique( + std::move(exprLeft), std::move(exprRight)), + varLeft); + ++itLeft; + ++itRight; + } else if (varLeft < varRight) { + resPairs.emplace_back(std::move(*itLeft)); + ++itLeft; + } else { + resPairs.emplace_back(std::move(*itRight)); + ++itRight; + } + } + std::ranges::move(itLeft, expressionsLeft.end(), + std::back_inserter(resPairs)); + std::ranges::move(itRight, expressionsRight.end(), + std::back_inserter(resPairs)); + return resPairs; +} + +//______________________________________________________________________________ +template +std::optional> mergeChildrenOrLogicImpl( + std::optional>&& leftChildExprs, + std::optional>&& rightChildExprs) { + if (!leftChildExprs.has_value() || !rightChildExprs.has_value()) { + // If one of the children yields no PrefilterExpressions, we simply can't + // further define a PrefilterExpression with this OR (||) conjunction. + // + // EXAMPLE / REASON: + // Assume that left is - {(=10, ?x), (=10, ?y)} + // and right is - std::nullopt ({undefinable prefilter}) + // Remark: left represents the expression ?x = 10 && ?y = 10. + // We can't define a PrefilterExpression here because left must not always + // yield true when this OR expression evaluates to true (because right can + // plausibly yield true). + // And because we can't definitely say that one child (and which of the two + // children) must be true, pre-filtering w.r.t. underlying compressed blocks + // is not possible here. + // In short: OR has multiple congifurations that yield true w.r.t. two child + // expressions => we can't define a distinct PrefilterExpression. + return std::nullopt; + } + auto& expressionsLeft = leftChildExprs.value(); + auto& expressionsRight = rightChildExprs.value(); + auto itLeft = expressionsLeft.begin(); + auto itRight = expressionsRight.begin(); + std::vector resPairs; + while (itLeft != expressionsLeft.end() && itRight != expressionsRight.end()) { + auto& [exprLeft, varLeft] = *itLeft; + auto& [exprRight, varRight] = *itRight; + // The logic that is implemented with this loop. + // + // EXAMPLE 1 + // left child: {(>=5, ?y)} + // right child: {(<=3, ?x)} + // We want to pre-filter the blocks for the expression: ?y >= 5 || ?x <= 3 + // No PrefilterExpression will be returned. + // + // EXAMPLE 2 + // left child: {(>=5, ?x)} + // right child: {(=0, ?x)} + // We want to pre-filter the blocks to the expression: ?x >= 5 || ?x = 0 + // The resulting PrefilterExpression is {((>=5 OR =0), ?x)} + // + // EXAMPLE 3 + // left child: {(>=10, ?x), (!=0, ?y)} + // right child: {(<= 0, ?x)} + // We have to construct a PrefilterExpression for (?x >= 10 && ?y != 0) || + // ?x <= 0. If this OR expression yields true, at least ?x >= 10 || ?x <= 0 + // must be staisifed; for this objective we can construct a + // PrefiterExpression. We can't make a distinct predicition w.r.t. ?y != 0 + // => not relevant for the PrefilterExpression. Thus, we return the + // PrefilterExpresion {((>= 10 OR <= 0), ?x)}. + if (varLeft == varRight) { + resPairs.emplace_back(std::make_unique( + std::move(exprLeft), std::move(exprRight)), + varLeft); + ++itLeft; + ++itRight; + } else if (varLeft < varRight) { + ++itLeft; + } else { + ++itRight; + } + } + if (resPairs.empty()) { + return std::nullopt; + } + return resPairs; +} + +template +constexpr auto getCombineLogic(bool isNegated) { + if constexpr (std::is_same_v) { + // This follows the principles of De Morgan's law. + // If this is a child of a NotExpression, we have to swap the combination + // procedure (swap AND(&&) and OR(||) respectively). This equals a partial + // application of De Morgan's law. On the resulting PrefilterExpressions, + // NOT is applied in UnaryNegateExpression(Impl). For more details take a + // look at the implementation in NumericUnaryExpressions.cpp + return !isNegated ? mergeChildrenAndLogicImpl + : mergeChildrenOrLogicImpl; + } else { + static_assert(std::is_same_v); + return !isNegated ? mergeChildrenOrLogicImpl + : mergeChildrenAndLogicImpl; + } +} +} // namespace constructPrefilterExpr + +//______________________________________________________________________________ +template +requires(isOperation && + prefilterExpressions::check_is_logical_v) +class LogicalBinaryExpressionImpl : public NaryExpression { + public: + using NaryExpression::NaryExpression; + + std::optional> + getPrefilterExpressionForMetadata(bool isNegated) const override { + static_assert(this->N == 2); + auto leftChild = + this->getNthChild(0).value()->getPrefilterExpressionForMetadata( + isNegated); + auto rightChild = + this->getNthChild(1).value()->getPrefilterExpressionForMetadata( + isNegated); + return constructPrefilterExpr::getCombineLogic( + isNegated)(std::move(leftChild), std::move(rightChild)); + } +}; + +//______________________________________________________________________________ +using AndExpression = LogicalBinaryExpressionImpl< + prefilterExpressions::AndExpression, + Operation<2, FV, + SET>>; + +using OrExpression = LogicalBinaryExpressionImpl< + prefilterExpressions::OrExpression, + Operation<2, FV, + SET>>; } // namespace detail @@ -92,8 +278,10 @@ SparqlExpression::Ptr makeAndExpression(SparqlExpression::Ptr child1, SparqlExpression::Ptr child2) { return std::make_unique(std::move(child1), std::move(child2)); } + SparqlExpression::Ptr makeOrExpression(SparqlExpression::Ptr child1, SparqlExpression::Ptr child2) { return std::make_unique(std::move(child1), std::move(child2)); } + } // namespace sparqlExpression diff --git a/src/engine/sparqlExpressions/NumericUnaryExpressions.cpp b/src/engine/sparqlExpressions/NumericUnaryExpressions.cpp index fdc464b666..f612cfc954 100644 --- a/src/engine/sparqlExpressions/NumericUnaryExpressions.cpp +++ b/src/engine/sparqlExpressions/NumericUnaryExpressions.cpp @@ -2,11 +2,14 @@ // Chair of Algorithms and Data Structures. // Author: Johannes Kalmbach +#include + #include "engine/sparqlExpressions/NaryExpressionImpl.h" namespace sparqlExpression { namespace detail { +// _____________________________________________________________________________ // Unary negation. inline auto unaryNegate = [](TernaryBool a) { using enum TernaryBool; @@ -20,10 +23,38 @@ inline auto unaryNegate = [](TernaryBool a) { } AD_FAIL(); }; -NARY_EXPRESSION(UnaryNegateExpression, 1, - FV, - SET); +template +requires(isOperation) +class UnaryNegateExpressionImpl : public NaryExpression { + public: + using NaryExpression::NaryExpression; + + std::optional> + getPrefilterExpressionForMetadata( + [[maybe_unused]] bool isNegated) const override { + static_assert(this->N == 1); + auto optExprVarVec = + this->getNthChild(0).value()->getPrefilterExpressionForMetadata( + !isNegated); + if (!optExprVarVec.has_value()) { + return std::nullopt; + } + std::ranges::for_each( + optExprVarVec.value(), [](PrefilterExprVariablePair& exprVarPair) { + exprVarPair.first = + std::make_unique( + std::move(exprVarPair.first)); + }); + return optExprVarVec; + } +}; + +using UnaryNegateExpression = UnaryNegateExpressionImpl< + Operation<1, FV, + SET>>; + +// _____________________________________________________________________________ // Unary Minus. inline auto unaryMinus = makeNumericExpression>(); NARY_EXPRESSION(UnaryMinusExpression, 1, diff --git a/src/engine/sparqlExpressions/RelationalExpressions.cpp b/src/engine/sparqlExpressions/RelationalExpressions.cpp index c60d49909c..e1d056ba7e 100644 --- a/src/engine/sparqlExpressions/RelationalExpressions.cpp +++ b/src/engine/sparqlExpressions/RelationalExpressions.cpp @@ -411,15 +411,16 @@ ExpressionResult InExpression::evaluate( // _____________________________________________________________________________ template -std::optional, Variable>> -RelationalExpression::getPrefilterExpressionForMetadata() const { +std::optional> +RelationalExpression::getPrefilterExpressionForMetadata( + [[maybe_unused]] bool isNegated) const { AD_CORRECTNESS_CHECK(children_.size() == 2); const SparqlExpression::Ptr& child0 = children_.at(0); const SparqlExpression::Ptr& child1 = children_.at(1); - auto checkBinarySearchEvaluable = [](const SparqlExpression::Ptr& child0, - const SparqlExpression::Ptr& child1) + const auto checkBinarySearchEvaluable = + [](const SparqlExpression::Ptr& child0, + const SparqlExpression::Ptr& child1) -> std::optional> { if (const auto* literalValueId = dynamic_cast(child0.get())) { @@ -432,29 +433,27 @@ RelationalExpression::getPrefilterExpressionForMetadata() const { return std::nullopt; }; - auto createPairFilterExpressionVariable = - [this](std::optional> optValuePair) - -> std::pair, - Variable> { - const auto& [valueId, variable] = optValuePair.value(); - return std::make_pair( + const auto createPrefilterExprVariablePair = + [this](const std::pair& valuePair) + -> std::vector { + std::vector pairVec; + pairVec.emplace_back( std::make_unique>( - valueId), - variable); + valuePair.first), + valuePair.second); + return pairVec; }; - // Option 1: child0 is a (constant) reference ValueId, while child1 contains a // Variable to the respective column on which we want to filter. const auto& optionalPair1 = checkBinarySearchEvaluable(child0, child1); if (optionalPair1.has_value()) { - return createPairFilterExpressionVariable(optionalPair1); + return createPrefilterExprVariablePair(optionalPair1.value()); } - // Option 2: child1 is a (constant) reference ValueId, while child0 contains a // Variable to the respective column on which we want to filter. const auto& optionalPair2 = checkBinarySearchEvaluable(child1, child0); if (optionalPair2.has_value()) { - return createPairFilterExpressionVariable(optionalPair2); + return createPrefilterExprVariablePair(optionalPair2.value()); } return std::nullopt; } diff --git a/src/engine/sparqlExpressions/RelationalExpressions.h b/src/engine/sparqlExpressions/RelationalExpressions.h index 4eae15531c..6448435c0c 100644 --- a/src/engine/sparqlExpressions/RelationalExpressions.h +++ b/src/engine/sparqlExpressions/RelationalExpressions.h @@ -42,9 +42,9 @@ class RelationalExpression : public SparqlExpression { // corresponding `PrefilterExpression` for the pre-filtering procedure on // `CompressedBlockMetadata`. In addition we return the `Variable` that // corresponds to the sorted column. - std::optional, Variable>> - getPrefilterExpressionForMetadata() const override; + std::optional> + getPrefilterExpressionForMetadata( + [[maybe_unused]] bool isNegated) const override; // These expressions are typically used inside `FILTER` clauses, so we need // proper estimates. diff --git a/src/engine/sparqlExpressions/SparqlExpression.cpp b/src/engine/sparqlExpressions/SparqlExpression.cpp index c62092498f..4482bf7182 100644 --- a/src/engine/sparqlExpressions/SparqlExpression.cpp +++ b/src/engine/sparqlExpressions/SparqlExpression.cpp @@ -109,9 +109,9 @@ Estimates SparqlExpression::getEstimatesForFilterExpression( } // _____________________________________________________________________________ -std::optional, Variable>> -SparqlExpression::getPrefilterExpressionForMetadata() const { +std::optional> +SparqlExpression::getPrefilterExpressionForMetadata( + [[maybe_unused]] bool isNegated) const { return std::nullopt; }; diff --git a/src/engine/sparqlExpressions/SparqlExpression.h b/src/engine/sparqlExpressions/SparqlExpression.h index e82e86275b..a864a809b5 100644 --- a/src/engine/sparqlExpressions/SparqlExpression.h +++ b/src/engine/sparqlExpressions/SparqlExpression.h @@ -96,13 +96,14 @@ class SparqlExpression { [[maybe_unused]] const std::optional& primarySortKeyVariable) const; - // Returns a `PrefilterExpression` with the respective `Variable` for the - // necessarily sorted column, if there are relational expressions (e.g. - // `>=` or `!=`) that are binary evaluable. Those expressions can also be - // linked via logical expressions (`&&`, `||` and `!`). - virtual std::optional, Variable>> - getPrefilterExpressionForMetadata() const; + // Returns a vector with pairs, each containing a `PrefilterExpression` and + // the corresponding `Variable`. The `Variable` indicates which + // column is required to be sorted, and hence is as a consequence also binary + // evaluable regarding the relational (e.g. `>=`) / logical (`&&`, `||` and + // `!`) expressions. + virtual std::optional> + getPrefilterExpressionForMetadata( + [[maybe_unused]] bool isNegated = false) const; // Returns true iff this expression is a simple constant. Default // implementation returns `false`. diff --git a/src/engine/sparqlExpressions/SparqlExpressionPimpl.cpp b/src/engine/sparqlExpressions/SparqlExpressionPimpl.cpp index 92fa18cbab..8a05ec147d 100644 --- a/src/engine/sparqlExpressions/SparqlExpressionPimpl.cpp +++ b/src/engine/sparqlExpressions/SparqlExpressionPimpl.cpp @@ -93,10 +93,9 @@ auto SparqlExpressionPimpl::getEstimatesForFilterExpression( } //_____________________________________________________________________________ -std::optional, Variable>> -SparqlExpressionPimpl::getPrefilterExpressionForMetadata() const { - return _pimpl->getPrefilterExpressionForMetadata(); +std::optional> +SparqlExpressionPimpl::getPrefilterExpressionForMetadata(bool isNegated) const { + return _pimpl->getPrefilterExpressionForMetadata(isNegated); } // _____________________________________________________________________________ diff --git a/src/engine/sparqlExpressions/SparqlExpressionPimpl.h b/src/engine/sparqlExpressions/SparqlExpressionPimpl.h index f32f23203d..80a7766260 100644 --- a/src/engine/sparqlExpressions/SparqlExpressionPimpl.h +++ b/src/engine/sparqlExpressions/SparqlExpressionPimpl.h @@ -19,6 +19,12 @@ namespace sparqlExpression { class SparqlExpression; struct EvaluationContext; +// Improve return type readability. +// Pair containing `PrefilterExpression` pointer and a `Variable`. +using PrefilterExprVariablePair = + std::pair, + Variable>; + // Hide the `SparqlExpression` implementation in a Pimpl class, so that code // using this implementation only has to include the (small and therefore cheap // to include) `SparqlExpressionPimpl.h` @@ -112,9 +118,8 @@ class SparqlExpressionPimpl { uint64_t inputSizeEstimate, const std::optional& primarySortKeyVariable); - std::optional, Variable>> - getPrefilterExpressionForMetadata() const; + std::optional> + getPrefilterExpressionForMetadata(bool isNegated = false) const; SparqlExpression* getPimpl() { return _pimpl.get(); } [[nodiscard]] const SparqlExpression* getPimpl() const { diff --git a/src/index/CompressedBlockPrefiltering.cpp b/src/index/CompressedBlockPrefiltering.cpp index 6b7607af09..4517c379e0 100644 --- a/src/index/CompressedBlockPrefiltering.cpp +++ b/src/index/CompressedBlockPrefiltering.cpp @@ -237,6 +237,28 @@ std::vector RelationalExpression::evaluateImpl( return getSetUnion(relevantBlocks, mixedDatatypeBlocks, evaluationColumn); }; +//______________________________________________________________________________ +template +bool RelationalExpression::operator==( + const PrefilterExpression& other) const { + const RelationalExpression* otherRelational = + dynamic_cast*>(&other); + if (!otherRelational) { + return false; + } + return referenceId_ == otherRelational->referenceId_; +}; + +//______________________________________________________________________________ +template +std::string RelationalExpression::info( + [[maybe_unused]] size_t depth) const { + std::stringstream stream; + stream << "Prefilter RelationalExpression<" << static_cast(Comparison) + << ">\nValueId: " << referenceId_ << std::endl; + return stream.str(); +}; + // SECTION LOGICAL OPERATIONS //______________________________________________________________________________ template @@ -257,14 +279,6 @@ LogicalExpression::logicalComplement() const { } }; -//______________________________________________________________________________ -std::unique_ptr NotExpression::logicalComplement() const { - // Logically we complement (negate) a NOT here => NOT cancels out. - // Therefore, we can simply return the child of the respective NOT - // expression after undoing its previous complementation. - return child_->logicalComplement(); -}; - //______________________________________________________________________________ template std::vector LogicalExpression::evaluateImpl( @@ -281,10 +295,67 @@ std::vector LogicalExpression::evaluateImpl( } }; +//______________________________________________________________________________ +template +bool LogicalExpression::operator==( + const PrefilterExpression& other) const { + const LogicalExpression* otherlogical = + dynamic_cast*>(&other); + if (!otherlogical) { + return false; + } + return *child1_ == *otherlogical->child1_ && + *child2_ == *otherlogical->child2_; +}; + +//______________________________________________________________________________ +template +std::string LogicalExpression::info(size_t depth) const { + std::string child1Info = + depth < maxInfoRecursion ? child1_->info(depth + 1) : "MAX_DEPTH"; + std::string child2Info = + depth < maxInfoRecursion ? child2_->info(depth + 1) : "MAX_DEPTH"; + std::stringstream stream; + stream << "Prefilter LogicalExpression<" << static_cast(Operation) + << ">\n" + << "child1 {" << child1Info << "}" + << "child2 {" << child2Info << "}" << std::endl; + return stream.str(); +}; + +// SECTION NOT-EXPRESSION +//______________________________________________________________________________ +std::unique_ptr NotExpression::logicalComplement() const { + // Logically we complement (negate) a NOT here => NOT cancels out. + // Therefore, we can simply return the child of the respective NOT + // expression after undoing its previous complementation. + return child_->logicalComplement(); +}; + //______________________________________________________________________________ std::vector NotExpression::evaluateImpl( const std::vector& input, size_t evaluationColumn) const { return child_->evaluate(input, evaluationColumn); }; +//______________________________________________________________________________ +bool NotExpression::operator==(const PrefilterExpression& other) const { + const NotExpression* otherNotExpression = + dynamic_cast(&other); + if (!otherNotExpression) { + return false; + } + return *child_ == *otherNotExpression->child_; +} + +//______________________________________________________________________________ +std::string NotExpression::info(size_t depth) const { + std::string childInfo = + depth < maxInfoRecursion ? child_->info(depth + 1) : "MAX_DEPTH"; + std::stringstream stream; + stream << "Prefilter NotExpression:\n" + << "child {" << childInfo << "}" << std::endl; + return stream.str(); +} + } // namespace prefilterExpressions diff --git a/src/index/CompressedBlockPrefiltering.h b/src/index/CompressedBlockPrefiltering.h index 70ff7fdfd7..6b3b7f5dc9 100644 --- a/src/index/CompressedBlockPrefiltering.h +++ b/src/index/CompressedBlockPrefiltering.h @@ -13,6 +13,12 @@ namespace prefilterExpressions { +//______________________________________________________________________________ +// The maximum recursion depth for `info()` / `operator<<()`. A depth of `3` +// should be sufficient for most `PrefilterExpressions` with our use case. +constexpr size_t maxInfoRecursion = 3; + +//______________________________________________________________________________ // The compressed block metadata (see `CompressedRelation.h`) that we use to // filter out the non-relevant blocks by checking their content of // `firstTriple_` and `lastTriple_` (`PermutedTriple`) @@ -37,6 +43,11 @@ class PrefilterExpression { public: virtual ~PrefilterExpression() = default; + virtual bool operator==(const PrefilterExpression& other) const = 0; + + // Format content for debugging. + virtual std::string info(size_t depth) const = 0; + virtual std::unique_ptr logicalComplement() const = 0; // The respective metadata to the blocks is expected to be provided in @@ -44,6 +55,13 @@ class PrefilterExpression { std::vector evaluate(const std::vector& input, size_t evaluationColumn) const; + // Format for debugging + friend std::ostream& operator<<(std::ostream& str, + const PrefilterExpression& expression) { + str << expression.info(0) << "." << std::endl; + return str; + } + private: virtual std::vector evaluateImpl( const std::vector& input, @@ -70,6 +88,8 @@ class RelationalExpression : public PrefilterExpression { : referenceId_(referenceId) {} std::unique_ptr logicalComplement() const override; + bool operator==(const PrefilterExpression& other) const override; + std::string info([[maybe_unused]] size_t depth) const override; private: std::vector evaluateImpl( @@ -97,6 +117,8 @@ class LogicalExpression : public PrefilterExpression { : child1_(std::move(child1)), child2_(std::move(child2)) {} std::unique_ptr logicalComplement() const override; + bool operator==(const PrefilterExpression& other) const override; + std::string info(size_t depth) const override; private: std::vector evaluateImpl( @@ -114,6 +136,8 @@ class NotExpression : public PrefilterExpression { : child_(child->logicalComplement()) {} std::unique_ptr logicalComplement() const override; + bool operator==(const PrefilterExpression& other) const override; + std::string info(size_t depth) const override; private: std::vector evaluateImpl( @@ -155,4 +179,19 @@ using AndExpression = prefilterExpressions::LogicalExpression< using OrExpression = prefilterExpressions::LogicalExpression< prefilterExpressions::LogicalOperators::OR>; +//______________________________________________________________________________ +// Helpers to check for the respective type of `PrefilterExpression` +template +struct check_is : std::false_type {}; + +template +struct check_is> : std::true_type {}; +template +struct check_is> : std::true_type {}; + +template +constexpr bool check_is_logical_v = check_is::value; +template +constexpr bool check_is_relational_v = check_is::value; + } // namespace prefilterExpressions diff --git a/src/parser/data/Variable.h b/src/parser/data/Variable.h index 32a1ee99a9..ffa39b5e12 100644 --- a/src/parser/data/Variable.h +++ b/src/parser/data/Variable.h @@ -56,6 +56,11 @@ class Variable { bool operator==(const Variable&) const = default; + // The construction of PrefilterExpressions requires a defined < order. + bool operator<(const Variable& other) const { + return this->_name < other._name; + }; + // Make the type hashable for absl, see // https://abseil.io/docs/cpp/guides/hash. template diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 9c13fc826d..e4e3ed73d1 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -289,7 +289,7 @@ addLinkAndDiscoverTest(AlgorithmTest) addLinkAndDiscoverTestSerial(CompressedRelationsTest index) -addLinkAndDiscoverTestSerial(CompressedBlockPrefilteringTest index) +addLinkAndDiscoverTestSerial(CompressedBlockPrefilteringTest sparqlExpressions index engine) addLinkAndDiscoverTest(ExceptionTest) diff --git a/test/CompressedBlockPrefilteringTest.cpp b/test/CompressedBlockPrefilteringTest.cpp index 8caab1c0ff..023bf5e752 100644 --- a/test/CompressedBlockPrefilteringTest.cpp +++ b/test/CompressedBlockPrefilteringTest.cpp @@ -7,10 +7,14 @@ #include #include "./SparqlExpressionTestHelpers.h" +#include "./util/GTestHelpers.h" +#include "engine/sparqlExpressions/NaryExpression.h" +#include "engine/sparqlExpressions/RelationalExpressions.h" #include "index/CompressedBlockPrefiltering.h" #include "util/DateYearDuration.h" #include "util/IdTestHelpers.h" +using ad_utility::source_location; using ad_utility::testing::BlankNodeId; using ad_utility::testing::BoolId; using ad_utility::testing::DateId; @@ -18,9 +22,10 @@ using ad_utility::testing::DoubleId; using ad_utility::testing::IntId; using ad_utility::testing::UndefId; using ad_utility::testing::VocabId; -using sparqlExpression::TestContext; using namespace prefilterExpressions; +// TEST SECTION 1 (PrefilterExpression) +//______________________________________________________________________________ //______________________________________________________________________________ struct MetadataBlocks { /* @@ -142,19 +147,6 @@ struct MetadataBlocks { //______________________________________________________________________________ // Test PrefilterExpressions -template -struct check_is : std::false_type {}; - -template -struct check_is> : std::true_type {}; -template -struct check_is> : std::true_type {}; - -template -constexpr bool check_is_logical_v = check_is::value; -template -constexpr bool check_is_relational_v = check_is::value; - //______________________________________________________________________________ static const auto testThrowError = [](std::unique_ptr expression, size_t evaluationColumn, @@ -885,3 +877,548 @@ TEST(PrefilterExpression, testInputConditionCheck) { 2, DoubleId(-4.24), IntId(5), blocks.blocksInvalidCol2, "The columns up to the evaluation column must contain the same values."); } + +// TEST SECTION 2 (getPrefilterExpressionForMetadata) +//______________________________________________________________________________ +//______________________________________________________________________________ +using namespace sparqlExpression; +using optPrefilterVec = std::optional>; +using Literal = ad_utility::triple_component::Literal; +using Iri = ad_utility::triple_component::Iri; +using MakeBinarySprqlExprFunc = std::function; +using RelValues = std::variant; + +// TEST HELPER SECTION +//______________________________________________________________________________ +// make `Literal` +const auto L = [](const std::string& content) -> Literal { + return Literal::fromStringRepresentation(content); +}; + +//______________________________________________________________________________ +// make `Iri` +const auto I = [](const std::string& content) -> Iri { + return Iri::fromIriref(content); +}; + +//______________________________________________________________________________ +const auto makeLiteralSparqlExpr = + [](const auto& child) -> std::unique_ptr { + using T = std::decay_t; + if constexpr (std::is_same_v) { + return std::make_unique(child); + } else if constexpr (std::is_same_v) { + return std::make_unique(child); + } else if constexpr (std::is_same_v) { + return std::make_unique(child); + } else if constexpr (std::is_same_v) { + return std::make_unique(child); + } else { + FAIL() << "Unexpected Type."; + } +}; + +//______________________________________________________________________________ +template +std::unique_ptr makeRelationalSparqlExprImpl( + const RelValues& child0, const RelValues& child1) { + auto getExpr = + [](const auto& variantVal) -> std::unique_ptr { + return makeLiteralSparqlExpr(variantVal); + }; + return std::make_unique(std::array{ + std::visit(getExpr, child0), std::visit(getExpr, child1)}); +}; + +//______________________________________________________________________________ +template +std::unique_ptr makeLogicalBinaryPrefilterExprImpl( + std::unique_ptr child1, + std::unique_ptr child2) { + return std::make_unique(std::move(child1), std::move(child2)); +}; + +//______________________________________________________________________________ +std::unique_ptr makeNotPrefilterExprImpl( + std::unique_ptr child) { + return std::make_unique( + std::move(child)); +}; + +//______________________________________________________________________________ +constexpr auto lessThanSparqlExpr = + &makeRelationalSparqlExprImpl; +constexpr auto lessThanFilterExpr = + &makeRelExpr; +constexpr auto lessEqSparqlExpr = + &makeRelationalSparqlExprImpl; +constexpr auto lessEqFilterExpr = + &makeRelExpr; +constexpr auto eqSparqlExpr = + &makeRelationalSparqlExprImpl; +constexpr auto eqFilterExpr = + &makeRelExpr; +constexpr auto notEqSparqlExpr = + &makeRelationalSparqlExprImpl; +constexpr auto notEqFilterExpr = + &makeRelExpr; +constexpr auto greaterEqSparqlExpr = + &makeRelationalSparqlExprImpl; +constexpr auto greaterEqFilterExpr = + &makeRelExpr; +constexpr auto greaterThanSparqlExpr = + &makeRelationalSparqlExprImpl; +constexpr auto greaterThanFilterExpr = + &makeRelExpr; +constexpr auto andSparqlExpr = &makeAndExpression; +constexpr auto orSparqlExpr = &makeOrExpression; +constexpr auto notSparqlExpr = &makeUnaryNegateExpression; +constexpr auto andFilterExpr = + &makeLogicalBinaryPrefilterExprImpl; +constexpr auto orFilterExpr = + &makeLogicalBinaryPrefilterExprImpl; +constexpr auto notFilterExpr = &makeNotPrefilterExprImpl; + +//______________________________________________________________________________ +const auto checkVectorPrefilterExprVariablePair = + [](const std::vector& result, + const std::vector& expected) -> bool { + if (result.size() != expected.size()) { + ADD_FAILURE() << "Expected vectors (result vs. expected) of equal length"; + return false; + }; + const auto isEqualImpl = [](const PrefilterExprVariablePair& resPair, + const PrefilterExprVariablePair& expPair) { + if (*resPair.first != *expPair.first || resPair.second != expPair.second) { + std::stringstream stream; + stream << "The following value pairs don't match:" + << "\nRESULT: " << *resPair.first << "EXPECTED: " << *expPair.first + << "RESULT: VARIABLE" << resPair.second.name() + << "\nEXPECTED: VARIABLE" << expPair.second.name() << std::endl; + ADD_FAILURE() << stream.str(); + return false; + } + return true; + }; + return std::equal(result.begin(), result.end(), expected.begin(), + isEqualImpl); +}; + +//______________________________________________________________________________ +const auto checkEqualityPrefilterMethodT = + [](const optPrefilterVec& result, const optPrefilterVec& expected) { + const auto testEquality = + [](const optPrefilterVec& result, const optPrefilterVec& expected) { + if (!result.has_value() && !expected.has_value()) { + return true; + } else if (result.has_value() && expected.has_value()) { + return checkVectorPrefilterExprVariablePair(result.value(), + expected.value()); + } else { + ADD_FAILURE() + << "Expected both values to either contain a value or to be " + "std::optional."; + return false; + } + }; + ASSERT_TRUE(testEquality(result, expected)); + }; + +//______________________________________________________________________________ +const auto evalToEmptyCheck = [](std::unique_ptr sparqlExpr) { + std::vector prefilterVarPair; + checkEqualityPrefilterMethodT(sparqlExpr->getPrefilterExpressionForMetadata(), + std::nullopt); +}; + +//______________________________________________________________________________ +const auto evalAndEqualityCheck = + [](std::unique_ptr sparqlExpr, + std::convertible_to auto&&... prefilterArgs) { + std::vector prefilterVarPair; + (prefilterVarPair.emplace_back( + std::forward(prefilterArgs)), + ...); + checkEqualityPrefilterMethodT( + sparqlExpr->getPrefilterExpressionForMetadata(), + std::move(prefilterVarPair)); + }; + +//______________________________________________________________________________ +// Construct a pair with the given `PrefilterExpression` and `Variable` value. +auto pr = [](std::unique_ptr expr, + const Variable& var) -> PrefilterExprVariablePair { + return {std::move(expr), var}; +}; + +//______________________________________________________________________________ +// TEST SECTION +//______________________________________________________________________________ +// Test coverage for the default implementation of +// getPrefilterExpressionForMetadata. +TEST(SparqlExpression, testGetPrefilterExpressionDefault) { + evalToEmptyCheck(makeUnaryMinusExpression(makeLiteralSparqlExpr(IntId(0)))); + evalToEmptyCheck(makeMultiplyExpression(makeLiteralSparqlExpr(DoubleId(11)), + makeLiteralSparqlExpr(DoubleId(3)))); + evalToEmptyCheck( + makeStrEndsExpression(makeLiteralSparqlExpr(L("\"Freiburg\"")), + makeLiteralSparqlExpr(L("\"burg\"")))); + evalToEmptyCheck(makeIsIriExpression(makeLiteralSparqlExpr(I("")))); + evalToEmptyCheck(makeLogExpression(makeLiteralSparqlExpr(DoubleId(8)))); + evalToEmptyCheck( + makeStrIriDtExpression(makeLiteralSparqlExpr(L("\"test\"")), + makeLiteralSparqlExpr(I("")))); +} + +//______________________________________________________________________________ +// Check that the (Sparql) RelationalExpression returns the expected +// PrefilterExpression. +TEST(SparqlExpression, getPrefilterExpressionFromSparqlRelational) { + const Variable var = Variable{"?x"}; + MetadataBlocks blocks{}; + evalAndEqualityCheck(eqSparqlExpr(var, BoolId(true)), + pr(eqFilterExpr(BoolId(true)), var)); + evalAndEqualityCheck(greaterEqSparqlExpr(var, IntId(1)), + pr(greaterEqFilterExpr(IntId(1)), var)); + evalAndEqualityCheck(notEqSparqlExpr(VocabId(10), var), + pr(notEqFilterExpr(VocabId(10)), var)); + evalAndEqualityCheck(greaterEqSparqlExpr(BlankNodeId(1), var), + pr(greaterEqFilterExpr(BlankNodeId(1)), var)); + evalAndEqualityCheck(lessEqSparqlExpr(var, blocks.referenceDate1), + pr(lessEqFilterExpr(blocks.referenceDate1), var)); + evalAndEqualityCheck(lessThanSparqlExpr(DoubleId(10.2), var), + pr(lessThanFilterExpr(DoubleId(10.2)), var)); +} + +//______________________________________________________________________________ +// More complex relational SparqlExpressions for which +// getPrefilterExpressionForMetadata should yield a vector containing the actual +// corresponding PrefilterExpression values. +TEST(SparqlExpression, getPrefilterExpressionsToComplexSparqlExpressions) { + const Variable varX = Variable{"?x"}; + const Variable varY = Variable{"?y"}; + const Variable varZ = Variable{"?z"}; + // ?x >= 10 AND ?x != 20 + // expected prefilter pairs: + // {<((>= 10) AND (!= 20)), ?x>} + evalAndEqualityCheck(andSparqlExpr(greaterEqSparqlExpr(varX, IntId(10)), + notEqSparqlExpr(varX, IntId(20))), + pr(andFilterExpr(greaterEqFilterExpr(IntId(10)), + notEqFilterExpr(IntId(20))), + varX)); + // ?x == VocabId(10) AND ?y >= VocabId(10) + // expected prefilter pairs: + // {<(== VocabId(10)), ?x>, <(>= VocabId(10)), ?y>} + evalAndEqualityCheck(andSparqlExpr(eqSparqlExpr(varX, VocabId(10)), + greaterEqSparqlExpr(varY, VocabId(10))), + pr(eqFilterExpr(VocabId(10)), varX), + pr(greaterEqFilterExpr(VocabId(10)), varY)); + // !(?x >= 10 OR ?x <= 0) + // expected prefilter pairs: + // {= 10 OR ?x <= 0), ?x>} + evalAndEqualityCheck( + notSparqlExpr(orSparqlExpr(greaterEqSparqlExpr(varX, IntId(10)), + lessEqSparqlExpr(varX, IntId(0)))), + pr(notFilterExpr(orFilterExpr(greaterEqFilterExpr(IntId(10)), + lessEqFilterExpr(IntId(0)))), + varX)); + // !(?z == VocabId(10) AND ?z >= VocabId(20)) + // expected prefilter pairs: + // {= VocabId(20)) , ?z>} + evalAndEqualityCheck( + notSparqlExpr(andSparqlExpr(eqSparqlExpr(varZ, VocabId(10)), + greaterEqSparqlExpr(varZ, VocabId(20)))), + pr(notFilterExpr(andFilterExpr(eqFilterExpr(VocabId(10)), + greaterEqFilterExpr(VocabId(20)))), + varZ)); + // (?x == VocabId(10) AND ?z == VocabId(0)) AND ?y != DoubleId(22.1) + // expected prefilter pairs: + // {<(==VocabId(10)) , ?x>, <(!=DoubleId(22.1)), ?y>, <(==VocabId(0)), ?z>} + evalAndEqualityCheck( + andSparqlExpr(andSparqlExpr(eqSparqlExpr(VocabId(10), varX), + eqSparqlExpr(varZ, VocabId(0))), + notEqSparqlExpr(DoubleId(22.1), varY)), + pr(eqFilterExpr(VocabId(10)), varX), + pr(notEqFilterExpr(DoubleId(22.1)), varY), + pr(eqFilterExpr(VocabId(0)), varZ)); + // (?z >= 1000 AND ?x == VocabId(10)) OR ?z >= 10000 + // expected prefilter pairs: + // {<((>=1000) OR (>= 10000)), ?z>} + evalAndEqualityCheck( + orSparqlExpr(andSparqlExpr(greaterEqSparqlExpr(varZ, IntId(1000)), + eqSparqlExpr(varX, VocabId(10))), + greaterEqSparqlExpr(varZ, IntId(10000))), + pr(orFilterExpr(greaterEqFilterExpr(IntId(1000)), + greaterEqFilterExpr(IntId(10000))), + varZ)); + // !((?z <= VocabId(10) OR ?y <= VocabId(10)) OR ?x <= VocabId(10)) + // expected prefilter pairs: + // {, , } + evalAndEqualityCheck(notSparqlExpr(orSparqlExpr( + orSparqlExpr(lessEqSparqlExpr(varZ, VocabId(10)), + lessEqSparqlExpr(varY, VocabId(10))), + lessEqSparqlExpr(varX, VocabId(10)))), + pr(notFilterExpr(lessEqFilterExpr(VocabId(10))), varX), + pr(notFilterExpr(lessEqFilterExpr(VocabId(10))), varY), + pr(notFilterExpr(lessEqFilterExpr(VocabId(10))), varZ)); + // ?x >= 10 AND ?y >= 10 + // expected prefilter pairs: + // {<(>= 10), ?x>, <(>= 10), ?y>} + evalAndEqualityCheck(andSparqlExpr(greaterEqSparqlExpr(varX, IntId(10)), + greaterEqSparqlExpr(varY, IntId(10))), + pr(greaterEqFilterExpr(IntId(10)), varX), + pr(greaterEqFilterExpr(IntId(10)), varY)); + // ?x <= 0 AND ?y <= 0 + // expected prefilter pairs: + // {<(<= 0), ?x>, <(<= 0), ?y>} + evalAndEqualityCheck(andSparqlExpr(lessEqSparqlExpr(varX, IntId(0)), + lessEqSparqlExpr(varY, IntId(0))), + pr(lessEqFilterExpr(IntId(0)), varX), + pr(lessEqFilterExpr(IntId(0)), varY)); + // (?x >= 10 AND ?y >= 10) OR (?x <= 0 AND ?y <= 0) + // expected prefilter pairs: + // {<((>= 10) OR (<= 0)), ?x> <(>= 10) OR (<= 0)), ?y>} + evalAndEqualityCheck( + orSparqlExpr(andSparqlExpr(greaterEqSparqlExpr(varX, IntId(10)), + greaterEqSparqlExpr(varY, IntId(10))), + andSparqlExpr(lessEqSparqlExpr(varX, IntId(0)), + lessEqSparqlExpr(varY, IntId(0)))), + pr(orFilterExpr(greaterEqFilterExpr(IntId(10)), + lessEqFilterExpr(IntId(0))), + varX), + pr(orFilterExpr(greaterEqFilterExpr(IntId(10)), + lessEqFilterExpr(IntId(0))), + varY)); + // !(?x >= 10 OR ?y >= 10) OR !(?x <= 0 OR ?y <= 0) + // expected prefilter pairs: + // {<((!(>= 10) OR !(<= 0))), ?x> <(!(>= 10) OR !(<= 0))), ?y>} + evalAndEqualityCheck( + orSparqlExpr( + notSparqlExpr(orSparqlExpr(greaterEqSparqlExpr(varX, IntId(10)), + greaterEqSparqlExpr(varY, IntId(10)))), + notSparqlExpr(orSparqlExpr(lessEqSparqlExpr(varX, IntId(0)), + lessEqSparqlExpr(varY, IntId(0))))), + pr(orFilterExpr(notFilterExpr(greaterEqFilterExpr(IntId(10))), + notFilterExpr(lessEqFilterExpr(IntId(0)))), + varX), + pr(orFilterExpr(notFilterExpr(greaterEqFilterExpr(IntId(10))), + notFilterExpr(lessEqFilterExpr(IntId(0)))), + varY)); + // !(?x == VocabId(10) OR ?x == VocabId(20)) AND !(?z >= 10.00 OR ?y == false) + // expected prefilter pairs: + // {, , + // = 10), ?z>} + evalAndEqualityCheck( + andSparqlExpr( + notSparqlExpr(orSparqlExpr(eqSparqlExpr(varX, VocabId(10)), + eqSparqlExpr(varX, VocabId(20)))), + notSparqlExpr(orSparqlExpr(greaterEqSparqlExpr(varZ, DoubleId(10)), + eqSparqlExpr(varY, BoolId(false))))), + pr(notFilterExpr(orFilterExpr(eqFilterExpr(VocabId(10)), + eqFilterExpr(VocabId(20)))), + varX), + pr(notFilterExpr(eqFilterExpr(BoolId(false))), varY), + pr(notFilterExpr(greaterEqFilterExpr(DoubleId(10))), varZ)); + // !(!(?x >= 10 AND ?y >= 10)) OR !(!(?x <= 0 AND ?y <= 0)) + // expected prefilter pairs: + // {<(!!(>= 10) OR !!(<= 0)), ?x>, <(!!(>= 10) OR !!(<= 0)) ,?y>} + evalAndEqualityCheck( + orSparqlExpr(notSparqlExpr(notSparqlExpr( + andSparqlExpr(greaterEqSparqlExpr(varX, IntId(10)), + greaterEqSparqlExpr(varY, IntId(10))))), + notSparqlExpr(notSparqlExpr( + andSparqlExpr(lessEqSparqlExpr(varX, IntId(0)), + lessEqSparqlExpr(varY, IntId(0)))))), + pr(orFilterExpr( + notFilterExpr(notFilterExpr(greaterEqFilterExpr(IntId(10)))), + notFilterExpr(notFilterExpr(lessEqFilterExpr(IntId(0))))), + varX), + pr(orFilterExpr( + notFilterExpr(notFilterExpr(greaterEqFilterExpr(IntId(10)))), + notFilterExpr(notFilterExpr(lessEqFilterExpr(IntId(0))))), + varY)); + // !((?x >= VocabId(0) AND ?x <= VocabId(10)) OR !(?x != VocabId(99))) + // expected prefilter pairs: + // {= VocabId(0)) AND (<= VocabId(10))) OR !(!= VocabId(99))) , ?x>} + evalAndEqualityCheck(notSparqlExpr(orSparqlExpr( + andSparqlExpr(greaterEqSparqlExpr(varX, VocabId(0)), + lessEqSparqlExpr(varX, VocabId(10))), + notSparqlExpr(notEqSparqlExpr(varX, VocabId(99))))), + pr(notFilterExpr(orFilterExpr( + andFilterExpr(greaterEqFilterExpr(VocabId(0)), + lessEqFilterExpr(VocabId(10))), + notFilterExpr(notEqFilterExpr(VocabId(99))))), + varX)); + // !((?y >= 10 AND ?y <= 100) OR !(?x >= VocabId(99))) + // expected prefilter pairs: + // {= VocabId(0)) AND (<= VocabId(10)), ?y>, = VocabId(99))), ?x>} + evalAndEqualityCheck( + notSparqlExpr( + orSparqlExpr(andSparqlExpr(greaterEqSparqlExpr(varY, VocabId(0)), + lessEqSparqlExpr(varY, VocabId(10))), + notSparqlExpr(greaterEqSparqlExpr(varX, VocabId(99))))), + pr(notFilterExpr(notFilterExpr(greaterEqFilterExpr(VocabId(99)))), varX), + pr(notFilterExpr(andFilterExpr(greaterEqFilterExpr(VocabId(0)), + lessEqFilterExpr(VocabId(10)))), + varY)); + // ?z >= 10 AND ?z <= 100 AND ?x >= 10 AND ?x != 50 AND !(?y <= 10) AND + // !(?city <= VocabId(1000) OR ?city == VocabId(1005)) + // expected prefilter pairs: + // {, <((>= 10) AND (!= + // 50)), ?x>, , <((>= 10) AND (<= 100)), ?z>} + evalAndEqualityCheck( + andSparqlExpr( + andSparqlExpr( + andSparqlExpr(greaterEqSparqlExpr(varZ, IntId(10)), + lessEqSparqlExpr(varZ, IntId(100))), + andSparqlExpr(andSparqlExpr(greaterEqSparqlExpr(varX, IntId(10)), + notEqSparqlExpr(varX, IntId(50))), + notSparqlExpr(lessEqSparqlExpr(varY, IntId(10))))), + notSparqlExpr( + orSparqlExpr(lessEqSparqlExpr(Variable{"?city"}, VocabId(1000)), + eqSparqlExpr(Variable{"?city"}, VocabId(1005))))), + pr(notFilterExpr(orFilterExpr(lessEqFilterExpr(VocabId(1000)), + eqFilterExpr(VocabId(1005)))), + Variable{"?city"}), + pr(andFilterExpr(greaterEqFilterExpr(IntId(10)), + notEqFilterExpr(IntId(50))), + varX), + pr(notFilterExpr(lessEqFilterExpr(IntId(10))), varY), + pr(andFilterExpr(greaterEqFilterExpr(IntId(10)), + lessEqFilterExpr(IntId(100))), + varZ)); + // ?x >= 10 OR (?x >= -10 AND ?x < 0.00) + // expected prefilter pairs: + // {<((>= 10) OR ((>= -10) AND (< 0.00))), ?x>} + evalAndEqualityCheck( + orSparqlExpr(greaterEqSparqlExpr(varX, IntId(10)), + andSparqlExpr(greaterEqSparqlExpr(varX, IntId(-10)), + lessThanSparqlExpr(DoubleId(0.00), varX))), + pr(orFilterExpr(greaterEqFilterExpr(IntId(10)), + andFilterExpr(greaterEqFilterExpr(IntId(-10)), + lessThanFilterExpr(DoubleId(0.00)))), + varX)); + // !(!(?x >= 10) OR !!(?x >= -10 AND ?x < 0.00)) + // expected prefilter pairs: + // {= 10) OR !!((>= -10) AND (< 0.00))), ?x>} + evalAndEqualityCheck(notSparqlExpr(orSparqlExpr( + notSparqlExpr(greaterEqSparqlExpr(varX, IntId(10))), + notSparqlExpr(notSparqlExpr(andSparqlExpr( + greaterEqSparqlExpr(varX, IntId(-10)), + lessThanSparqlExpr(DoubleId(0.00), varX)))))), + pr(notFilterExpr(orFilterExpr( + notFilterExpr(greaterEqFilterExpr(IntId(10))), + notFilterExpr(notFilterExpr(andFilterExpr( + greaterEqFilterExpr(IntId(-10)), + lessThanFilterExpr(DoubleId(0.00))))))), + varX)); +} + +//______________________________________________________________________________ +// For this test we expect that no PrefilterExpression is available. +TEST(SparqlExpression, getEmptyPrefilterFromSparqlRelational) { + const Variable var = Variable{"?x"}; + const Iri iri = I(""); + const Literal lit = L("\"lit\""); + evalToEmptyCheck(lessEqSparqlExpr(var, var)); + evalToEmptyCheck(notEqSparqlExpr(iri, var)); + evalToEmptyCheck(eqSparqlExpr(var, iri)); + evalToEmptyCheck(notEqSparqlExpr(IntId(10), DoubleId(23.3))); + evalToEmptyCheck(greaterThanSparqlExpr(DoubleId(10), lit)); + evalToEmptyCheck(lessThanSparqlExpr(VocabId(10), BoolId(10))); + evalToEmptyCheck(greaterEqSparqlExpr(lit, lit)); + evalToEmptyCheck(eqSparqlExpr(iri, iri)); +} + +//______________________________________________________________________________ +// For the following more complex SparqlExpression trees, we also expect an +// empty PrefilterExpression vector. +TEST(SparqlExpression, getEmptyPrefilterForMoreComplexSparqlExpressions) { + const Variable varX = Variable{"?x"}; + const Variable varY = Variable{"?y"}; + const Variable varZ = Variable{"?z"}; + // ?x <= 10.00 OR ?y > 10 + evalToEmptyCheck(orSparqlExpr(lessEqSparqlExpr(DoubleId(10), varX), + greaterThanSparqlExpr(IntId(10), varY))); + // ?x >= VocabId(23) OR ?z == VocabId(1) + evalToEmptyCheck(orSparqlExpr(greaterEqSparqlExpr(varX, VocabId(23)), + eqSparqlExpr(varZ, VocabId(1)))); + // (?x < VocabId(10) OR ?z <= VocabId(4)) OR ?z != 5.00 + evalToEmptyCheck( + orSparqlExpr(orSparqlExpr(lessThanSparqlExpr(varX, VocabId(10)), + lessEqSparqlExpr(VocabId(4), varZ)), + notEqSparqlExpr(varZ, DoubleId(5)))); + // !(?z > 10.20 AND ?x < 0.001) + // is equal to + // ?z <= 10.20 OR ?x >= 0.001 + evalToEmptyCheck( + notSparqlExpr(andSparqlExpr(greaterThanSparqlExpr(DoubleId(10.2), varZ), + lessThanSparqlExpr(DoubleId(0.001), varX)))); + // !(?x > 10.20 AND ?z != VocabId(22)) + // is equal to + // ?x <= 10.20 OR ?z == VocabId(22) + evalToEmptyCheck( + notSparqlExpr(andSparqlExpr(greaterThanSparqlExpr(DoubleId(10.2), varX), + notEqSparqlExpr(VocabId(22), varZ)))); + // !(!((?x < VocabId(10) OR ?x <= VocabId(4)) OR ?z != 5.00)) + // is equal to + // (?x < VocabId(10) OR ?x <= VocabId(4)) OR ?z != 5.00 + evalToEmptyCheck(notSparqlExpr(notSparqlExpr( + orSparqlExpr(orSparqlExpr(lessThanSparqlExpr(varX, VocabId(10)), + lessEqSparqlExpr(VocabId(4), varX)), + notEqSparqlExpr(varZ, DoubleId(5)))))); + // !(?x != 10 AND !(?y >= 10.00 OR ?z <= 10)) + // is equal to + // ?x == 10 OR ?y >= 10.00 OR ?z <= 10 + evalToEmptyCheck(notSparqlExpr(andSparqlExpr( + notEqSparqlExpr(varX, IntId(10)), + notSparqlExpr(orSparqlExpr(greaterEqSparqlExpr(varY, DoubleId(10.00)), + lessEqSparqlExpr(varZ, IntId(10))))))); + // !((?x != 10 AND ?z != 10) AND (?y == 10 AND ?x >= 20)) + // is equal to + //?x == 10 OR ?z == 10 OR ?y != 10 OR ?x < 20 + evalToEmptyCheck(notSparqlExpr( + andSparqlExpr(andSparqlExpr(notEqSparqlExpr(varX, IntId(10)), + notEqSparqlExpr(varZ, IntId(10))), + andSparqlExpr(eqSparqlExpr(varY, IntId(10)), + greaterEqSparqlExpr(varX, IntId(20)))))); + // !(?z >= 40 AND (?z != 10.00 AND ?y != VocabId(1))) + // is equal to + // ?z <= 40 OR ?z == 10.00 OR ?y == VocabId(1) + evalToEmptyCheck(notSparqlExpr( + andSparqlExpr(greaterEqSparqlExpr(varZ, IntId(40)), + andSparqlExpr(notEqSparqlExpr(varZ, DoubleId(10.00)), + notEqSparqlExpr(varY, VocabId(1)))))); + // ?z <= true OR !(?x == 10 AND ?y == 10) + // is equal to + // ?z <= true OR ?x != 10 OR ?y != 10 + evalToEmptyCheck(orSparqlExpr( + lessEqSparqlExpr(varZ, BoolId(true)), + notSparqlExpr(andSparqlExpr(eqSparqlExpr(varX, IntId(10)), + eqSparqlExpr(IntId(10), varY))))); + // !(!(?z <= true OR !(?x == 10 AND ?y == 10))) + // is equal to + // ?z <= true OR ?x != 10 OR ?y != 10 + evalToEmptyCheck(notSparqlExpr(notSparqlExpr(orSparqlExpr( + lessEqSparqlExpr(varZ, BoolId(true)), + notSparqlExpr(andSparqlExpr(eqSparqlExpr(varX, IntId(10)), + eqSparqlExpr(IntId(10), varY))))))); + // !(!(?x != 10 OR !(?y >= 10.00 AND ?z <= 10))) + // is equal to + // ?x != 10 OR ?y < 10.00 OR ?z > 10 + evalToEmptyCheck(notSparqlExpr(notSparqlExpr(orSparqlExpr( + notEqSparqlExpr(varX, IntId(10)), + notSparqlExpr(andSparqlExpr(greaterEqSparqlExpr(varY, DoubleId(10.00)), + lessEqSparqlExpr(varZ, IntId(10)))))))); + // !(!(?x == VocabId(10) OR ?y >= 25) AND !(!(?z == true AND ?country == + // VocabId(20)))) + // is equal to + // ?x == VocabId(10) OR ?y >= 25 OR ?z == true AND ?country == VocabId(20) + evalToEmptyCheck(notSparqlExpr(andSparqlExpr( + notSparqlExpr(orSparqlExpr(eqSparqlExpr(varX, VocabId(10)), + greaterEqSparqlExpr(varY, IntId(25)))), + notSparqlExpr(notSparqlExpr( + andSparqlExpr(eqSparqlExpr(varZ, BoolId(true)), + eqSparqlExpr(Variable{"?country"}, VocabId(20)))))))); +} From e4be1fe168eb1bc2a2f6b0d2d8d7c1c73cc0e323 Mon Sep 17 00:00:00 2001 From: realHannes Date: Tue, 22 Oct 2024 17:17:27 +0200 Subject: [PATCH 40/81] all builds should compile now --- .../sparqlExpressions/NumericBinaryExpressions.cpp | 2 +- .../sparqlExpressions/NumericUnaryExpressions.cpp | 2 +- .../sparqlExpressions/RelationalExpressions.cpp | 2 +- test/CompressedBlockPrefilteringTest.cpp | 14 +++++++++++++- 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/engine/sparqlExpressions/NumericBinaryExpressions.cpp b/src/engine/sparqlExpressions/NumericBinaryExpressions.cpp index 21df9e0f6d..04e3f536f4 100644 --- a/src/engine/sparqlExpressions/NumericBinaryExpressions.cpp +++ b/src/engine/sparqlExpressions/NumericBinaryExpressions.cpp @@ -236,7 +236,7 @@ class LogicalBinaryExpressionImpl : public NaryExpression { std::optional> getPrefilterExpressionForMetadata(bool isNegated) const override { - static_assert(this->N == 2); + AD_CORRECTNESS_CHECK(this->N == 2); auto leftChild = this->getNthChild(0).value()->getPrefilterExpressionForMetadata( isNegated); diff --git a/src/engine/sparqlExpressions/NumericUnaryExpressions.cpp b/src/engine/sparqlExpressions/NumericUnaryExpressions.cpp index f612cfc954..19a369ffd2 100644 --- a/src/engine/sparqlExpressions/NumericUnaryExpressions.cpp +++ b/src/engine/sparqlExpressions/NumericUnaryExpressions.cpp @@ -33,7 +33,7 @@ class UnaryNegateExpressionImpl : public NaryExpression { std::optional> getPrefilterExpressionForMetadata( [[maybe_unused]] bool isNegated) const override { - static_assert(this->N == 1); + AD_CORRECTNESS_CHECK(this->N == 1); auto optExprVarVec = this->getNthChild(0).value()->getPrefilterExpressionForMetadata( !isNegated); diff --git a/src/engine/sparqlExpressions/RelationalExpressions.cpp b/src/engine/sparqlExpressions/RelationalExpressions.cpp index e1d056ba7e..66180b41e5 100644 --- a/src/engine/sparqlExpressions/RelationalExpressions.cpp +++ b/src/engine/sparqlExpressions/RelationalExpressions.cpp @@ -434,7 +434,7 @@ RelationalExpression::getPrefilterExpressionForMetadata( }; const auto createPrefilterExprVariablePair = - [this](const std::pair& valuePair) + [](const std::pair& valuePair) -> std::vector { std::vector pairVec; pairVec.emplace_back( diff --git a/test/CompressedBlockPrefilteringTest.cpp b/test/CompressedBlockPrefilteringTest.cpp index 023bf5e752..f33398fa65 100644 --- a/test/CompressedBlockPrefilteringTest.cpp +++ b/test/CompressedBlockPrefilteringTest.cpp @@ -915,7 +915,8 @@ const auto makeLiteralSparqlExpr = } else if constexpr (std::is_same_v) { return std::make_unique(child); } else { - FAIL() << "Unexpected Type."; + throw std::runtime_error( + "Can't create a LiteralExpression from provided (input) type."); } }; @@ -1107,6 +1108,17 @@ TEST(SparqlExpression, getPrefilterExpressionsToComplexSparqlExpressions) { pr(andFilterExpr(greaterEqFilterExpr(IntId(10)), notEqFilterExpr(IntId(20))), varX)); + // ?z > VocabId(0) AND ?y > 0 AND ?x < 30.00 + // expected prefilter pairs + // {<(< 30.00), ?x>, <(> 0), ?y>, <(> VocabId(0)), ?z>} + evalAndEqualityCheck( + andSparqlExpr(andSparqlExpr(greaterThanSparqlExpr(varZ, VocabId(0)), + greaterThanSparqlExpr(varY, IntId(0))), + lessThanSparqlExpr(varX, DoubleId(30.00))), + pr(lessThanFilterExpr(DoubleId(30.00)), varX), + pr(greaterThanFilterExpr(IntId(0)), varY), + pr(greaterThanFilterExpr(VocabId(0)), varZ)); + // ?x == VocabId(10) AND ?y >= VocabId(10) // expected prefilter pairs: // {<(== VocabId(10)), ?x>, <(>= VocabId(10)), ?y>} From 74756fe679544b1e1b35c14fc6095cd4e2c832b9 Mon Sep 17 00:00:00 2001 From: realHannes Date: Tue, 22 Oct 2024 22:35:42 +0200 Subject: [PATCH 41/81] fix build issue and increase test coverage --- .../NumericBinaryExpressions.cpp | 4 +- test/CompressedBlockPrefilteringTest.cpp | 87 +++++++++++++++++-- 2 files changed, 83 insertions(+), 8 deletions(-) diff --git a/src/engine/sparqlExpressions/NumericBinaryExpressions.cpp b/src/engine/sparqlExpressions/NumericBinaryExpressions.cpp index 04e3f536f4..4ae1abc273 100644 --- a/src/engine/sparqlExpressions/NumericBinaryExpressions.cpp +++ b/src/engine/sparqlExpressions/NumericBinaryExpressions.cpp @@ -152,7 +152,7 @@ std::optional> mergeChildrenOrLogicImpl( // And because we can't definitely say that one child (and which of the two // children) must be true, pre-filtering w.r.t. underlying compressed blocks // is not possible here. - // In short: OR has multiple congifurations that yield true w.r.t. two child + // In short: OR has multiple configurations that yield true w.r.t. two child // expressions => we can't define a distinct PrefilterExpression. return std::nullopt; } @@ -184,7 +184,7 @@ std::optional> mergeChildrenOrLogicImpl( // We have to construct a PrefilterExpression for (?x >= 10 && ?y != 0) || // ?x <= 0. If this OR expression yields true, at least ?x >= 10 || ?x <= 0 // must be staisifed; for this objective we can construct a - // PrefiterExpression. We can't make a distinct predicition w.r.t. ?y != 0 + // PrefiterExpression. We can't make a distinct prediction w.r.t. ?y != 0 // => not relevant for the PrefilterExpression. Thus, we return the // PrefilterExpresion {((>= 10 OR <= 0), ?x)}. if (varLeft == varRight) { diff --git a/test/CompressedBlockPrefilteringTest.cpp b/test/CompressedBlockPrefilteringTest.cpp index f33398fa65..eff46e0902 100644 --- a/test/CompressedBlockPrefilteringTest.cpp +++ b/test/CompressedBlockPrefilteringTest.cpp @@ -24,7 +24,7 @@ using ad_utility::testing::UndefId; using ad_utility::testing::VocabId; using namespace prefilterExpressions; -// TEST SECTION 1 (PrefilterExpression) +// TEST SECTION 1 //______________________________________________________________________________ //______________________________________________________________________________ struct MetadataBlocks { @@ -58,7 +58,8 @@ struct MetadataBlocks { {VocabId10, DoubleId33, firstId}, // firstTriple {VocabId10, DoubleId33, lastId}, // lastTriple {}, - false}; + false, + 0}; }; const BlockMetadata b1 = makeBlock(IntId(0), IntId(0)); @@ -91,7 +92,8 @@ struct MetadataBlocks { {VocabId10, DoubleId33, IntId(0)}, {VocabId10, DoubleId(22), IntId(0)}, {}, - false}; + false, + 0}; std::vector blocksInvalidCol1 = {b1_1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17}; @@ -100,7 +102,8 @@ struct MetadataBlocks { {VocabId(11), DoubleId33, IntId(-10)}, {VocabId10, DoubleId33, IntId(-8)}, {}, - false}; + false, + 0}; std::vector blocksInvalidCol2 = {b1, b2, b3, b4, b5_1, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17}; @@ -878,7 +881,7 @@ TEST(PrefilterExpression, testInputConditionCheck) { "The columns up to the evaluation column must contain the same values."); } -// TEST SECTION 2 (getPrefilterExpressionForMetadata) +// TEST SECTION 2 //______________________________________________________________________________ //______________________________________________________________________________ using namespace sparqlExpression; @@ -1054,7 +1057,64 @@ auto pr = [](std::unique_ptr expr, }; //______________________________________________________________________________ -// TEST SECTION +// TEST SECTION 2 + +//______________________________________________________________________________ +// Test PrefilterExpression equality operator. +TEST(PrefilterExpression, testEqualityOperator) { + MetadataBlocks blocks{}; + // Relational PrefilterExpressions + ASSERT_FALSE(*greaterEqFilterExpr(blocks.referenceDate1) == + *greaterEqFilterExpr(blocks.referenceDate2)); + ASSERT_FALSE(*notEqFilterExpr(BoolId(true)) == *eqFilterExpr(BoolId(true))); + ASSERT_TRUE(*eqFilterExpr(IntId(1)) == *eqFilterExpr(IntId(1))); + ASSERT_TRUE(*greaterEqFilterExpr(blocks.referenceDate1) == + *greaterEqFilterExpr(blocks.referenceDate1)); + // NotExpression + ASSERT_TRUE(*notFilterExpr(eqFilterExpr(IntId(0))) == + *notFilterExpr(eqFilterExpr(IntId(0)))); + ASSERT_TRUE(*notFilterExpr(notFilterExpr(greaterEqFilterExpr(VocabId(0)))) == + *notFilterExpr(notFilterExpr(greaterEqFilterExpr(VocabId(0))))); + ASSERT_FALSE(*notFilterExpr(greaterThanFilterExpr(IntId(0))) == + *eqFilterExpr(IntId(0))); + ASSERT_FALSE(*notFilterExpr(andFilterExpr(eqFilterExpr(IntId(1)), + eqFilterExpr(IntId(0)))) == + *notFilterExpr(greaterEqFilterExpr(VocabId(0)))); + // Binary PrefilterExpressions (AND and OR) + ASSERT_TRUE( + *orFilterExpr(eqFilterExpr(IntId(0)), lessEqFilterExpr(IntId(0))) == + *orFilterExpr(eqFilterExpr(IntId(0)), lessEqFilterExpr(IntId(0)))); + ASSERT_TRUE( + *andFilterExpr(lessEqFilterExpr(VocabId(1)), + lessEqFilterExpr(IntId(0))) == + *andFilterExpr(lessEqFilterExpr(VocabId(1)), lessEqFilterExpr(IntId(0)))); + ASSERT_FALSE( + *orFilterExpr(eqFilterExpr(IntId(0)), lessEqFilterExpr(IntId(0))) == + *andFilterExpr(lessEqFilterExpr(VocabId(1)), lessEqFilterExpr(IntId(0)))); + ASSERT_FALSE( + *notFilterExpr( + orFilterExpr(eqFilterExpr(IntId(0)), lessEqFilterExpr(IntId(0)))) == + *orFilterExpr(eqFilterExpr(IntId(0)), lessEqFilterExpr(IntId(0)))); +} + +//______________________________________________________________________________ +// Test PrefilterExpression content formatting for debugging. +TEST(PrefilterExpression, checkPrintFormattedPrefilterExpression) { + const auto& relExpr = lessThanFilterExpr(IntId(10)); + EXPECT_EQ((std::stringstream() << *relExpr).str(), + "Prefilter RelationalExpression<0>\nValueId: I:10\n.\n"); + const auto& notExpr = notFilterExpr(eqFilterExpr(VocabId(0))); + EXPECT_EQ((std::stringstream() << *notExpr).str(), + "Prefilter NotExpression:\nchild {Prefilter " + "RelationalExpression<3>\nValueId: V:0\n}\n.\n"); + const auto& orExpr = + orFilterExpr(lessEqFilterExpr(IntId(0)), eqFilterExpr(IntId(5))); + EXPECT_EQ((std::stringstream() << *orExpr).str(), + "Prefilter LogicalExpression<1>\nchild1 {Prefilter " + "RelationalExpression<1>\nValueId: I:0\n}child2 {Prefilter " + "RelationalExpression<2>\nValueId: I:5\n}\n.\n"); +} + //______________________________________________________________________________ // Test coverage for the default implementation of // getPrefilterExpressionForMetadata. @@ -1325,6 +1385,15 @@ TEST(SparqlExpression, getPrefilterExpressionsToComplexSparqlExpressions) { greaterEqFilterExpr(IntId(-10)), lessThanFilterExpr(DoubleId(0.00))))))), varX)); + // ?y != ?x AND ?x >= 10 + // expected prefilter pairs: + // {<(>= 10), ?x>} + evalAndEqualityCheck(andSparqlExpr(notEqSparqlExpr(varY, varX), + greaterEqSparqlExpr(varX, IntId(10))), + pr(greaterEqFilterExpr(IntId(10)), varX)); + evalAndEqualityCheck(andSparqlExpr(greaterEqSparqlExpr(varX, IntId(10)), + notEqSparqlExpr(varY, varX)), + pr(greaterEqFilterExpr(IntId(10)), varX)); } //______________________________________________________________________________ @@ -1341,6 +1410,12 @@ TEST(SparqlExpression, getEmptyPrefilterFromSparqlRelational) { evalToEmptyCheck(lessThanSparqlExpr(VocabId(10), BoolId(10))); evalToEmptyCheck(greaterEqSparqlExpr(lit, lit)); evalToEmptyCheck(eqSparqlExpr(iri, iri)); + evalToEmptyCheck(orSparqlExpr(eqSparqlExpr(var, var), + greaterThanSparqlExpr(var, IntId(0)))); + evalToEmptyCheck( + orSparqlExpr(eqSparqlExpr(var, var), greaterThanSparqlExpr(var, var))); + evalToEmptyCheck( + andSparqlExpr(eqSparqlExpr(var, var), greaterThanSparqlExpr(var, var))); } //______________________________________________________________________________ From 49de4d04d26860f46698f5efb5aa16c83aa5eaf8 Mon Sep 17 00:00:00 2001 From: realHannes Date: Fri, 25 Oct 2024 13:29:40 +0200 Subject: [PATCH 42/81] introduce test structure from PR1 (#1503) --- src/index/CompressedBlockPrefiltering.cpp | 8 +- test/CompressedBlockPrefilteringTest.cpp | 1776 ++++++++------------- 2 files changed, 683 insertions(+), 1101 deletions(-) diff --git a/src/index/CompressedBlockPrefiltering.cpp b/src/index/CompressedBlockPrefiltering.cpp index 4517c379e0..953e52026f 100644 --- a/src/index/CompressedBlockPrefiltering.cpp +++ b/src/index/CompressedBlockPrefiltering.cpp @@ -79,14 +79,12 @@ static auto checkEvalRequirements(const std::vector& input, // Check for equality of IDs up to the evaluation column. if (checkInvalid(std::not_equal_to<>{}, evaluationColumn)) { throwRuntimeError( - "The columns up to the evaluation column must contain the same " - "values."); + "The values in the columns up to the evaluation column must be " + "consistent."); } // Check for order of IDs on evaluation Column. if (checkInvalid(std::greater<>{}, evaluationColumn + 1)) { - throwRuntimeError( - "The data blocks must be provided in sorted order regarding the " - "evaluation column."); + throwRuntimeError("The blocks must be provided in sorted order."); } } }; diff --git a/test/CompressedBlockPrefilteringTest.cpp b/test/CompressedBlockPrefilteringTest.cpp index eff46e0902..c37b6cb2f2 100644 --- a/test/CompressedBlockPrefilteringTest.cpp +++ b/test/CompressedBlockPrefilteringTest.cpp @@ -7,14 +7,14 @@ #include #include "./SparqlExpressionTestHelpers.h" -#include "./util/GTestHelpers.h" #include "engine/sparqlExpressions/NaryExpression.h" #include "engine/sparqlExpressions/RelationalExpressions.h" #include "index/CompressedBlockPrefiltering.h" #include "util/DateYearDuration.h" +#include "util/GTestHelpers.h" #include "util/IdTestHelpers.h" -using ad_utility::source_location; +namespace { using ad_utility::testing::BlankNodeId; using ad_utility::testing::BoolId; using ad_utility::testing::DateId; @@ -22,868 +22,539 @@ using ad_utility::testing::DoubleId; using ad_utility::testing::IntId; using ad_utility::testing::UndefId; using ad_utility::testing::VocabId; +constexpr auto DateParser = &DateYearOrDuration::parseXsdDate; using namespace prefilterExpressions; -// TEST SECTION 1 +namespace makeFilterExpr { //______________________________________________________________________________ -//______________________________________________________________________________ -struct MetadataBlocks { - /* - Our pre-filtering procedure expects blocks that are in correct (ascending) - order w.r.t. their contained ValueIds given the first and last triple. - - The correct order of the ValueIds is dependent on their type and underlying - representation. +// Make RelationalExpression +template +auto relExpr = + [](const ValueId& referenceId) -> std::unique_ptr { + return std::make_unique(referenceId); +}; - Short overview on the ascending order logic for the underlying values: - Order ValueIds for (signed) integer values - [0... max, -max... -1] - Order ValueIds for (signed) doubles values - [0.0... max, -0.0... -max] - Order ValueIds for Vocab and LocalVocab values given the vocabulary with - indices (up to N) - [VocabId(0), .... VocabId(N)] +// Make AndExpression or OrExpression +template +auto logExpr = [](std::unique_ptr child1, + std::unique_ptr child2) + -> std::unique_ptr { + return std::make_unique(std::move(child1), std::move(child2)); +}; - COLUMN 0 and COLUMN 1 contain fixed values, this is a necessary condition - that is also checked during the pre-filtering procedure. The actual evaluation - column (we filter w.r.t. values of COLUMN 2) contains mixed types. - */ +// Make NotExpression +auto notExpr = [](std::unique_ptr child) + -> std::unique_ptr { + return std::make_unique(std::move(child)); +}; - // Values for fixed columns - const Id VocabId10 = VocabId(10); - const Id DoubleId33 = DoubleId(33); +} // namespace makeFilterExpr +//______________________________________________________________________________ +// instantiation relational +// LESS THAN (`<`, `PrefilterExpression`) +constexpr auto lt = makeFilterExpr::relExpr; +// LESS EQUAL (`<=`, `PrefilterExpression`) +constexpr auto le = makeFilterExpr::relExpr; +// GREATER EQUAL (`>=`, `PrefilterExpression`) +constexpr auto ge = makeFilterExpr::relExpr; +// GREATER THAN (`>`, `PrefiterExpression`) +constexpr auto gt = makeFilterExpr::relExpr; +// EQUAL (`==`, `PrefiterExpression`) +constexpr auto eq = makeFilterExpr::relExpr; +// NOT EQUAL (`!=`, `PrefiterExpression`) +constexpr auto neq = makeFilterExpr::relExpr; +// AND (`&&`, `PrefiterExpression`) +constexpr auto andExpr = makeFilterExpr::logExpr; +// OR (`||`, `PrefiterExpression`) +constexpr auto orExpr = makeFilterExpr::logExpr; +// NOT (`!`, `PrefiterExpression`) +constexpr auto notExpr = makeFilterExpr::notExpr; - auto makeBlock(const ValueId& firstId, const ValueId& lastId) - -> BlockMetadata { - return {{}, - 0, - // COLUMN 0 | COLUMN 1 | COLUMN 2 - {VocabId10, DoubleId33, firstId}, // firstTriple - {VocabId10, DoubleId33, lastId}, // lastTriple - {}, - false, - 0}; - }; +//______________________________________________________________________________ +/* +Our pre-filtering procedure expects blocks that are in correct (ascending) +order w.r.t. their contained ValueIds given the first and last triple. - const BlockMetadata b1 = makeBlock(IntId(0), IntId(0)); - const BlockMetadata b2 = makeBlock(IntId(0), IntId(5)); - const BlockMetadata b3 = makeBlock(IntId(5), IntId(6)); - const BlockMetadata b4 = makeBlock(IntId(8), IntId(9)); - const BlockMetadata b5 = makeBlock(IntId(-10), IntId(-8)); - const BlockMetadata b6 = makeBlock(IntId(-4), IntId(-4)); - // b7 contains mixed datatypes (COLUMN 2) - const BlockMetadata b7 = makeBlock(IntId(-4), DoubleId(2)); - const BlockMetadata b8 = makeBlock(DoubleId(2), DoubleId(2)); - const BlockMetadata b9 = makeBlock(DoubleId(4), DoubleId(4)); - const BlockMetadata b10 = makeBlock(DoubleId(4), DoubleId(10)); - const BlockMetadata b11 = makeBlock(DoubleId(-1.23), DoubleId(-6.25)); - const BlockMetadata b12 = makeBlock(DoubleId(-6.25), DoubleId(-6.25)); - const BlockMetadata b13 = makeBlock(DoubleId(-10.42), DoubleId(-12.00)); - // b14 contains mixed datatypes (COLUMN 2) - const BlockMetadata b14 = makeBlock(DoubleId(-14.01), VocabId(0)); - const BlockMetadata b15 = makeBlock(VocabId(10), VocabId(14)); - const BlockMetadata b16 = makeBlock(VocabId(14), VocabId(14)); - const BlockMetadata b17 = makeBlock(VocabId(14), VocabId(17)); - std::vector blocks = {b1, b2, b3, b4, b5, b6, - b7, b8, b9, b10, b11, b12, - b13, b14, b15, b16, b17}; +The correct order of the ValueIds is dependent on their type and underlying +representation. - // The following blocks will be swapped with their respective correct block, - // to test if the method evaluate checks the pre-conditions properly. - const BlockMetadata b1_1{{}, - 0, - {VocabId10, DoubleId33, IntId(0)}, - {VocabId10, DoubleId(22), IntId(0)}, - {}, - false, - 0}; - std::vector blocksInvalidCol1 = {b1_1, b2, b3, b4, b5, b6, - b7, b8, b9, b10, b11, b12, - b13, b14, b15, b16, b17}; - const BlockMetadata b5_1{{}, - 0, - {VocabId(11), DoubleId33, IntId(-10)}, - {VocabId10, DoubleId33, IntId(-8)}, - {}, - false, - 0}; - std::vector blocksInvalidCol2 = {b1, b2, b3, b4, b5_1, b6, - b7, b8, b9, b10, b11, b12, - b13, b14, b15, b16, b17}; - std::vector blocksInvalidOrder1 = { - b2, b1, b3, b4, b5, b6, b7, b8, b9, - b10, b11, b12, b13, b14, b15, b16, b17}; - std::vector blocksInvalidOrder2 = { - b1, b2, b3, b4, b5, b6, b7, b8, b9, - b10, b11, b12, b14, b13, b15, b16, b17}; - std::vector blocksWithDuplicate1 = { - b1, b1, b2, b3, b4, b5, b6, b7, b8, - b9, b10, b11, b12, b13, b14, b15, b16, b17}; - std::vector blocksWithDuplicate2 = { - b1, b2, b3, b4, b5, b6, b7, b8, b9, - b10, b11, b12, b13, b14, b15, b16, b17, b17}; +Short overview on the ascending order logic for the underlying values: +Order ValueIds for (signed) integer values - [0... max, -max... -1] +Order ValueIds for (signed) doubles values - [0.0... max, -0.0... -max] +Order ValueIds for Vocab and LocalVocab values given the vocabulary with +indices (up to N) - [VocabId(0), .... VocabId(N)] - //____________________________________________________________________________ - // Additional blocks for different datatype mix. +COLUMN 1 and COLUMN 2 contain fixed values, this is a necessary condition +that is also checked during the pre-filtering procedure. The actual evaluation +column (we filter w.r.t. values of COLUMN 0) contains mixed types. +*/ +//______________________________________________________________________________ +class TestPrefilterExprOnBlockMetadata : public ::testing::Test { + public: + const Id referenceDate1 = DateId(DateParser, "1999-11-11"); + const Id referenceDate2 = DateId(DateParser, "2005-02-27"); const Id undef = Id::makeUndefined(); const Id falseId = BoolId(false); const Id trueId = BoolId(true); - const Id referenceDate1 = - DateId(DateYearOrDuration::parseXsdDate, "1999-11-11"); - const Id referenceDate2 = - DateId(DateYearOrDuration::parseXsdDate, "2005-02-27"); - const Id referenceDateEqual = - DateId(DateYearOrDuration::parseXsdDate, "2000-01-01"); - const BlockMetadata bd1 = makeBlock(undef, undef); - const BlockMetadata bd2 = makeBlock(undef, falseId); - const BlockMetadata bd3 = makeBlock(falseId, falseId); - const BlockMetadata bd4 = makeBlock(trueId, trueId); - const BlockMetadata bd5 = - makeBlock(trueId, DateId(DateYearOrDuration::parseXsdDate, "1999-12-12")); - const BlockMetadata bd6 = - makeBlock(DateId(DateYearOrDuration::parseXsdDate, "2000-01-01"), - DateId(DateYearOrDuration::parseXsdDate, "2000-01-01")); - const BlockMetadata bd7 = makeBlock( - DateId(DateYearOrDuration::parseXsdDate, "2024-10-08"), BlankNodeId(10)); - std::vector otherBlocks = {bd1, bd2, bd3, bd4, bd5, bd6, bd7}; -}; + const Id referenceDateEqual = DateId(DateParser, "2000-01-01"); -// Static tests, they focus on corner case values for the given block triples. -//______________________________________________________________________________ -//______________________________________________________________________________ -// Test PrefilterExpressions + // Fixed column ValueIds + const Id VocabId10 = VocabId(10); + const Id DoubleId33 = DoubleId(33); + const Id GraphId = VocabId(0); -//______________________________________________________________________________ -static const auto testThrowError = - [](std::unique_ptr expression, size_t evaluationColumn, - const std::vector& input, - const std::string& expectedErrorMessage) { - try { - expression->evaluate(input, evaluationColumn); - FAIL() << "Expected thrown error message."; - } catch (const std::runtime_error& error) { - EXPECT_EQ(std::string(error.what()), expectedErrorMessage); - } catch (...) { - FAIL() << "Expected an error of type std::runtime_error, but a " - "different error was thrown."; - } - }; + // Define BlockMetadata + const BlockMetadata b1 = makeBlock(undef, undef); + const BlockMetadata b2 = makeBlock(undef, falseId); + const BlockMetadata b3 = makeBlock(falseId, falseId); + const BlockMetadata b4 = makeBlock(trueId, IntId(0)); + const BlockMetadata b5 = makeBlock(IntId(0), IntId(0)); + const BlockMetadata b6 = makeBlock(IntId(0), IntId(5)); + const BlockMetadata b7 = makeBlock(IntId(5), IntId(6)); + const BlockMetadata b8 = makeBlock(IntId(8), IntId(9)); + const BlockMetadata b9 = makeBlock(IntId(-10), IntId(-8)); + const BlockMetadata b10 = makeBlock(IntId(-4), IntId(-4)); + const BlockMetadata b11 = makeBlock(IntId(-4), DoubleId(2)); + const BlockMetadata b12 = makeBlock(DoubleId(2), DoubleId(2)); + const BlockMetadata b13 = makeBlock(DoubleId(4), DoubleId(4)); + const BlockMetadata b14 = makeBlock(DoubleId(4), DoubleId(10)); + const BlockMetadata b15 = makeBlock(DoubleId(-1.23), DoubleId(-6.25)); + const BlockMetadata b16 = makeBlock(DoubleId(-6.25), DoubleId(-6.25)); + const BlockMetadata b17 = makeBlock(DoubleId(-10.42), DoubleId(-12.00)); + const BlockMetadata b18 = makeBlock(DoubleId(-14.01), VocabId(0)); + const BlockMetadata b19 = makeBlock(VocabId(10), VocabId(14)); + const BlockMetadata b20 = makeBlock(VocabId(14), VocabId(14)); + const BlockMetadata b21 = makeBlock(VocabId(14), VocabId(17)); + const BlockMetadata b22 = + makeBlock(VocabId(20), DateId(DateParser, "1999-12-12")); + const BlockMetadata b23 = makeBlock(DateId(DateParser, "2000-01-01"), + DateId(DateParser, "2000-01-01")); + const BlockMetadata b24 = + makeBlock(DateId(DateParser, "2024-10-08"), BlankNodeId(10)); -//______________________________________________________________________________ -template -std::unique_ptr makeRelExpr(const ValueId& referenceId) { - return std::make_unique(referenceId); -} + // All blocks that contain mixed (ValueId) types over column 0 + const std::vector mixedBlocks = {b2, b4, b11, b18, b22, b24}; -//______________________________________________________________________________ -template -std::unique_ptr makeLogExpr(const ValueId& referenceId1, - const ValueId& referenceId2) { - return std::make_unique(makeRelExpr(referenceId1), - makeRelExpr(referenceId2)); -} + // Ordered and unique vector with BlockMetadata + const std::vector blocks = { + b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, + b13, b14, b15, b16, b17, b18, b19, b20, b21, b22, b23, b24}; -//______________________________________________________________________________ -template -std::unique_ptr makeNotExpression( - const ValueId& referenceId1, const std::optional optId2) { - if constexpr (check_is_relational_v && - check_is_logical_v) { - assert(optId2.has_value() && "Logical Expressions require two ValueIds"); - return std::make_unique( - makeLogExpr(referenceId1, optId2.value())); - } else if constexpr (std::is_same_v) { - return std::make_unique( - makeNotExpression(referenceId1, optId2)); - } else { - return std::make_unique(makeRelExpr(referenceId1)); - } -} + const std::vector blocksInvalidOrder1 = { + b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, + b13, b14, b15, b16, b17, b18, b19, b20, b21, b22, b24, b23}; -//______________________________________________________________________________ -template -struct TestRelationalExpression { - void operator()(size_t evaluationColumn, const ValueId& referenceId, - const std::vector& input, const ResT& expected) - requires check_is_relational_v { - auto expression = makeRelExpr(referenceId); - if constexpr (std::is_same_v) { - testThrowError(std::move(expression), evaluationColumn, input, expected); - } else { - static_assert(std::is_same_v>); - EXPECT_EQ(expression->evaluate(input, evaluationColumn), expected); - } + const std::vector blocksInvalidOrder2 = { + b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, + b14, b10, b15, b16, b17, b18, b19, b20, b21, b22, b23, b24}; + + const std::vector blocksWithDuplicate1 = { + b1, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, + b13, b14, b15, b16, b17, b18, b19, b20, b21, b22, b23, b24}; + + const std::vector blocksWithDuplicate2 = { + b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, + b14, b15, b16, b17, b18, b19, b20, b21, b22, b23, b24, b24}; + + // Function to create BlockMetadata + const BlockMetadata makeBlock(const ValueId& firstId, const ValueId& lastId) { + assert(firstId <= lastId); + static size_t blockIdx = 0; + ++blockIdx; + return {{{}, + 0, + // COLUMN 0 | COLUMN 1 | COLUMN 2 + {firstId, VocabId10, DoubleId33, GraphId}, // firstTriple + {lastId, VocabId10, DoubleId33, GraphId}, // lastTriple + {}, + false}, + blockIdx}; } -}; -//______________________________________________________________________________ -template -struct TestLogicalExpression { - template - void test(size_t evaluationColumn, const ValueId& referenceIdChild1, - ValueId referenceIdChild2, const std::vector& input, - const ResT& expected) - requires(check_is_logical_v && check_is_relational_v && - check_is_relational_v) { - auto expression = makeLogExpr( - referenceIdChild1, referenceIdChild2); - if constexpr (std::is_same_v) { - testThrowError(std::move(expression), evaluationColumn, input, expected); - } else { - static_assert(std::is_same_v>); - EXPECT_EQ(expression->evaluate(input, evaluationColumn), expected); - } + // Check if expected error is thrown. + auto makeTestErrorCheck(std::unique_ptr expr, + const std::vector& input, + const std::string& expected, + size_t evaluationColumn = 0) { + AD_EXPECT_THROW_WITH_MESSAGE(expr->evaluate(input, evaluationColumn), + ::testing::HasSubstr(expected)); } -}; -//______________________________________________________________________________ -template -struct TestNotExpression { - template - void test(size_t evaluationColumn, const std::vector& input, - const ResT& expected, const ValueId& referenceId1, - std::optional optId2 = std::nullopt) - requires check_is_relational_v { - auto expression = - makeNotExpression(referenceId1, optId2); - if constexpr (std::is_same_v) { - testThrowError(std::move(expression), evaluationColumn, input, expected); - } else { - static_assert(std::is_same_v>); - EXPECT_EQ(expression->evaluate(input, evaluationColumn), expected); - } + // Check that the provided expression prefilters the correct blocks. + auto makeTest(std::unique_ptr expr, + std::vector&& expected) { + std::vector expectedAdjusted; + // This is for convenience, we automatically insert all mixed blocks + // which must be always returned. + std::ranges::set_union( + expected, mixedBlocks, std::back_inserter(expectedAdjusted), + [](const BlockMetadata& b1, const BlockMetadata& b2) { + return b1.blockIndex_ < b2.blockIndex_; + }); + ASSERT_EQ(expr->evaluate(blocks, 0), expectedAdjusted); } }; +} // namespace + //______________________________________________________________________________ -TEST(BlockMetadata, testBlockFormatForDebugging) { - MetadataBlocks blocks{}; +TEST_F(TestPrefilterExprOnBlockMetadata, testBlockFormatForDebugging) { EXPECT_EQ( - "#BlockMetadata\n(first) Triple: V:10 D:33.000000 I:0\n(last) Triple: " - "V:10 D:33.000000 I:0\nnum. rows: 0.\n", - (std::stringstream() << blocks.b1).str()); + "#BlockMetadata\n(first) Triple: I:0 V:10 D:33.000000 V:0\n(last) " + "Triple: I:0 V:10 D:33.000000 V:0\nnum. rows: 0.\n", + (std::stringstream() << b5).str()); EXPECT_EQ( - "#BlockMetadata\n(first) Triple: V:10 D:33.000000 I:-4\n(last) Triple: " - "V:10 D:33.000000 D:2.000000\nnum. rows: 0.\n", - (std::stringstream() << blocks.b7).str()); + "#BlockMetadata\n(first) Triple: I:-4 V:10 D:33.000000 V:0\n(last) " + "Triple: D:2.000000 V:10 D:33.000000 V:0\nnum. rows: 0.\n", + (std::stringstream() << b11).str()); EXPECT_EQ( - "#BlockMetadata\n(first) Triple: V:10 D:33.000000 V:14\n(last) Triple: " - "V:10 D:33.000000 V:17\nnum. rows: 0.\n", - (std::stringstream() << blocks.b17).str()); + "#BlockMetadata\n(first) Triple: V:14 V:10 D:33.000000 V:0\n(last) " + "Triple: V:17 V:10 D:33.000000 V:0\nnum. rows: 0.\n", + (std::stringstream() << b21).str()); } +// Test Relational Expressions //______________________________________________________________________________ // Test LessThanExpression -TEST(RelationalExpression, testLessThanExpressions) { - MetadataBlocks blocks{}; - TestRelationalExpression> - testExpression; - testExpression( - 2, IntId(5), blocks.blocks, - {blocks.b1, blocks.b2, blocks.b5, blocks.b6, blocks.b7, blocks.b8, - blocks.b9, blocks.b10, blocks.b11, blocks.b12, blocks.b13, blocks.b14}); - - testExpression(2, IntId(-12), blocks.blocks, {blocks.b7, blocks.b14}); - testExpression(2, IntId(0), blocks.blocks, - {blocks.b5, blocks.b6, blocks.b7, blocks.b11, blocks.b12, - blocks.b13, blocks.b14}); - testExpression(2, IntId(100), blocks.blocks, - {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b5, - blocks.b6, blocks.b7, blocks.b8, blocks.b9, blocks.b10, - blocks.b11, blocks.b12, blocks.b13, blocks.b14}); - testExpression(2, DoubleId(-3), blocks.blocks, - {blocks.b5, blocks.b6, blocks.b7, blocks.b11, blocks.b12, - blocks.b13, blocks.b14}); - testExpression(2, DoubleId(-14.01), blocks.blocks, {blocks.b7, blocks.b14}); - testExpression(2, DoubleId(-11.22), blocks.blocks, - {blocks.b7, blocks.b13, blocks.b14}); - testExpression( - 2, DoubleId(-4.121), blocks.blocks, - {blocks.b5, blocks.b7, blocks.b11, blocks.b12, blocks.b13, blocks.b14}); - testExpression(2, VocabId(0), blocks.blocks, {blocks.b7, blocks.b14}); - testExpression(2, VocabId(12), blocks.blocks, - {blocks.b7, blocks.b14, blocks.b15}); - testExpression(2, VocabId(14), blocks.blocks, - {blocks.b7, blocks.b14, blocks.b15}); - testExpression(2, VocabId(16), blocks.blocks, - {blocks.b7, blocks.b14, blocks.b15, blocks.b16, blocks.b17}); - // test blocks.otherBlocks - testExpression(2, blocks.undef, blocks.otherBlocks, - {blocks.bd2, blocks.bd5, blocks.bd7}); - testExpression(2, blocks.falseId, blocks.otherBlocks, - {blocks.bd2, blocks.bd5, blocks.bd7}); - testExpression(2, blocks.trueId, blocks.otherBlocks, - {blocks.bd2, blocks.bd3, blocks.bd5, blocks.bd7}); - testExpression(2, blocks.referenceDate1, blocks.otherBlocks, - {blocks.bd2, blocks.bd5, blocks.bd7}); - testExpression(2, blocks.referenceDateEqual, blocks.otherBlocks, - {blocks.bd2, blocks.bd5, blocks.bd7}); - testExpression(2, blocks.referenceDate2, blocks.otherBlocks, - {blocks.bd2, blocks.bd5, blocks.bd6, blocks.bd7}); - testExpression(2, BlankNodeId(11), blocks.otherBlocks, - {blocks.bd2, blocks.bd5, blocks.bd7}); +TEST_F(TestPrefilterExprOnBlockMetadata, testLessThanExpressions) { + makeTest(lt(IntId(5)), + {b5, b6, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18}); + makeTest(lt(IntId(-12)), {b18}); + makeTest(lt(IntId(0)), {b9, b10, b15, b16, b17, b18}); + makeTest(lt(DoubleId(-14.01)), {b18}); + makeTest(lt(DoubleId(-11.22)), {b17, b18}); + makeTest(lt(DoubleId(-4.121)), {b9, b15, b16, b17, b18}); + makeTest(lt(VocabId(0)), {b18}); + makeTest(lt(VocabId(12)), {b18, b19}); + makeTest(lt(VocabId(14)), {b18, b19}); + makeTest(lt(VocabId(16)), {b18, b19, b20, b21}); + makeTest(lt(IntId(100)), + {b5, b6, b7, b8, b9, b10, b12, b13, b14, b15, b16, b17, b18}); + makeTest(lt(undef), {}); + makeTest(lt(falseId), {}); + makeTest(lt(trueId), {b2, b3}); + makeTest(lt(referenceDate1), {}); + makeTest(lt(referenceDateEqual), {b22}); + makeTest(lt(referenceDate2), {b22, b23, b24}); + makeTest(lt(BlankNodeId(11)), {b24}); } //______________________________________________________________________________ // Test LessEqualExpression -TEST(RelationalExpression, testLessEqualExpressions) { - MetadataBlocks blocks{}; - TestRelationalExpression> - testExpression; - testExpression(2, IntId(0), blocks.blocks, - {blocks.b1, blocks.b2, blocks.b5, blocks.b6, blocks.b7, - blocks.b11, blocks.b12, blocks.b13, blocks.b14}); - testExpression( - 2, IntId(-6), blocks.blocks, - {blocks.b5, blocks.b7, blocks.b11, blocks.b12, blocks.b13, blocks.b14}); - testExpression(2, IntId(7), blocks.blocks, - {blocks.b1, blocks.b2, blocks.b3, blocks.b5, blocks.b6, - blocks.b7, blocks.b8, blocks.b9, blocks.b10, blocks.b11, - blocks.b12, blocks.b13, blocks.b14}); - testExpression(2, IntId(-9), blocks.blocks, - {blocks.b5, blocks.b7, blocks.b13, blocks.b14}); - testExpression(2, DoubleId(-9.131), blocks.blocks, - {blocks.b5, blocks.b7, blocks.b13, blocks.b14}); - testExpression(2, DoubleId(1.1415), blocks.blocks, - {blocks.b1, blocks.b2, blocks.b5, blocks.b6, blocks.b7, - blocks.b11, blocks.b12, blocks.b13, blocks.b14}); - testExpression(2, DoubleId(3.1415), blocks.blocks, - {blocks.b1, blocks.b2, blocks.b5, blocks.b6, blocks.b7, - blocks.b8, blocks.b11, blocks.b12, blocks.b13, blocks.b14}); - testExpression(2, DoubleId(-11.99999999999999), blocks.blocks, - {blocks.b7, blocks.b13, blocks.b14}); - testExpression(2, DoubleId(-14.03), blocks.blocks, {blocks.b7, blocks.b14}); - testExpression(2, VocabId(0), blocks.blocks, {blocks.b7, blocks.b14}); - testExpression(2, VocabId(11), blocks.blocks, - {blocks.b7, blocks.b14, blocks.b15}); - testExpression(2, VocabId(14), blocks.blocks, - {blocks.b7, blocks.b14, blocks.b15, blocks.b16, blocks.b17}); - // test block.otherBlocks - testExpression(2, blocks.undef, blocks.otherBlocks, - {blocks.bd2, blocks.bd5, blocks.bd7}); - testExpression(2, blocks.falseId, blocks.otherBlocks, - {blocks.bd2, blocks.bd3, blocks.bd5, blocks.bd7}); - testExpression(2, blocks.trueId, blocks.otherBlocks, - {blocks.bd2, blocks.bd3, blocks.bd4, blocks.bd5, blocks.bd7}); - testExpression(2, blocks.referenceDateEqual, blocks.otherBlocks, - {blocks.bd2, blocks.bd5, blocks.bd6, blocks.bd7}); - testExpression(2, BlankNodeId(11), blocks.otherBlocks, - {blocks.bd2, blocks.bd5, blocks.bd7}); +TEST_F(TestPrefilterExprOnBlockMetadata, testLessEqualExpressions) { + makeTest(le(IntId(0)), {b5, b6, b9, b10, b11, b15, b16, b17, b18}); + makeTest(le(IntId(-6)), {b9, b11, b15, b16, b17, b18}); + makeTest(le(IntId(7)), + {b5, b6, b7, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18}); + makeTest(le(IntId(-9)), {b9, b11, b17, b18}); + makeTest(le(DoubleId(-9.131)), {b9, b11, b17, b18}); + makeTest(le(DoubleId(1.1415)), {b5, b6, b9, b10, b11, b15, b16, b17, b18}); + makeTest(le(DoubleId(3.1415)), + {b5, b6, b9, b10, b11, b12, b15, b16, b17, b18}); + makeTest(le(DoubleId(-11.99999999999999)), {b17, b18}); + makeTest(le(DoubleId(-14.03)), {b18}); + makeTest(le(VocabId(0)), {b18}); + makeTest(le(VocabId(11)), {b18, b19}); + makeTest(le(VocabId(14)), {b18, b19, b20, b21}); + makeTest(le(undef), {}); + makeTest(le(falseId), {b2, b3}); + makeTest(le(trueId), {b2, b3, b4}); + makeTest(le(referenceDateEqual), {b22, b23}); + makeTest(le(BlankNodeId(11)), {b24}); } //______________________________________________________________________________ // Test GreaterThanExpression -TEST(RelationalExpression, testGreaterThanExpression) { - MetadataBlocks blocks{}; - TestRelationalExpression> - testExpression; - testExpression(2, DoubleId(5.5375), blocks.blocks, - {blocks.b3, blocks.b4, blocks.b7, blocks.b10, blocks.b14}); - testExpression(2, DoubleId(9.9994), blocks.blocks, - {blocks.b7, blocks.b10, blocks.b14}); - testExpression( - 2, IntId(-5), blocks.blocks, - {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b6, blocks.b7, - blocks.b8, blocks.b9, blocks.b10, blocks.b11, blocks.b14}); - testExpression( - 2, DoubleId(-5.5375), blocks.blocks, - {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b6, blocks.b7, - blocks.b8, blocks.b9, blocks.b10, blocks.b11, blocks.b14}); - testExpression( - 2, DoubleId(-6.2499999), blocks.blocks, - {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b6, blocks.b7, - blocks.b8, blocks.b9, blocks.b10, blocks.b11, blocks.b14}); - testExpression(2, IntId(1), blocks.blocks, - {blocks.b2, blocks.b3, blocks.b4, blocks.b7, blocks.b8, - blocks.b9, blocks.b10, blocks.b14}); - testExpression(2, IntId(3), blocks.blocks, - {blocks.b2, blocks.b3, blocks.b4, blocks.b7, blocks.b9, - blocks.b10, blocks.b14}); - testExpression( - 2, IntId(4), blocks.blocks, - {blocks.b2, blocks.b3, blocks.b4, blocks.b7, blocks.b10, blocks.b14}); - testExpression(2, IntId(-4), blocks.blocks, - {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b7, - blocks.b8, blocks.b9, blocks.b10, blocks.b11, blocks.b14}); - testExpression(2, IntId(33), blocks.blocks, {blocks.b7, blocks.b14}); - testExpression(2, VocabId(22), blocks.blocks, {blocks.b7, blocks.b14}); - testExpression(2, VocabId(14), blocks.blocks, - {blocks.b7, blocks.b14, blocks.b17}); - testExpression(2, VocabId(12), blocks.blocks, - {blocks.b7, blocks.b14, blocks.b15, blocks.b16, blocks.b17}); - // test blocks.otherBlocks - testExpression(2, blocks.undef, blocks.otherBlocks, - {blocks.bd2, blocks.bd5, blocks.bd7}); - testExpression(2, blocks.falseId, blocks.otherBlocks, - {blocks.bd2, blocks.bd4, blocks.bd5, blocks.bd7}); - testExpression(2, blocks.trueId, blocks.otherBlocks, - {blocks.bd2, blocks.bd5, blocks.bd7}); - testExpression(2, blocks.referenceDateEqual, blocks.otherBlocks, - {blocks.bd2, blocks.bd5, blocks.bd7}); - testExpression(2, blocks.referenceDate1, blocks.otherBlocks, - {blocks.bd2, blocks.bd5, blocks.bd6, blocks.bd7}); - testExpression(2, IntId(5), blocks.otherBlocks, - {blocks.bd2, blocks.bd5, blocks.bd7}); +TEST_F(TestPrefilterExprOnBlockMetadata, testGreaterThanExpression) { + makeTest(gt(DoubleId(5.5375)), {b7, b8, b11, b14, b18}); + makeTest(gt(DoubleId(9.9994)), {b14}); + makeTest(gt(IntId(-5)), {b5, b6, b7, b8, b10, b11, b12, b13, b14, b15}); + makeTest(gt(DoubleId(-5.5375)), + {b5, b6, b7, b8, b10, b11, b12, b13, b14, b15}); + makeTest(gt(DoubleId(-6.2499999)), + {b5, b6, b7, b8, b10, b11, b12, b13, b14, b15}); + makeTest(gt(IntId(1)), {b6, b7, b8, b11, b12, b13, b14}); + makeTest(gt(IntId(3)), {b6, b7, b8, b11, b13, b14}); + makeTest(gt(IntId(4)), {b6, b7, b8, b11, b14}); + makeTest(gt(IntId(-4)), {b5, b6, b7, b8, b11, b12, b13, b14, b15}); + makeTest(gt(IntId(33)), {}); + makeTest(gt(VocabId(22)), {b22}); + makeTest(gt(VocabId(14)), {b21, b22}); + makeTest(gt(VocabId(12)), {b19, b20, b21, b22}); + makeTest(gt(undef), {}); + makeTest(gt(falseId), {b4}); + makeTest(gt(trueId), {}); + makeTest(gt(referenceDateEqual), {b24}); + makeTest(gt(referenceDate1), {b22, b23, b24}); + makeTest(gt(referenceDate2), {b24}); } //______________________________________________________________________________ // Test GreaterEqualExpression -TEST(RelationalExpression, testGreaterEqualExpression) { - MetadataBlocks blocks{}; - TestRelationalExpression> - testExpression; - testExpression(2, IntId(0), blocks.blocks, - {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b7, - blocks.b8, blocks.b9, blocks.b10, blocks.b14}); - testExpression(2, IntId(8), blocks.blocks, - {blocks.b4, blocks.b7, blocks.b10, blocks.b14}); - testExpression(2, DoubleId(9.98), blocks.blocks, - {blocks.b7, blocks.b10, blocks.b14}); - testExpression(2, IntId(-3), blocks.blocks, - {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b7, - blocks.b8, blocks.b9, blocks.b10, blocks.b11, blocks.b14}); - testExpression(2, IntId(-10), blocks.blocks, - {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b5, - blocks.b6, blocks.b7, blocks.b8, blocks.b9, blocks.b10, - blocks.b11, blocks.b12, blocks.b14}); - testExpression(2, DoubleId(-3.1415), blocks.blocks, - {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b7, - blocks.b8, blocks.b9, blocks.b10, blocks.b11, blocks.b14}); - testExpression( - 2, DoubleId(-4.000001), blocks.blocks, - {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b6, blocks.b7, - blocks.b8, blocks.b9, blocks.b10, blocks.b11, blocks.b14}); - testExpression(2, DoubleId(10.000), blocks.blocks, - {blocks.b7, blocks.b10, blocks.b14}); - testExpression(2, DoubleId(-15.22), blocks.blocks, - {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b5, - blocks.b6, blocks.b7, blocks.b8, blocks.b9, blocks.b10, - blocks.b11, blocks.b12, blocks.b13, blocks.b14}); - testExpression(2, DoubleId(7.999999), blocks.blocks, - {blocks.b4, blocks.b7, blocks.b10, blocks.b14}); - testExpression(2, DoubleId(10.0001), blocks.blocks, {blocks.b7, blocks.b14}); - testExpression(2, VocabId(14), blocks.blocks, - {blocks.b7, blocks.b14, blocks.b15, blocks.b16, blocks.b17}); - testExpression(2, VocabId(10), blocks.blocks, - {blocks.b7, blocks.b14, blocks.b15, blocks.b16, blocks.b17}); - testExpression(2, VocabId(17), blocks.blocks, - {blocks.b7, blocks.b14, blocks.b17}); - // test blocks.otherBlocks - testExpression(2, blocks.undef, blocks.otherBlocks, - {blocks.bd2, blocks.bd5, blocks.bd7}); - testExpression(2, blocks.falseId, blocks.otherBlocks, - {blocks.bd2, blocks.bd3, blocks.bd4, blocks.bd5, blocks.bd7}); - testExpression(2, blocks.trueId, blocks.otherBlocks, - {blocks.bd2, blocks.bd4, blocks.bd5, blocks.bd7}); - testExpression(2, blocks.referenceDateEqual, blocks.otherBlocks, - {blocks.bd2, blocks.bd5, blocks.bd6, blocks.bd7}); - testExpression(2, VocabId(0), blocks.otherBlocks, - {blocks.bd2, blocks.bd5, blocks.bd7}); +TEST_F(TestPrefilterExprOnBlockMetadata, testGreaterEqualExpression) { + makeTest(ge(IntId(0)), {b5, b6, b7, b8, b11, b12, b13, b14}); + makeTest(ge(IntId(8)), {b8, b11, b14}); + makeTest(ge(DoubleId(9.98)), {b11, b14}); + makeTest(ge(IntId(-3)), {b5, b6, b7, b8, b11, b12, b13, b14, b15}); + makeTest(ge(IntId(-10)), + {b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16}); + makeTest(ge(DoubleId(-3.1415)), {b5, b6, b7, b8, b11, b12, b13, b14, b15}); + makeTest(ge(DoubleId(-4.000001)), + {b5, b6, b7, b8, b10, b11, b12, b13, b14, b15}); + makeTest(ge(DoubleId(10.000)), {b11, b14}); + makeTest(ge(DoubleId(-15.22)), + {b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18}); + makeTest(ge(DoubleId(7.999999)), {b8, b11, b14}); + makeTest(ge(DoubleId(10.0001)), {}); + makeTest(ge(VocabId(14)), {b18, b19, b20, b21, b22}); + makeTest(ge(VocabId(10)), {b18, b19, b20, b21, b22}); + makeTest(ge(VocabId(17)), {b18, b21, b22}); + makeTest(ge(undef), {}); + makeTest(ge(falseId), {b2, b3, b4}); + makeTest(ge(trueId), {b4}); + makeTest(ge(referenceDateEqual), {b23, b24}); } //______________________________________________________________________________ // Test EqualExpression -TEST(RelationalExpression, testEqualExpression) { - MetadataBlocks blocks{}; - TestRelationalExpression> - testExpression; - testExpression(2, IntId(0), blocks.blocks, - {blocks.b1, blocks.b2, blocks.b7, blocks.b14}); - testExpression(2, IntId(5), blocks.blocks, - {blocks.b2, blocks.b3, blocks.b7, blocks.b10, blocks.b14}); - testExpression(2, IntId(22), blocks.blocks, {blocks.b7, blocks.b14}); - testExpression(2, IntId(-10), blocks.blocks, - {blocks.b5, blocks.b7, blocks.b14}); - testExpression(2, DoubleId(-6.25), blocks.blocks, - {blocks.b7, blocks.b11, blocks.b12, blocks.b14}); - testExpression(2, IntId(-11), blocks.blocks, - {blocks.b7, blocks.b13, blocks.b14}); - testExpression(2, DoubleId(-14.02), blocks.blocks, {blocks.b7, blocks.b14}); - testExpression(2, DoubleId(-0.001), blocks.blocks, {blocks.b7, blocks.b14}); - testExpression(2, DoubleId(0), blocks.blocks, - {blocks.b1, blocks.b2, blocks.b7, blocks.b14}); - testExpression(2, IntId(2), blocks.blocks, - {blocks.b2, blocks.b7, blocks.b8, blocks.b14}); - testExpression(2, DoubleId(5.5), blocks.blocks, - {blocks.b3, blocks.b7, blocks.b10, blocks.b14}); - testExpression(2, DoubleId(1.5), blocks.blocks, - {blocks.b2, blocks.b7, blocks.b14}); - testExpression(2, VocabId(1), blocks.blocks, {blocks.b7, blocks.b14}); - testExpression(2, VocabId(14), blocks.blocks, - {blocks.b7, blocks.b14, blocks.b15, blocks.b16, blocks.b17}); - testExpression(2, VocabId(11), blocks.blocks, - {blocks.b7, blocks.b14, blocks.b15}); - testExpression(2, VocabId(17), blocks.blocks, - {blocks.b7, blocks.b14, blocks.b17}); - testExpression(2, IntId(-4), blocks.blocks, - {blocks.b6, blocks.b7, blocks.b11, blocks.b14}); - // test blocks.otherBlocks - testExpression(2, blocks.trueId, blocks.otherBlocks, - {blocks.bd2, blocks.bd4, blocks.bd5, blocks.bd7}); - testExpression(2, blocks.referenceDate1, blocks.otherBlocks, - {blocks.bd2, blocks.bd5, blocks.bd7}); - testExpression(2, blocks.referenceDateEqual, blocks.otherBlocks, - {blocks.bd2, blocks.bd5, blocks.bd6, blocks.bd7}); - testExpression(2, blocks.referenceDate2, blocks.otherBlocks, - {blocks.bd2, blocks.bd5, blocks.bd7}); +TEST_F(TestPrefilterExprOnBlockMetadata, testEqualExpression) { + makeTest(eq(IntId(0)), {b4, b5, b6, b11}); + makeTest(eq(IntId(5)), {b6, b7, b11, b14}); + makeTest(eq(IntId(22)), {}); + makeTest(eq(IntId(-10)), {b9, b11, b18}); + makeTest(eq(DoubleId(-6.25)), {b15, b16}); + makeTest(eq(IntId(-11)), {b17}); + makeTest(eq(DoubleId(-14.02)), {b18}); + makeTest(eq(DoubleId(-0.001)), {b11}); + makeTest(eq(DoubleId(0)), {b4, b5, b6, b11}); + makeTest(eq(IntId(2)), {b6, b11, b12}); + makeTest(eq(DoubleId(5.5)), {b7, b11, b14}); + makeTest(eq(DoubleId(1.5)), {b6, b11}); + makeTest(eq(VocabId(1)), {b18}); + makeTest(eq(VocabId(14)), {b18, b19, b20, b21}); + makeTest(eq(VocabId(11)), {b18, b19}); + makeTest(eq(VocabId(17)), {b18, b21}); + makeTest(eq(IntId(-4)), {b10, b11, b15}); + makeTest(eq(trueId), {b4}); + makeTest(eq(referenceDate1), {b22}); + makeTest(eq(referenceDateEqual), {b23}); + makeTest(eq(referenceDate2), {}); } //______________________________________________________________________________ // Test NotEqualExpression -TEST(RelationalExpression, testNotEqualExpression) { - MetadataBlocks blocks{}; - TestRelationalExpression> - testExpression; - testExpression(2, DoubleId(0.00), blocks.blocks, - {blocks.b2, blocks.b3, blocks.b4, blocks.b5, blocks.b6, - blocks.b7, blocks.b8, blocks.b9, blocks.b10, blocks.b11, - blocks.b12, blocks.b13, blocks.b14}); - testExpression(2, IntId(-4), blocks.blocks, - {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b5, - blocks.b7, blocks.b8, blocks.b9, blocks.b10, blocks.b11, - blocks.b12, blocks.b13, blocks.b14}); - testExpression(2, DoubleId(0.001), blocks.blocks, - {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b5, - blocks.b6, blocks.b7, blocks.b8, blocks.b9, blocks.b10, - blocks.b11, blocks.b12, blocks.b13, blocks.b14}); - testExpression(2, IntId(2), blocks.blocks, - {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b5, - blocks.b6, blocks.b7, blocks.b9, blocks.b10, blocks.b11, - blocks.b12, blocks.b13, blocks.b14}); - testExpression(2, DoubleId(-6.2500), blocks.blocks, - {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b5, - blocks.b6, blocks.b7, blocks.b8, blocks.b9, blocks.b10, - blocks.b11, blocks.b13, blocks.b14}); - testExpression(2, IntId(5), blocks.blocks, - {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b5, - blocks.b6, blocks.b7, blocks.b8, blocks.b9, blocks.b10, - blocks.b11, blocks.b12, blocks.b13, blocks.b14}); - testExpression(2, DoubleId(-101.23), blocks.blocks, - {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b5, - blocks.b6, blocks.b7, blocks.b8, blocks.b9, blocks.b10, - blocks.b11, blocks.b12, blocks.b13, blocks.b14}); - testExpression(2, VocabId(0), blocks.blocks, - {blocks.b7, blocks.b14, blocks.b15, blocks.b16, blocks.b17}); - testExpression(2, VocabId(7), blocks.blocks, - {blocks.b7, blocks.b14, blocks.b15, blocks.b16, blocks.b17}); - testExpression(2, VocabId(14), blocks.blocks, - {blocks.b7, blocks.b14, blocks.b15, blocks.b17}); - testExpression(2, VocabId(17), blocks.blocks, - {blocks.b7, blocks.b14, blocks.b15, blocks.b16, blocks.b17}); - // test blocks.otherBlocks - testExpression(2, blocks.undef, blocks.otherBlocks, - {blocks.bd2, blocks.bd5, blocks.bd7}); - testExpression(2, blocks.falseId, blocks.otherBlocks, - {blocks.bd2, blocks.bd4, blocks.bd5, blocks.bd7}); - testExpression(2, blocks.referenceDateEqual, blocks.otherBlocks, - {blocks.bd2, blocks.bd5, blocks.bd7}); - testExpression(2, blocks.referenceDate1, blocks.otherBlocks, - {blocks.bd2, blocks.bd5, blocks.bd6, blocks.bd7}); +TEST_F(TestPrefilterExprOnBlockMetadata, testNotEqualExpression) { + makeTest(neq(DoubleId(0.00)), + {b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18}); + makeTest(neq(IntId(-4)), + {b5, b6, b7, b8, b9, b11, b12, b13, b14, b15, b16, b17, b18}); + makeTest(neq(DoubleId(0.001)), + {b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18}); + makeTest(neq(IntId(2)), + {b5, b6, b7, b8, b9, b10, b11, b13, b14, b15, b16, b17, b18}); + makeTest(neq(DoubleId(-6.2500)), + {b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b17, b18}); + makeTest(neq(IntId(5)), + {b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18}); + makeTest(neq(DoubleId(-101.23)), + {b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18}); + makeTest(neq(VocabId(0)), {b19, b20, b21, b22}); + makeTest(neq(VocabId(7)), {b18, b19, b20, b21, b22}); + makeTest(neq(VocabId(14)), {b18, b19, b21, b22}); + makeTest(neq(VocabId(17)), {b18, b19, b20, b21, b22}); + makeTest(neq(undef), {}); + makeTest(neq(falseId), {b4}); + makeTest(neq(referenceDateEqual), {b22, b24}); + makeTest(neq(referenceDate1), {b22, b23, b24}); } -//______________________________________________________________________________ -//______________________________________________________________________________ // Test Logical Expressions - +//______________________________________________________________________________ // Test AndExpression -TEST(LogicalExpression, testAndExpression) { - MetadataBlocks blocks{}; - TestLogicalExpression> - testExpression; - testExpression.test( - 2, VocabId(10), VocabId(10), blocks.blocks, - {blocks.b7, blocks.b14, blocks.b15, blocks.b16, blocks.b17}); - testExpression.test( - 2, VocabId(0), VocabId(17), blocks.blocks, - {blocks.b7, blocks.b14, blocks.b17}); - testExpression.test( - 2, VocabId(12), VocabId(17), blocks.blocks, {blocks.b7, blocks.b14}); - testExpression.test( - 2, VocabId(10), VocabId(14), blocks.blocks, - {blocks.b7, blocks.b14, blocks.b15}); - testExpression.test( - 2, VocabId(0), VocabId(10), blocks.blocks, {blocks.b7, blocks.b14}); - testExpression.test( - 2, VocabId(17), VocabId(17), blocks.blocks, - {blocks.b7, blocks.b14, blocks.b15, blocks.b16, blocks.b17}); - testExpression.test( - 2, DoubleId(-6.25), IntId(-7), blocks.blocks, {blocks.b7, blocks.b14}); - testExpression.test( - 2, DoubleId(-6.25), DoubleId(-6.25), blocks.blocks, - {blocks.b7, blocks.b14}); - testExpression.test( - 2, IntId(0), IntId(0), blocks.blocks, {blocks.b7, blocks.b14}); - testExpression.test( - 2, IntId(-10), DoubleId(0.00), blocks.blocks, - {blocks.b5, blocks.b6, blocks.b7, blocks.b11, blocks.b12, blocks.b14}); - // Also a corner case. - testExpression.test( - 2, IntId(0), DoubleId(0), blocks.blocks, - {blocks.b2, blocks.b7, blocks.b14}); - testExpression.test( - 2, IntId(0), IntId(0), blocks.blocks, - {blocks.b1, blocks.b2, blocks.b7, blocks.b14}); - testExpression.test( - 2, DoubleId(-34.23), DoubleId(15.1), blocks.blocks, - {blocks.b7, blocks.b14}); - testExpression.test( - 2, IntId(0), DoubleId(-4), blocks.blocks, - {blocks.b5, blocks.b6, blocks.b7, blocks.b11, blocks.b12, blocks.b13, - blocks.b14}); - testExpression.test( - 2, IntId(0), IntId(-4), blocks.blocks, - {blocks.b2, blocks.b3, blocks.b4, blocks.b5, blocks.b7, blocks.b8, - blocks.b9, blocks.b10, blocks.b11, blocks.b12, blocks.b13, blocks.b14}); - testExpression.test( - 2, DoubleId(-3.1415), DoubleId(4.5), blocks.blocks, - {blocks.b2, blocks.b7, blocks.b10, blocks.b14}); - testExpression.test( - 2, DoubleId(-6.25), IntId(0), blocks.blocks, - {blocks.b5, blocks.b6, blocks.b7, blocks.b11, blocks.b13, blocks.b14}); - testExpression.test( - 2, DoubleId(-4), DoubleId(1), blocks.blocks, {blocks.b7, blocks.b14}); - testExpression.test( - 2, DoubleId(-2), IntId(-3), blocks.blocks, - {blocks.b7, blocks.b11, blocks.b14}); +TEST_F(TestPrefilterExprOnBlockMetadata, testAndExpression) { + makeTest(andExpr(ge(VocabId(10)), gt(VocabId(10))), {b19, b20, b21, b22}); + makeTest(andExpr(ge(VocabId(10)), ge(VocabId(10))), {b19, b20, b21, b22}); + makeTest(andExpr(ge(VocabId(12)), gt(VocabId(17))), {b22}); + makeTest(andExpr(ge(VocabId(12)), gt(VocabId(17))), {b22}); + makeTest(andExpr(ge(VocabId(10)), lt(VocabId(14))), {b19}); + makeTest(andExpr(le(VocabId(0)), lt(VocabId(10))), {b18}); + makeTest(andExpr(le(VocabId(17)), lt(VocabId(17))), {b18, b19, b20, b21}); + makeTest(andExpr(ge(DoubleId(-6.25)), lt(IntId(-7))), {}); + makeTest(andExpr(gt(DoubleId(-6.25)), lt(DoubleId(-6.25))), {}); + makeTest(andExpr(gt(IntId(0)), lt(IntId(0))), {}); + makeTest(andExpr(gt(IntId(-10)), lt(DoubleId(0))), {b9, b10, b11, b15, b16}); + makeTest(andExpr(gt(IntId(0)), eq(DoubleId(0))), {b6, b11}); + makeTest(andExpr(ge(IntId(0)), eq(IntId(0))), {b5, b6, b11}); + makeTest(andExpr(gt(DoubleId(-34.23)), ge(DoubleId(15.1))), {}); + makeTest(andExpr(lt(IntId(0)), le(DoubleId(-4))), + {b9, b10, b11, b15, b16, b17, b18}); + makeTest(andExpr(neq(IntId(0)), neq(IntId(-4))), + {b6, b7, b8, b9, b11, b12, b13, b14, b15, b16, b17, b18}); + makeTest(andExpr(neq(DoubleId(-3.141)), eq(DoubleId(4.5))), + {b6, b11, b14, b18}); + makeTest(andExpr(neq(DoubleId(-6.25)), lt(IntId(0))), + {b9, b10, b11, b15, b17, b18}); + makeTest(andExpr(le(DoubleId(-4)), ge(DoubleId(1))), {}); + makeTest(andExpr(le(DoubleId(-2)), eq(IntId(-3))), {b11, b15}); + makeTest(andExpr(andExpr(le(IntId(10)), gt(DoubleId(0))), eq(undef)), {}); + makeTest(andExpr(gt(referenceDate1), le(IntId(10))), {}); + makeTest(andExpr(gt(IntId(4)), andExpr(gt(DoubleId(8)), lt(IntId(10)))), + {b8, b14}); + makeTest(andExpr(eq(IntId(0)), andExpr(lt(IntId(-20)), gt(IntId(30)))), {}); + makeTest(andExpr(eq(IntId(0)), andExpr(le(IntId(0)), ge(IntId(0)))), + {b4, b5, b6, b11}); } //______________________________________________________________________________ // Test OrExpression -TEST(LogicalExpression, testOrExpression) { - MetadataBlocks blocks{}; - TestLogicalExpression> - testExpression; - testExpression.test( - 2, VocabId(22), VocabId(0), blocks.blocks, - {blocks.b7, blocks.b14, blocks.b15, blocks.b16, blocks.b17}); - testExpression.test( - 2, VocabId(0), VocabId(16), blocks.blocks, - {blocks.b7, blocks.b14, blocks.b17}); - testExpression.test( - 2, VocabId(17), VocabId(242), blocks.blocks, {blocks.b7, blocks.b14}); - testExpression.test( - 2, DoubleId(-5.95), VocabId(14), blocks.blocks, - {blocks.b5, blocks.b7, blocks.b11, blocks.b12, blocks.b13, blocks.b14, - blocks.b15, blocks.b16, blocks.b17}); - testExpression.test( - 2, DoubleId(0), VocabId(14), blocks.blocks, - {blocks.b1, blocks.b2, blocks.b7, blocks.b14, blocks.b15, blocks.b17}); - testExpression.test( - 2, DoubleId(0.0), DoubleId(-6.25), blocks.blocks, - {blocks.b1, blocks.b2, blocks.b7, blocks.b11, blocks.b12, blocks.b14}); - testExpression.test( - 2, DoubleId(-11.99), DoubleId(-15.22), blocks.blocks, - {blocks.b7, blocks.b13, blocks.b14}); - testExpression.test( - 2, DoubleId(7.99), DoubleId(-7.99), blocks.blocks, - {blocks.b4, blocks.b5, blocks.b7, blocks.b10, blocks.b13, blocks.b14}); - testExpression.test( - 2, IntId(-15), IntId(2), blocks.blocks, - {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b5, blocks.b6, - blocks.b7, blocks.b8, blocks.b9, blocks.b10, blocks.b11, blocks.b12, - blocks.b13, blocks.b14}); - testExpression.test( - 2, IntId(0), IntId(-4), blocks.blocks, - {blocks.b1, blocks.b2, blocks.b6, blocks.b7, blocks.b11, blocks.b14}); - testExpression.test( - 2, VocabId(14), IntId(2), blocks.blocks, - {blocks.b2, blocks.b7, blocks.b8, blocks.b14, blocks.b15, blocks.b17}); - testExpression.test( - 2, DoubleId(-1), IntId(1), blocks.blocks, - {blocks.b2, blocks.b3, blocks.b4, blocks.b5, blocks.b6, blocks.b7, - blocks.b8, blocks.b9, blocks.b10, blocks.b11, blocks.b12, blocks.b13, - blocks.b14}); - testExpression.test( - 2, DoubleId(-4), IntId(-4), blocks.blocks, - {blocks.b5, blocks.b6, blocks.b7, blocks.b11, blocks.b12, blocks.b13, - blocks.b14}); +TEST_F(TestPrefilterExprOnBlockMetadata, testOrExpression) { + makeTest(orExpr(lt(VocabId(22)), le(VocabId(0))), {b18, b19, b20, b21}); + makeTest(orExpr(le(VocabId(0)), ge(VocabId(16))), {b18, b21, b22}); + makeTest(orExpr(gt(VocabId(17)), ge(VocabId(17))), {b21, b22}); + makeTest(orExpr(lt(DoubleId(-5.95)), eq(VocabId(14))), + {b9, b15, b16, b17, b18, b19, b20, b21}); + makeTest(orExpr(eq(DoubleId(0)), neq(VocabId(14))), + {b5, b6, b11, b18, b19, b21}); + makeTest(orExpr(eq(DoubleId(0)), eq(DoubleId(-6.25))), + {b5, b6, b11, b15, b16, b18}); + makeTest(orExpr(gt(undef), le(IntId(-6))), {b9, b15, b16, b17, b18}); + makeTest(orExpr(le(trueId), gt(referenceDate1)), {b2, b3, b4, b22, b23, b24}); + makeTest(orExpr(eq(IntId(0)), orExpr(lt(IntId(-10)), gt(IntId(8)))), + {b5, b6, b8, b11, b14, b17, b18}); + makeTest(orExpr(gt(referenceDate2), eq(trueId)), {b4}); + makeTest(orExpr(eq(VocabId(17)), orExpr(lt(VocabId(0)), gt(VocabId(20)))), + {b21, b22}); + makeTest(orExpr(eq(undef), gt(referenceDateEqual)), {b24}); + makeTest(orExpr(gt(IntId(8)), gt(DoubleId(22.1))), {b8, b14}); + makeTest(orExpr(lt(DoubleId(-8.25)), le(IntId(-10))), {b9, b17, b18}); + makeTest(orExpr(eq(IntId(0)), neq(DoubleId(0.25))), + {b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18}); + makeTest(orExpr(gt(referenceDate1), orExpr(gt(trueId), eq(IntId(0)))), + {b4, b5, b6, b11, b22, b23, b24}); + makeTest(orExpr(gt(DoubleId(-6.25)), lt(DoubleId(-6.25))), + {b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b17, b18}); + makeTest(orExpr(orExpr(eq(IntId(0)), eq(IntId(5))), + orExpr(eq(DoubleId(-6.25)), lt(DoubleId(-12)))), + {b4, b5, b6, b7, b11, b14, b15, b16, b18}); + makeTest(orExpr(le(trueId), gt(falseId)), {b2, b3, b4}); + makeTest(orExpr(eq(VocabId(0)), eq(DoubleId(0.25))), {b6, b11, b18}); } //______________________________________________________________________________ // Test NotExpression -TEST(LogicalExpression, testNotExpression) { - MetadataBlocks blocks{}; - TestNotExpression> testExpression{}; - testExpression.test( - 2, blocks.blocks, - {blocks.b7, blocks.b14, blocks.b15, blocks.b16, blocks.b17}, VocabId(2)); - testExpression.test( - 2, blocks.blocks, {blocks.b7, blocks.b14, blocks.b15, blocks.b17}, - VocabId(14)); - testExpression.test( - 2, blocks.blocks, - {blocks.b7, blocks.b14, blocks.b15, blocks.b16, blocks.b17}, VocabId(14)); - testExpression.test( - 2, blocks.blocks, - {blocks.b7, blocks.b14, blocks.b15, blocks.b16, blocks.b17}, VocabId(0)); - testExpression.test( - 2, blocks.blocks, - {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b5, blocks.b6, - blocks.b7, blocks.b8, blocks.b9, blocks.b10, blocks.b11, blocks.b12, - blocks.b13, blocks.b14}, - DoubleId(-14.01)); - testExpression.test( - 2, blocks.blocks, {blocks.b7, blocks.b14}, DoubleId(-14.01)); - testExpression.test( - 2, blocks.blocks, - {blocks.b5, blocks.b6, blocks.b7, blocks.b11, blocks.b12, blocks.b13, - blocks.b14}, - DoubleId(-4.00)); - testExpression.test( - 2, blocks.blocks, {blocks.b7, blocks.b14}, DoubleId(-24.4)); - testExpression.test( - 2, blocks.blocks, - {blocks.b2, blocks.b3, blocks.b4, blocks.b7, blocks.b8, blocks.b9, - blocks.b10, blocks.b14}, - IntId(0)); - testExpression.test( - 2, blocks.blocks, - {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b5, blocks.b6, - blocks.b7, blocks.b8, blocks.b9, blocks.b10, blocks.b11, blocks.b13, - blocks.b14}, - DoubleId(-6.25)); - testExpression.test( - 2, blocks.blocks, - {blocks.b2, blocks.b7, blocks.b9, blocks.b10, blocks.b14}, DoubleId(4)); - testExpression.test( - 2, blocks.blocks, - {blocks.b1, blocks.b2, blocks.b5, blocks.b6, blocks.b7, blocks.b11, - blocks.b12, blocks.b13, blocks.b14}, - DoubleId(0)); - testExpression.test(0, blocks.blocks, {}, blocks.VocabId10); - testExpression.test(1, blocks.blocks, {}, blocks.DoubleId33); - testExpression.test(0, blocks.blocks, blocks.blocks, - blocks.VocabId10); - testExpression.test(1, blocks.blocks, {}, - blocks.DoubleId33); - testExpression.test( - 2, blocks.blocks, {blocks.b1, blocks.b2, blocks.b7, blocks.b14}, - IntId(0)); - testExpression.test( - 2, blocks.blocks, - {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b5, blocks.b6, - blocks.b7, blocks.b8, blocks.b9, blocks.b10, blocks.b11, blocks.b13, - blocks.b14}, - DoubleId(-6.25)); - testExpression.test( - 2, blocks.blocks, {blocks.b7, blocks.b14}, VocabId(10)); - testExpression.test( - 2, blocks.blocks, - {blocks.b2, blocks.b3, blocks.b4, blocks.b7, blocks.b9, blocks.b10, - blocks.b14}, - DoubleId(3.99)); - testExpression - .test( - 2, blocks.blocks, - {blocks.b2, blocks.b3, blocks.b4, blocks.b5, blocks.b6, blocks.b7, - blocks.b8, blocks.b9, blocks.b10, blocks.b11, blocks.b12, blocks.b13, - blocks.b14}, - IntId(0), IntId(0)); - testExpression.test( - 2, blocks.blocks, {blocks.b5, blocks.b7, blocks.b14}, IntId(-10), - DoubleId(-14.02)); - testExpression - .test( - 2, blocks.blocks, - {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b5, blocks.b6, - blocks.b7, blocks.b8, blocks.b9, blocks.b10, blocks.b11, blocks.b12, - blocks.b13, blocks.b14}, - IntId(10), DoubleId(-6.25)); - testExpression - .test( - 2, blocks.blocks, - {blocks.b5, blocks.b6, blocks.b7, blocks.b11, blocks.b12, blocks.b13, - blocks.b14}, - IntId(-4), DoubleId(-6.25)); - testExpression - .test( - 2, blocks.blocks, - {blocks.b1, blocks.b2, blocks.b3, blocks.b4, blocks.b5, blocks.b6, - blocks.b7, blocks.b8, blocks.b9, blocks.b10, blocks.b11, blocks.b12, - blocks.b13, blocks.b14}, - DoubleId(-7), IntId(6)); - testExpression - .test( - 2, blocks.blocks, - {blocks.b2, blocks.b3, blocks.b7, blocks.b8, blocks.b9, blocks.b10, - blocks.b14}, - IntId(0), DoubleId(6)); - testExpression - .test( - 2, blocks.blocks, {blocks.b5, blocks.b7, blocks.b13, blocks.b14}, - DoubleId(0), IntId(-10)); - testExpression.test( - 2, blocks.blocks, {blocks.b7, blocks.b14, blocks.b15}, VocabId(10), - VocabId(10)); - testExpression.test( - 2, blocks.blocks, {blocks.b6, blocks.b7, blocks.b11, blocks.b14}, - DoubleId(-4), IntId(-4)); - testExpression - .test( - 2, blocks.blocks, {blocks.b7, blocks.b14}, IntId(-42), VocabId(0)); - testExpression - .test( - 2, blocks.blocks, {blocks.b7, blocks.b14, blocks.b15}, VocabId(14), - VocabId(15)); - testExpression.test( - 2, blocks.blocks, {blocks.b7, blocks.b11, blocks.b12, blocks.b14}, - DoubleId(-7.25), DoubleId(-6.25)); +TEST_F(TestPrefilterExprOnBlockMetadata, testNotExpression) { + makeTest(notExpr(eq(VocabId(2))), {b18, b19, b20, b21, b22}); + makeTest(notExpr(eq(VocabId(14))), {b18, b19, b21, b22}); + makeTest(notExpr(neq(VocabId(14))), {b19, b20, b21}); + makeTest(notExpr(gt(VocabId(2))), {b18}); + makeTest(notExpr(lt(DoubleId(-14.01))), + {b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18}); + makeTest(notExpr(ge(DoubleId(-14.01))), {b18}); + makeTest(notExpr(gt(DoubleId(-4.00))), {b9, b10, b11, b15, b16, b17, b18}); + makeTest(notExpr(ge(DoubleId(-24.4))), {b18}); + makeTest(notExpr(gt(referenceDate2)), {b22, b23}); + makeTest(notExpr(le(trueId)), {}); + makeTest(notExpr(le(IntId(0))), {b6, b7, b8, b11, b12, b13, b14}); + makeTest(notExpr(gt(undef)), {}); + makeTest(notExpr(eq(DoubleId(-6.25))), + {b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b17, b18}); + makeTest(notExpr(neq(DoubleId(4))), {b6, b11, b13, b14, b18}); + makeTest(notExpr(gt(DoubleId(0))), + {b4, b5, b6, b9, b10, b11, b15, b16, b17, b18}); + makeTest(notExpr(notExpr(eq(IntId(0)))), {b4, b5, b6, b11}); + makeTest(notExpr(notExpr(neq(DoubleId(-6.25)))), + {b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b17, b18}); + makeTest(notExpr(notExpr(lt(VocabId(10)))), {b18}); + makeTest(notExpr(notExpr(ge(DoubleId(3.99)))), {b6, b7, b8, b11, b13, b14}); + makeTest(notExpr(andExpr(le(IntId(0)), ge(IntId(0)))), + {b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18}); + makeTest(notExpr(andExpr(neq(IntId(-10)), neq(DoubleId(-14.02)))), {b9, b18}); + makeTest( + notExpr(andExpr(gt(IntId(10)), ge(DoubleId(-6.25)))), + {b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18}); + makeTest( + notExpr(andExpr(lt(DoubleId(-7)), ge(IntId(6)))), + {b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18}); + makeTest(notExpr(orExpr(le(IntId(0)), ge(DoubleId(6)))), + {b6, b7, b11, b12, b13, b14}); + makeTest(notExpr(orExpr(ge(DoubleId(0)), gt(IntId(-10)))), + {b9, b11, b17, b18}); + makeTest(notExpr(orExpr(lt(VocabId(10)), gt(VocabId(10)))), {b19}); + makeTest(notExpr(orExpr(lt(DoubleId(-4)), gt(IntId(-4)))), {b10, b11, b15}); + makeTest(notExpr(orExpr(gt(IntId(-42)), ge(VocabId(0)))), {b11}); + makeTest(notExpr(orExpr(ge(VocabId(14)), gt(VocabId(15)))), {b18, b19}); } //______________________________________________________________________________ -// Test that correct errors are thrown for invalid input (condition checking). -TEST(PrefilterExpression, testInputConditionCheck) { - MetadataBlocks blocks{}; - TestRelationalExpression - testRelationalExpression{}; - testRelationalExpression( - 2, DoubleId(10), blocks.blocksInvalidCol1, - "The columns up to the evaluation column must contain the same values."); - testRelationalExpression( - 1, DoubleId(10), blocks.blocksInvalidCol1, - "The data blocks must be provided in sorted order regarding the " - "evaluation column."); - testRelationalExpression(2, DoubleId(10), blocks.blocksWithDuplicate2, - "The provided data blocks must be unique."); +// Test PrefilterExpressions mixed +TEST_F(TestPrefilterExprOnBlockMetadata, testGeneralPrefilterExprCombinations) { + makeTest(andExpr(notExpr(gt(DoubleId(-14.01))), lt(IntId(0))), {b18}); + makeTest( + orExpr(andExpr(gt(DoubleId(8.25)), le(IntId(10))), eq(DoubleId(-6.25))), + {b8, b14, b15, b16}); + makeTest( + orExpr(andExpr(gt(DoubleId(8.25)), le(IntId(10))), lt(DoubleId(-6.25))), + {b8, b9, b14, b17, b18}); + makeTest(andExpr(orExpr(ge(trueId), le(falseId)), eq(referenceDate1)), {}); + makeTest(andExpr(eq(IntId(0)), orExpr(lt(IntId(-11)), le(IntId(-12)))), {}); + makeTest( + andExpr(eq(DoubleId(-4)), orExpr(gt(IntId(-4)), lt(DoubleId(-1.25)))), + {b10, b11, b15}); + makeTest(orExpr(notExpr(andExpr(lt(IntId(10)), gt(IntId(5)))), eq(IntId(0))), + {b4, b5, b6, b7, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18}); + makeTest(andExpr(orExpr(gt(VocabId(16)), le(VocabId(5))), gt(DoubleId(7.25))), + {}); + makeTest(andExpr(lt(falseId), orExpr(lt(IntId(10)), gt(DoubleId(17.25)))), + {}); + makeTest( + orExpr(andExpr(gt(VocabId(16)), ge(VocabId(17))), gt(DoubleId(7.25))), + {b8, b14, b18, b21, b22}); + makeTest(orExpr(eq(trueId), andExpr(gt(referenceDate1), lt(referenceDate2))), + {b4, b22, b23}); +} - TestNotExpression testNotExpression{}; - testNotExpression.test( - 2, blocks.blocksWithDuplicate1, - "The provided data blocks must be unique.", VocabId(2)); - testNotExpression.test( - 2, blocks.blocksInvalidOrder1, - "The data blocks must be provided in sorted order regarding the " - "evaluation column.", - DoubleId(-14.1)); - testNotExpression.test( - 0, blocks.blocksInvalidCol2, - "The data blocks must be provided in sorted order regarding the " - "evaluation column.", - IntId(0)); - testNotExpression.test( - 1, blocks.blocksInvalidCol2, - "The columns up to the evaluation column must contain the same values.", - IntId(0)); - testNotExpression.test( - 2, blocks.blocksInvalidCol2, - "The columns up to the evaluation column must contain the same values.", - IntId(0)); +//______________________________________________________________________________ +// Test that correct errors are thrown for invalid input (condition +TEST_F(TestPrefilterExprOnBlockMetadata, testInputConditionCheck) { + makeTestErrorCheck(le(IntId(5)), blocksWithDuplicate1, + "The provided data blocks must be unique."); + makeTestErrorCheck(andExpr(gt(VocabId(10)), le(VocabId(20))), + blocksWithDuplicate2, + "The provided data blocks must be unique."); + makeTestErrorCheck(gt(DoubleId(2)), blocksInvalidOrder1, + "The blocks must be provided in sorted order."); + makeTestErrorCheck(andExpr(gt(VocabId(10)), le(VocabId(20))), + blocksInvalidOrder2, + "The blocks must be provided in sorted order."); + makeTestErrorCheck( + gt(DoubleId(2)), blocks, + "The values in the columns up to the evaluation column must be " + "consistent.", + 1); + makeTestErrorCheck( + gt(DoubleId(2)), blocks, + "The values in the columns up to the evaluation column must be " + "consistent.", + 2); +} - TestLogicalExpression testAndExpression{}; - testAndExpression.test( - 2, DoubleId(-4.24), IntId(5), blocks.blocksWithDuplicate2, - "The provided data blocks must be unique."); - testAndExpression.test( - 2, DoubleId(-4.24), IntId(5), blocks.blocksInvalidOrder1, - "The data blocks must be provided in sorted order regarding the " - "evaluation column."); - testAndExpression.test( - 2, DoubleId(-4.24), IntId(5), blocks.blocksInvalidCol2, - "The columns up to the evaluation column must contain the same values."); +//______________________________________________________________________________ +// Check for correctness given only one BlockMetadata value is provided. +TEST_F(TestPrefilterExprOnBlockMetadata, testWithOneBlockMetadataValue) { + auto expr = orExpr(eq(DoubleId(-6.25)), eq(IntId(0))); + std::vector input = {b16}; + EXPECT_EQ(expr->evaluate(input, 0), input); + EXPECT_EQ(expr->evaluate(input, 1), std::vector{}); + EXPECT_EQ(expr->evaluate(input, 2), std::vector{}); } +//______________________________________________________________________________ +//______________________________________________________________________________ // TEST SECTION 2 //______________________________________________________________________________ //______________________________________________________________________________ +namespace { + using namespace sparqlExpression; using optPrefilterVec = std::optional>; using Literal = ad_utility::triple_component::Literal; @@ -905,6 +576,12 @@ const auto I = [](const std::string& content) -> Iri { return Iri::fromIriref(content); }; +//______________________________________________________________________________ +struct TestDates { + const Id referenceDate1 = DateId(DateParser, "1999-11-11"); + const Id referenceDate2 = DateId(DateParser, "2005-02-27"); +}; + //______________________________________________________________________________ const auto makeLiteralSparqlExpr = [](const auto& child) -> std::unique_ptr { @@ -944,45 +621,30 @@ std::unique_ptr makeLogicalBinaryPrefilterExprImpl( }; //______________________________________________________________________________ -std::unique_ptr makeNotPrefilterExprImpl( - std::unique_ptr child) { - return std::make_unique( - std::move(child)); -}; - -//______________________________________________________________________________ -constexpr auto lessThanSparqlExpr = +// LESS THAN (`<`, `SparqlExpression`) +constexpr auto ltSprql = &makeRelationalSparqlExprImpl; -constexpr auto lessThanFilterExpr = - &makeRelExpr; -constexpr auto lessEqSparqlExpr = +// LESS EQUAL (`<=`, `SparqlExpression`) +constexpr auto leSprql = &makeRelationalSparqlExprImpl; -constexpr auto lessEqFilterExpr = - &makeRelExpr; -constexpr auto eqSparqlExpr = +// EQUAL (`==`, `SparqlExpression`) +constexpr auto eqSprql = &makeRelationalSparqlExprImpl; -constexpr auto eqFilterExpr = - &makeRelExpr; -constexpr auto notEqSparqlExpr = +// NOT EQUAL (`!=`, `SparqlExpression`) +constexpr auto neqSprql = &makeRelationalSparqlExprImpl; -constexpr auto notEqFilterExpr = - &makeRelExpr; -constexpr auto greaterEqSparqlExpr = +// GREATER EQUAL (`>=`, `SparqlExpression`) +constexpr auto geSprql = &makeRelationalSparqlExprImpl; -constexpr auto greaterEqFilterExpr = - &makeRelExpr; -constexpr auto greaterThanSparqlExpr = +// GREATER THAN (`>`, `SparqlExpression`) +constexpr auto gtSprql = &makeRelationalSparqlExprImpl; -constexpr auto greaterThanFilterExpr = - &makeRelExpr; -constexpr auto andSparqlExpr = &makeAndExpression; -constexpr auto orSparqlExpr = &makeOrExpression; -constexpr auto notSparqlExpr = &makeUnaryNegateExpression; -constexpr auto andFilterExpr = - &makeLogicalBinaryPrefilterExprImpl; -constexpr auto orFilterExpr = - &makeLogicalBinaryPrefilterExprImpl; -constexpr auto notFilterExpr = &makeNotPrefilterExprImpl; +// AND (`&&`, `SparqlExpression`) +constexpr auto andSprqlExpr = &makeAndExpression; +// OR (`||`, `SparqlExpression`) +constexpr auto orSprqlExpr = &makeOrExpression; +// NOT (`!`, `SparqlExpression`) +constexpr auto notSprqlExpr = &makeUnaryNegateExpression; //______________________________________________________________________________ const auto checkVectorPrefilterExprVariablePair = @@ -1050,66 +712,52 @@ const auto evalAndEqualityCheck = }; //______________________________________________________________________________ -// Construct a pair with the given `PrefilterExpression` and `Variable` value. +// Construct a `PAIR` with the given `PrefilterExpression` and `Variable` value. auto pr = [](std::unique_ptr expr, const Variable& var) -> PrefilterExprVariablePair { return {std::move(expr), var}; }; - -//______________________________________________________________________________ -// TEST SECTION 2 +} // namespace //______________________________________________________________________________ // Test PrefilterExpression equality operator. TEST(PrefilterExpression, testEqualityOperator) { - MetadataBlocks blocks{}; + const TestDates dt{}; // Relational PrefilterExpressions - ASSERT_FALSE(*greaterEqFilterExpr(blocks.referenceDate1) == - *greaterEqFilterExpr(blocks.referenceDate2)); - ASSERT_FALSE(*notEqFilterExpr(BoolId(true)) == *eqFilterExpr(BoolId(true))); - ASSERT_TRUE(*eqFilterExpr(IntId(1)) == *eqFilterExpr(IntId(1))); - ASSERT_TRUE(*greaterEqFilterExpr(blocks.referenceDate1) == - *greaterEqFilterExpr(blocks.referenceDate1)); + ASSERT_FALSE(*ge(dt.referenceDate1) == *ge(dt.referenceDate2)); + ASSERT_FALSE(*neq(BoolId(true)) == *eq(BoolId(true))); + ASSERT_TRUE(*eq(IntId(1)) == *eq(IntId(1))); + ASSERT_TRUE(*ge(dt.referenceDate1) == *ge(dt.referenceDate1)); // NotExpression - ASSERT_TRUE(*notFilterExpr(eqFilterExpr(IntId(0))) == - *notFilterExpr(eqFilterExpr(IntId(0)))); - ASSERT_TRUE(*notFilterExpr(notFilterExpr(greaterEqFilterExpr(VocabId(0)))) == - *notFilterExpr(notFilterExpr(greaterEqFilterExpr(VocabId(0))))); - ASSERT_FALSE(*notFilterExpr(greaterThanFilterExpr(IntId(0))) == - *eqFilterExpr(IntId(0))); - ASSERT_FALSE(*notFilterExpr(andFilterExpr(eqFilterExpr(IntId(1)), - eqFilterExpr(IntId(0)))) == - *notFilterExpr(greaterEqFilterExpr(VocabId(0)))); + ASSERT_TRUE(*notExpr(eq(IntId(0))) == *notExpr(eq(IntId(0)))); + ASSERT_TRUE(*notExpr(notExpr(ge(VocabId(0)))) == + *notExpr(notExpr(ge(VocabId(0))))); + ASSERT_FALSE(*notExpr(gt(IntId(0))) == *eq(IntId(0))); + ASSERT_FALSE(*notExpr(andExpr(eq(IntId(1)), eq(IntId(0)))) == + *notExpr(ge(VocabId(0)))); // Binary PrefilterExpressions (AND and OR) - ASSERT_TRUE( - *orFilterExpr(eqFilterExpr(IntId(0)), lessEqFilterExpr(IntId(0))) == - *orFilterExpr(eqFilterExpr(IntId(0)), lessEqFilterExpr(IntId(0)))); - ASSERT_TRUE( - *andFilterExpr(lessEqFilterExpr(VocabId(1)), - lessEqFilterExpr(IntId(0))) == - *andFilterExpr(lessEqFilterExpr(VocabId(1)), lessEqFilterExpr(IntId(0)))); - ASSERT_FALSE( - *orFilterExpr(eqFilterExpr(IntId(0)), lessEqFilterExpr(IntId(0))) == - *andFilterExpr(lessEqFilterExpr(VocabId(1)), lessEqFilterExpr(IntId(0)))); - ASSERT_FALSE( - *notFilterExpr( - orFilterExpr(eqFilterExpr(IntId(0)), lessEqFilterExpr(IntId(0)))) == - *orFilterExpr(eqFilterExpr(IntId(0)), lessEqFilterExpr(IntId(0)))); + ASSERT_TRUE(*orExpr(eq(IntId(0)), le(IntId(0))) == + *orExpr(eq(IntId(0)), le(IntId(0)))); + ASSERT_TRUE(*andExpr(le(VocabId(1)), le(IntId(0))) == + *andExpr(le(VocabId(1)), le(IntId(0)))); + ASSERT_FALSE(*orExpr(eq(IntId(0)), le(IntId(0))) == + *andExpr(le(VocabId(1)), le(IntId(0)))); + ASSERT_FALSE(*notExpr(orExpr(eq(IntId(0)), le(IntId(0)))) == + *orExpr(eq(IntId(0)), le(IntId(0)))); } //______________________________________________________________________________ // Test PrefilterExpression content formatting for debugging. TEST(PrefilterExpression, checkPrintFormattedPrefilterExpression) { - const auto& relExpr = lessThanFilterExpr(IntId(10)); - EXPECT_EQ((std::stringstream() << *relExpr).str(), + auto expr = lt(IntId(10)); + EXPECT_EQ((std::stringstream() << *expr).str(), "Prefilter RelationalExpression<0>\nValueId: I:10\n.\n"); - const auto& notExpr = notFilterExpr(eqFilterExpr(VocabId(0))); - EXPECT_EQ((std::stringstream() << *notExpr).str(), + expr = notExpr(eq(VocabId(0))); + EXPECT_EQ((std::stringstream() << *expr).str(), "Prefilter NotExpression:\nchild {Prefilter " "RelationalExpression<3>\nValueId: V:0\n}\n.\n"); - const auto& orExpr = - orFilterExpr(lessEqFilterExpr(IntId(0)), eqFilterExpr(IntId(5))); - EXPECT_EQ((std::stringstream() << *orExpr).str(), + expr = orExpr(le(IntId(0)), eq(IntId(5))); + EXPECT_EQ((std::stringstream() << *expr).str(), "Prefilter LogicalExpression<1>\nchild1 {Prefilter " "RelationalExpression<1>\nValueId: I:0\n}child2 {Prefilter " "RelationalExpression<2>\nValueId: I:5\n}\n.\n"); @@ -1136,20 +784,17 @@ TEST(SparqlExpression, testGetPrefilterExpressionDefault) { // Check that the (Sparql) RelationalExpression returns the expected // PrefilterExpression. TEST(SparqlExpression, getPrefilterExpressionFromSparqlRelational) { + const TestDates dt{}; const Variable var = Variable{"?x"}; - MetadataBlocks blocks{}; - evalAndEqualityCheck(eqSparqlExpr(var, BoolId(true)), - pr(eqFilterExpr(BoolId(true)), var)); - evalAndEqualityCheck(greaterEqSparqlExpr(var, IntId(1)), - pr(greaterEqFilterExpr(IntId(1)), var)); - evalAndEqualityCheck(notEqSparqlExpr(VocabId(10), var), - pr(notEqFilterExpr(VocabId(10)), var)); - evalAndEqualityCheck(greaterEqSparqlExpr(BlankNodeId(1), var), - pr(greaterEqFilterExpr(BlankNodeId(1)), var)); - evalAndEqualityCheck(lessEqSparqlExpr(var, blocks.referenceDate1), - pr(lessEqFilterExpr(blocks.referenceDate1), var)); - evalAndEqualityCheck(lessThanSparqlExpr(DoubleId(10.2), var), - pr(lessThanFilterExpr(DoubleId(10.2)), var)); + evalAndEqualityCheck(eqSprql(var, BoolId(true)), pr(eq(BoolId(true)), var)); + evalAndEqualityCheck(geSprql(var, IntId(1)), pr(ge(IntId(1)), var)); + evalAndEqualityCheck(neqSprql(VocabId(10), var), pr(neq(VocabId(10)), var)); + evalAndEqualityCheck(geSprql(BlankNodeId(1), var), + pr(ge(BlankNodeId(1)), var)); + evalAndEqualityCheck(leSprql(var, dt.referenceDate1), + pr(le(dt.referenceDate1), var)); + evalAndEqualityCheck(ltSprql(DoubleId(10.2), var), + pr(lt(DoubleId(10.2)), var)); } //______________________________________________________________________________ @@ -1163,237 +808,186 @@ TEST(SparqlExpression, getPrefilterExpressionsToComplexSparqlExpressions) { // ?x >= 10 AND ?x != 20 // expected prefilter pairs: // {<((>= 10) AND (!= 20)), ?x>} - evalAndEqualityCheck(andSparqlExpr(greaterEqSparqlExpr(varX, IntId(10)), - notEqSparqlExpr(varX, IntId(20))), - pr(andFilterExpr(greaterEqFilterExpr(IntId(10)), - notEqFilterExpr(IntId(20))), - varX)); + evalAndEqualityCheck( + andSprqlExpr(geSprql(varX, IntId(10)), neqSprql(varX, IntId(20))), + pr(andExpr(ge(IntId(10)), neq(IntId(20))), varX)); // ?z > VocabId(0) AND ?y > 0 AND ?x < 30.00 // expected prefilter pairs // {<(< 30.00), ?x>, <(> 0), ?y>, <(> VocabId(0)), ?z>} - evalAndEqualityCheck( - andSparqlExpr(andSparqlExpr(greaterThanSparqlExpr(varZ, VocabId(0)), - greaterThanSparqlExpr(varY, IntId(0))), - lessThanSparqlExpr(varX, DoubleId(30.00))), - pr(lessThanFilterExpr(DoubleId(30.00)), varX), - pr(greaterThanFilterExpr(IntId(0)), varY), - pr(greaterThanFilterExpr(VocabId(0)), varZ)); + evalAndEqualityCheck(andSprqlExpr(andSprqlExpr(gtSprql(varZ, VocabId(0)), + gtSprql(varY, IntId(0))), + ltSprql(varX, DoubleId(30.00))), + pr(lt(DoubleId(30.00)), varX), pr(gt(IntId(0)), varY), + pr(gt(VocabId(0)), varZ)); // ?x == VocabId(10) AND ?y >= VocabId(10) // expected prefilter pairs: // {<(== VocabId(10)), ?x>, <(>= VocabId(10)), ?y>} - evalAndEqualityCheck(andSparqlExpr(eqSparqlExpr(varX, VocabId(10)), - greaterEqSparqlExpr(varY, VocabId(10))), - pr(eqFilterExpr(VocabId(10)), varX), - pr(greaterEqFilterExpr(VocabId(10)), varY)); + evalAndEqualityCheck( + andSprqlExpr(eqSprql(varX, VocabId(10)), geSprql(varY, VocabId(10))), + pr(eq(VocabId(10)), varX), pr(ge(VocabId(10)), varY)); // !(?x >= 10 OR ?x <= 0) // expected prefilter pairs: // {= 10 OR ?x <= 0), ?x>} - evalAndEqualityCheck( - notSparqlExpr(orSparqlExpr(greaterEqSparqlExpr(varX, IntId(10)), - lessEqSparqlExpr(varX, IntId(0)))), - pr(notFilterExpr(orFilterExpr(greaterEqFilterExpr(IntId(10)), - lessEqFilterExpr(IntId(0)))), - varX)); + evalAndEqualityCheck(notSprqlExpr(orSprqlExpr(geSprql(varX, IntId(10)), + leSprql(varX, IntId(0)))), + pr(notExpr(orExpr(ge(IntId(10)), le(IntId(0)))), varX)); // !(?z == VocabId(10) AND ?z >= VocabId(20)) // expected prefilter pairs: // {= VocabId(20)) , ?z>} evalAndEqualityCheck( - notSparqlExpr(andSparqlExpr(eqSparqlExpr(varZ, VocabId(10)), - greaterEqSparqlExpr(varZ, VocabId(20)))), - pr(notFilterExpr(andFilterExpr(eqFilterExpr(VocabId(10)), - greaterEqFilterExpr(VocabId(20)))), - varZ)); + notSprqlExpr( + andSprqlExpr(eqSprql(varZ, VocabId(10)), geSprql(varZ, VocabId(20)))), + pr(notExpr(andExpr(eq(VocabId(10)), ge(VocabId(20)))), varZ)); // (?x == VocabId(10) AND ?z == VocabId(0)) AND ?y != DoubleId(22.1) // expected prefilter pairs: // {<(==VocabId(10)) , ?x>, <(!=DoubleId(22.1)), ?y>, <(==VocabId(0)), ?z>} - evalAndEqualityCheck( - andSparqlExpr(andSparqlExpr(eqSparqlExpr(VocabId(10), varX), - eqSparqlExpr(varZ, VocabId(0))), - notEqSparqlExpr(DoubleId(22.1), varY)), - pr(eqFilterExpr(VocabId(10)), varX), - pr(notEqFilterExpr(DoubleId(22.1)), varY), - pr(eqFilterExpr(VocabId(0)), varZ)); + evalAndEqualityCheck(andSprqlExpr(andSprqlExpr(eqSprql(VocabId(10), varX), + eqSprql(varZ, VocabId(0))), + neqSprql(DoubleId(22.1), varY)), + pr(eq(VocabId(10)), varX), pr(neq(DoubleId(22.1)), varY), + pr(eq(VocabId(0)), varZ)); // (?z >= 1000 AND ?x == VocabId(10)) OR ?z >= 10000 // expected prefilter pairs: // {<((>=1000) OR (>= 10000)), ?z>} - evalAndEqualityCheck( - orSparqlExpr(andSparqlExpr(greaterEqSparqlExpr(varZ, IntId(1000)), - eqSparqlExpr(varX, VocabId(10))), - greaterEqSparqlExpr(varZ, IntId(10000))), - pr(orFilterExpr(greaterEqFilterExpr(IntId(1000)), - greaterEqFilterExpr(IntId(10000))), - varZ)); + evalAndEqualityCheck(orSprqlExpr(andSprqlExpr(geSprql(varZ, IntId(1000)), + eqSprql(varX, VocabId(10))), + geSprql(varZ, IntId(10000))), + pr(orExpr(ge(IntId(1000)), ge(IntId(10000))), varZ)); // !((?z <= VocabId(10) OR ?y <= VocabId(10)) OR ?x <= VocabId(10)) // expected prefilter pairs: // {, , } - evalAndEqualityCheck(notSparqlExpr(orSparqlExpr( - orSparqlExpr(lessEqSparqlExpr(varZ, VocabId(10)), - lessEqSparqlExpr(varY, VocabId(10))), - lessEqSparqlExpr(varX, VocabId(10)))), - pr(notFilterExpr(lessEqFilterExpr(VocabId(10))), varX), - pr(notFilterExpr(lessEqFilterExpr(VocabId(10))), varY), - pr(notFilterExpr(lessEqFilterExpr(VocabId(10))), varZ)); + evalAndEqualityCheck( + notSprqlExpr(orSprqlExpr( + orSprqlExpr(leSprql(varZ, VocabId(10)), leSprql(varY, VocabId(10))), + leSprql(varX, VocabId(10)))), + pr(notExpr(le(VocabId(10))), varX), pr(notExpr(le(VocabId(10))), varY), + pr(notExpr(le(VocabId(10))), varZ)); // ?x >= 10 AND ?y >= 10 // expected prefilter pairs: // {<(>= 10), ?x>, <(>= 10), ?y>} - evalAndEqualityCheck(andSparqlExpr(greaterEqSparqlExpr(varX, IntId(10)), - greaterEqSparqlExpr(varY, IntId(10))), - pr(greaterEqFilterExpr(IntId(10)), varX), - pr(greaterEqFilterExpr(IntId(10)), varY)); + evalAndEqualityCheck( + andSprqlExpr(geSprql(varX, IntId(10)), geSprql(varY, IntId(10))), + pr(ge(IntId(10)), varX), pr(ge(IntId(10)), varY)); // ?x <= 0 AND ?y <= 0 // expected prefilter pairs: // {<(<= 0), ?x>, <(<= 0), ?y>} - evalAndEqualityCheck(andSparqlExpr(lessEqSparqlExpr(varX, IntId(0)), - lessEqSparqlExpr(varY, IntId(0))), - pr(lessEqFilterExpr(IntId(0)), varX), - pr(lessEqFilterExpr(IntId(0)), varY)); + evalAndEqualityCheck( + andSprqlExpr(leSprql(varX, IntId(0)), leSprql(varY, IntId(0))), + pr(le(IntId(0)), varX), pr(le(IntId(0)), varY)); // (?x >= 10 AND ?y >= 10) OR (?x <= 0 AND ?y <= 0) // expected prefilter pairs: // {<((>= 10) OR (<= 0)), ?x> <(>= 10) OR (<= 0)), ?y>} evalAndEqualityCheck( - orSparqlExpr(andSparqlExpr(greaterEqSparqlExpr(varX, IntId(10)), - greaterEqSparqlExpr(varY, IntId(10))), - andSparqlExpr(lessEqSparqlExpr(varX, IntId(0)), - lessEqSparqlExpr(varY, IntId(0)))), - pr(orFilterExpr(greaterEqFilterExpr(IntId(10)), - lessEqFilterExpr(IntId(0))), - varX), - pr(orFilterExpr(greaterEqFilterExpr(IntId(10)), - lessEqFilterExpr(IntId(0))), - varY)); + orSprqlExpr( + andSprqlExpr(geSprql(varX, IntId(10)), geSprql(varY, IntId(10))), + andSprqlExpr(leSprql(varX, IntId(0)), leSprql(varY, IntId(0)))), + pr(orExpr(ge(IntId(10)), le(IntId(0))), varX), + pr(orExpr(ge(IntId(10)), le(IntId(0))), varY)); // !(?x >= 10 OR ?y >= 10) OR !(?x <= 0 OR ?y <= 0) // expected prefilter pairs: // {<((!(>= 10) OR !(<= 0))), ?x> <(!(>= 10) OR !(<= 0))), ?y>} evalAndEqualityCheck( - orSparqlExpr( - notSparqlExpr(orSparqlExpr(greaterEqSparqlExpr(varX, IntId(10)), - greaterEqSparqlExpr(varY, IntId(10)))), - notSparqlExpr(orSparqlExpr(lessEqSparqlExpr(varX, IntId(0)), - lessEqSparqlExpr(varY, IntId(0))))), - pr(orFilterExpr(notFilterExpr(greaterEqFilterExpr(IntId(10))), - notFilterExpr(lessEqFilterExpr(IntId(0)))), - varX), - pr(orFilterExpr(notFilterExpr(greaterEqFilterExpr(IntId(10))), - notFilterExpr(lessEqFilterExpr(IntId(0)))), - varY)); + orSprqlExpr(notSprqlExpr(orSprqlExpr(geSprql(varX, IntId(10)), + geSprql(varY, IntId(10)))), + notSprqlExpr(orSprqlExpr(leSprql(varX, IntId(0)), + leSprql(varY, IntId(0))))), + pr(orExpr(notExpr(ge(IntId(10))), notExpr(le(IntId(0)))), varX), + pr(orExpr(notExpr(ge(IntId(10))), notExpr(le(IntId(0)))), varY)); // !(?x == VocabId(10) OR ?x == VocabId(20)) AND !(?z >= 10.00 OR ?y == false) // expected prefilter pairs: // {, , // = 10), ?z>} evalAndEqualityCheck( - andSparqlExpr( - notSparqlExpr(orSparqlExpr(eqSparqlExpr(varX, VocabId(10)), - eqSparqlExpr(varX, VocabId(20)))), - notSparqlExpr(orSparqlExpr(greaterEqSparqlExpr(varZ, DoubleId(10)), - eqSparqlExpr(varY, BoolId(false))))), - pr(notFilterExpr(orFilterExpr(eqFilterExpr(VocabId(10)), - eqFilterExpr(VocabId(20)))), - varX), - pr(notFilterExpr(eqFilterExpr(BoolId(false))), varY), - pr(notFilterExpr(greaterEqFilterExpr(DoubleId(10))), varZ)); + andSprqlExpr(notSprqlExpr(orSprqlExpr(eqSprql(varX, VocabId(10)), + eqSprql(varX, VocabId(20)))), + notSprqlExpr(orSprqlExpr(geSprql(varZ, DoubleId(10)), + eqSprql(varY, BoolId(false))))), + pr(notExpr(orExpr(eq(VocabId(10)), eq(VocabId(20)))), varX), + pr(notExpr(eq(BoolId(false))), varY), + pr(notExpr(ge(DoubleId(10))), varZ)); // !(!(?x >= 10 AND ?y >= 10)) OR !(!(?x <= 0 AND ?y <= 0)) // expected prefilter pairs: // {<(!!(>= 10) OR !!(<= 0)), ?x>, <(!!(>= 10) OR !!(<= 0)) ,?y>} evalAndEqualityCheck( - orSparqlExpr(notSparqlExpr(notSparqlExpr( - andSparqlExpr(greaterEqSparqlExpr(varX, IntId(10)), - greaterEqSparqlExpr(varY, IntId(10))))), - notSparqlExpr(notSparqlExpr( - andSparqlExpr(lessEqSparqlExpr(varX, IntId(0)), - lessEqSparqlExpr(varY, IntId(0)))))), - pr(orFilterExpr( - notFilterExpr(notFilterExpr(greaterEqFilterExpr(IntId(10)))), - notFilterExpr(notFilterExpr(lessEqFilterExpr(IntId(0))))), + orSprqlExpr(notSprqlExpr(notSprqlExpr(andSprqlExpr( + geSprql(varX, IntId(10)), geSprql(varY, IntId(10))))), + notSprqlExpr(notSprqlExpr(andSprqlExpr( + leSprql(varX, IntId(0)), leSprql(varY, IntId(0)))))), + pr(orExpr(notExpr(notExpr(ge(IntId(10)))), + notExpr(notExpr(le(IntId(0))))), varX), - pr(orFilterExpr( - notFilterExpr(notFilterExpr(greaterEqFilterExpr(IntId(10)))), - notFilterExpr(notFilterExpr(lessEqFilterExpr(IntId(0))))), + pr(orExpr(notExpr(notExpr(ge(IntId(10)))), + notExpr(notExpr(le(IntId(0))))), varY)); // !((?x >= VocabId(0) AND ?x <= VocabId(10)) OR !(?x != VocabId(99))) // expected prefilter pairs: // {= VocabId(0)) AND (<= VocabId(10))) OR !(!= VocabId(99))) , ?x>} - evalAndEqualityCheck(notSparqlExpr(orSparqlExpr( - andSparqlExpr(greaterEqSparqlExpr(varX, VocabId(0)), - lessEqSparqlExpr(varX, VocabId(10))), - notSparqlExpr(notEqSparqlExpr(varX, VocabId(99))))), - pr(notFilterExpr(orFilterExpr( - andFilterExpr(greaterEqFilterExpr(VocabId(0)), - lessEqFilterExpr(VocabId(10))), - notFilterExpr(notEqFilterExpr(VocabId(99))))), - varX)); + evalAndEqualityCheck( + notSprqlExpr(orSprqlExpr( + andSprqlExpr(geSprql(varX, VocabId(0)), leSprql(varX, VocabId(10))), + notSprqlExpr(neqSprql(varX, VocabId(99))))), + pr(notExpr(orExpr(andExpr(ge(VocabId(0)), le(VocabId(10))), + notExpr(neq(VocabId(99))))), + varX)); // !((?y >= 10 AND ?y <= 100) OR !(?x >= VocabId(99))) // expected prefilter pairs: // {= VocabId(0)) AND (<= VocabId(10)), ?y>, = VocabId(99))), ?x>} evalAndEqualityCheck( - notSparqlExpr( - orSparqlExpr(andSparqlExpr(greaterEqSparqlExpr(varY, VocabId(0)), - lessEqSparqlExpr(varY, VocabId(10))), - notSparqlExpr(greaterEqSparqlExpr(varX, VocabId(99))))), - pr(notFilterExpr(notFilterExpr(greaterEqFilterExpr(VocabId(99)))), varX), - pr(notFilterExpr(andFilterExpr(greaterEqFilterExpr(VocabId(0)), - lessEqFilterExpr(VocabId(10)))), - varY)); + notSprqlExpr(orSprqlExpr( + andSprqlExpr(geSprql(varY, VocabId(0)), leSprql(varY, VocabId(10))), + notSprqlExpr(geSprql(varX, VocabId(99))))), + pr(notExpr(notExpr(ge(VocabId(99)))), varX), + pr(notExpr(andExpr(ge(VocabId(0)), le(VocabId(10)))), varY)); // ?z >= 10 AND ?z <= 100 AND ?x >= 10 AND ?x != 50 AND !(?y <= 10) AND // !(?city <= VocabId(1000) OR ?city == VocabId(1005)) // expected prefilter pairs: // {, <((>= 10) AND (!= // 50)), ?x>, , <((>= 10) AND (<= 100)), ?z>} evalAndEqualityCheck( - andSparqlExpr( - andSparqlExpr( - andSparqlExpr(greaterEqSparqlExpr(varZ, IntId(10)), - lessEqSparqlExpr(varZ, IntId(100))), - andSparqlExpr(andSparqlExpr(greaterEqSparqlExpr(varX, IntId(10)), - notEqSparqlExpr(varX, IntId(50))), - notSparqlExpr(lessEqSparqlExpr(varY, IntId(10))))), - notSparqlExpr( - orSparqlExpr(lessEqSparqlExpr(Variable{"?city"}, VocabId(1000)), - eqSparqlExpr(Variable{"?city"}, VocabId(1005))))), - pr(notFilterExpr(orFilterExpr(lessEqFilterExpr(VocabId(1000)), - eqFilterExpr(VocabId(1005)))), + andSprqlExpr( + andSprqlExpr( + andSprqlExpr(geSprql(varZ, IntId(10)), leSprql(varZ, IntId(100))), + andSprqlExpr(andSprqlExpr(geSprql(varX, IntId(10)), + neqSprql(varX, IntId(50))), + notSprqlExpr(leSprql(varY, IntId(10))))), + notSprqlExpr(orSprqlExpr(leSprql(Variable{"?city"}, VocabId(1000)), + eqSprql(Variable{"?city"}, VocabId(1005))))), + pr(notExpr(orExpr(le(VocabId(1000)), eq(VocabId(1005)))), Variable{"?city"}), - pr(andFilterExpr(greaterEqFilterExpr(IntId(10)), - notEqFilterExpr(IntId(50))), - varX), - pr(notFilterExpr(lessEqFilterExpr(IntId(10))), varY), - pr(andFilterExpr(greaterEqFilterExpr(IntId(10)), - lessEqFilterExpr(IntId(100))), - varZ)); + pr(andExpr(ge(IntId(10)), neq(IntId(50))), varX), + pr(notExpr(le(IntId(10))), varY), + pr(andExpr(ge(IntId(10)), le(IntId(100))), varZ)); // ?x >= 10 OR (?x >= -10 AND ?x < 0.00) // expected prefilter pairs: // {<((>= 10) OR ((>= -10) AND (< 0.00))), ?x>} evalAndEqualityCheck( - orSparqlExpr(greaterEqSparqlExpr(varX, IntId(10)), - andSparqlExpr(greaterEqSparqlExpr(varX, IntId(-10)), - lessThanSparqlExpr(DoubleId(0.00), varX))), - pr(orFilterExpr(greaterEqFilterExpr(IntId(10)), - andFilterExpr(greaterEqFilterExpr(IntId(-10)), - lessThanFilterExpr(DoubleId(0.00)))), + orSprqlExpr(geSprql(varX, IntId(10)), + andSprqlExpr(geSprql(varX, IntId(-10)), + ltSprql(DoubleId(0.00), varX))), + pr(orExpr(ge(IntId(10)), andExpr(ge(IntId(-10)), lt(DoubleId(0.00)))), varX)); // !(!(?x >= 10) OR !!(?x >= -10 AND ?x < 0.00)) // expected prefilter pairs: // {= 10) OR !!((>= -10) AND (< 0.00))), ?x>} - evalAndEqualityCheck(notSparqlExpr(orSparqlExpr( - notSparqlExpr(greaterEqSparqlExpr(varX, IntId(10))), - notSparqlExpr(notSparqlExpr(andSparqlExpr( - greaterEqSparqlExpr(varX, IntId(-10)), - lessThanSparqlExpr(DoubleId(0.00), varX)))))), - pr(notFilterExpr(orFilterExpr( - notFilterExpr(greaterEqFilterExpr(IntId(10))), - notFilterExpr(notFilterExpr(andFilterExpr( - greaterEqFilterExpr(IntId(-10)), - lessThanFilterExpr(DoubleId(0.00))))))), - varX)); + evalAndEqualityCheck( + notSprqlExpr(orSprqlExpr( + notSprqlExpr(geSprql(varX, IntId(10))), + notSprqlExpr(notSprqlExpr(andSprqlExpr( + geSprql(varX, IntId(-10)), ltSprql(DoubleId(0.00), varX)))))), + pr(notExpr(orExpr( + notExpr(ge(IntId(10))), + notExpr(notExpr(andExpr(ge(IntId(-10)), lt(DoubleId(0.00))))))), + varX)); // ?y != ?x AND ?x >= 10 // expected prefilter pairs: // {<(>= 10), ?x>} - evalAndEqualityCheck(andSparqlExpr(notEqSparqlExpr(varY, varX), - greaterEqSparqlExpr(varX, IntId(10))), - pr(greaterEqFilterExpr(IntId(10)), varX)); - evalAndEqualityCheck(andSparqlExpr(greaterEqSparqlExpr(varX, IntId(10)), - notEqSparqlExpr(varY, varX)), - pr(greaterEqFilterExpr(IntId(10)), varX)); + evalAndEqualityCheck( + andSprqlExpr(neqSprql(varY, varX), geSprql(varX, IntId(10))), + pr(ge(IntId(10)), varX)); + evalAndEqualityCheck( + andSprqlExpr(geSprql(varX, IntId(10)), neqSprql(varY, varX)), + pr(ge(IntId(10)), varX)); } //______________________________________________________________________________ @@ -1402,20 +996,17 @@ TEST(SparqlExpression, getEmptyPrefilterFromSparqlRelational) { const Variable var = Variable{"?x"}; const Iri iri = I(""); const Literal lit = L("\"lit\""); - evalToEmptyCheck(lessEqSparqlExpr(var, var)); - evalToEmptyCheck(notEqSparqlExpr(iri, var)); - evalToEmptyCheck(eqSparqlExpr(var, iri)); - evalToEmptyCheck(notEqSparqlExpr(IntId(10), DoubleId(23.3))); - evalToEmptyCheck(greaterThanSparqlExpr(DoubleId(10), lit)); - evalToEmptyCheck(lessThanSparqlExpr(VocabId(10), BoolId(10))); - evalToEmptyCheck(greaterEqSparqlExpr(lit, lit)); - evalToEmptyCheck(eqSparqlExpr(iri, iri)); - evalToEmptyCheck(orSparqlExpr(eqSparqlExpr(var, var), - greaterThanSparqlExpr(var, IntId(0)))); - evalToEmptyCheck( - orSparqlExpr(eqSparqlExpr(var, var), greaterThanSparqlExpr(var, var))); - evalToEmptyCheck( - andSparqlExpr(eqSparqlExpr(var, var), greaterThanSparqlExpr(var, var))); + evalToEmptyCheck(leSprql(var, var)); + evalToEmptyCheck(neqSprql(iri, var)); + evalToEmptyCheck(eqSprql(var, iri)); + evalToEmptyCheck(neqSprql(IntId(10), DoubleId(23.3))); + evalToEmptyCheck(gtSprql(DoubleId(10), lit)); + evalToEmptyCheck(ltSprql(VocabId(10), BoolId(10))); + evalToEmptyCheck(geSprql(lit, lit)); + evalToEmptyCheck(eqSprql(iri, iri)); + evalToEmptyCheck(orSprqlExpr(eqSprql(var, var), gtSprql(var, IntId(0)))); + evalToEmptyCheck(orSprqlExpr(eqSprql(var, var), gtSprql(var, var))); + evalToEmptyCheck(andSprqlExpr(eqSprql(var, var), gtSprql(var, var))); } //______________________________________________________________________________ @@ -1426,86 +1017,79 @@ TEST(SparqlExpression, getEmptyPrefilterForMoreComplexSparqlExpressions) { const Variable varY = Variable{"?y"}; const Variable varZ = Variable{"?z"}; // ?x <= 10.00 OR ?y > 10 - evalToEmptyCheck(orSparqlExpr(lessEqSparqlExpr(DoubleId(10), varX), - greaterThanSparqlExpr(IntId(10), varY))); + evalToEmptyCheck( + orSprqlExpr(leSprql(DoubleId(10), varX), gtSprql(IntId(10), varY))); // ?x >= VocabId(23) OR ?z == VocabId(1) - evalToEmptyCheck(orSparqlExpr(greaterEqSparqlExpr(varX, VocabId(23)), - eqSparqlExpr(varZ, VocabId(1)))); - // (?x < VocabId(10) OR ?z <= VocabId(4)) OR ?z != 5.00 evalToEmptyCheck( - orSparqlExpr(orSparqlExpr(lessThanSparqlExpr(varX, VocabId(10)), - lessEqSparqlExpr(VocabId(4), varZ)), - notEqSparqlExpr(varZ, DoubleId(5)))); + orSprqlExpr(geSprql(varX, VocabId(23)), eqSprql(varZ, VocabId(1)))); + // (?x < VocabId(10) OR ?z <= VocabId(4)) OR ?z != 5.00 + evalToEmptyCheck(orSprqlExpr( + orSprqlExpr(ltSprql(varX, VocabId(10)), leSprql(VocabId(4), varZ)), + neqSprql(varZ, DoubleId(5)))); // !(?z > 10.20 AND ?x < 0.001) // is equal to // ?z <= 10.20 OR ?x >= 0.001 - evalToEmptyCheck( - notSparqlExpr(andSparqlExpr(greaterThanSparqlExpr(DoubleId(10.2), varZ), - lessThanSparqlExpr(DoubleId(0.001), varX)))); + evalToEmptyCheck(notSprqlExpr(andSprqlExpr(gtSprql(DoubleId(10.2), varZ), + ltSprql(DoubleId(0.001), varX)))); // !(?x > 10.20 AND ?z != VocabId(22)) // is equal to // ?x <= 10.20 OR ?z == VocabId(22) - evalToEmptyCheck( - notSparqlExpr(andSparqlExpr(greaterThanSparqlExpr(DoubleId(10.2), varX), - notEqSparqlExpr(VocabId(22), varZ)))); + evalToEmptyCheck(notSprqlExpr(andSprqlExpr(gtSprql(DoubleId(10.2), varX), + neqSprql(VocabId(22), varZ)))); // !(!((?x < VocabId(10) OR ?x <= VocabId(4)) OR ?z != 5.00)) // is equal to // (?x < VocabId(10) OR ?x <= VocabId(4)) OR ?z != 5.00 - evalToEmptyCheck(notSparqlExpr(notSparqlExpr( - orSparqlExpr(orSparqlExpr(lessThanSparqlExpr(varX, VocabId(10)), - lessEqSparqlExpr(VocabId(4), varX)), - notEqSparqlExpr(varZ, DoubleId(5)))))); + evalToEmptyCheck(notSprqlExpr(notSprqlExpr(orSprqlExpr( + orSprqlExpr(ltSprql(varX, VocabId(10)), leSprql(VocabId(4), varX)), + neqSprql(varZ, DoubleId(5)))))); // !(?x != 10 AND !(?y >= 10.00 OR ?z <= 10)) // is equal to // ?x == 10 OR ?y >= 10.00 OR ?z <= 10 - evalToEmptyCheck(notSparqlExpr(andSparqlExpr( - notEqSparqlExpr(varX, IntId(10)), - notSparqlExpr(orSparqlExpr(greaterEqSparqlExpr(varY, DoubleId(10.00)), - lessEqSparqlExpr(varZ, IntId(10))))))); + evalToEmptyCheck(notSprqlExpr( + andSprqlExpr(neqSprql(varX, IntId(10)), + notSprqlExpr(orSprqlExpr(geSprql(varY, DoubleId(10.00)), + leSprql(varZ, IntId(10))))))); // !((?x != 10 AND ?z != 10) AND (?y == 10 AND ?x >= 20)) // is equal to //?x == 10 OR ?z == 10 OR ?y != 10 OR ?x < 20 - evalToEmptyCheck(notSparqlExpr( - andSparqlExpr(andSparqlExpr(notEqSparqlExpr(varX, IntId(10)), - notEqSparqlExpr(varZ, IntId(10))), - andSparqlExpr(eqSparqlExpr(varY, IntId(10)), - greaterEqSparqlExpr(varX, IntId(20)))))); + evalToEmptyCheck(notSprqlExpr(andSprqlExpr( + andSprqlExpr(neqSprql(varX, IntId(10)), neqSprql(varZ, IntId(10))), + andSprqlExpr(eqSprql(varY, IntId(10)), geSprql(varX, IntId(20)))))); // !(?z >= 40 AND (?z != 10.00 AND ?y != VocabId(1))) // is equal to // ?z <= 40 OR ?z == 10.00 OR ?y == VocabId(1) - evalToEmptyCheck(notSparqlExpr( - andSparqlExpr(greaterEqSparqlExpr(varZ, IntId(40)), - andSparqlExpr(notEqSparqlExpr(varZ, DoubleId(10.00)), - notEqSparqlExpr(varY, VocabId(1)))))); + evalToEmptyCheck(notSprqlExpr(andSprqlExpr( + geSprql(varZ, IntId(40)), andSprqlExpr(neqSprql(varZ, DoubleId(10.00)), + neqSprql(varY, VocabId(1)))))); // ?z <= true OR !(?x == 10 AND ?y == 10) // is equal to // ?z <= true OR ?x != 10 OR ?y != 10 - evalToEmptyCheck(orSparqlExpr( - lessEqSparqlExpr(varZ, BoolId(true)), - notSparqlExpr(andSparqlExpr(eqSparqlExpr(varX, IntId(10)), - eqSparqlExpr(IntId(10), varY))))); + evalToEmptyCheck( + orSprqlExpr(leSprql(varZ, BoolId(true)), + notSprqlExpr(andSprqlExpr(eqSprql(varX, IntId(10)), + eqSprql(IntId(10), varY))))); // !(!(?z <= true OR !(?x == 10 AND ?y == 10))) // is equal to // ?z <= true OR ?x != 10 OR ?y != 10 - evalToEmptyCheck(notSparqlExpr(notSparqlExpr(orSparqlExpr( - lessEqSparqlExpr(varZ, BoolId(true)), - notSparqlExpr(andSparqlExpr(eqSparqlExpr(varX, IntId(10)), - eqSparqlExpr(IntId(10), varY))))))); + evalToEmptyCheck(notSprqlExpr(notSprqlExpr( + orSprqlExpr(leSprql(varZ, BoolId(true)), + notSprqlExpr(andSprqlExpr(eqSprql(varX, IntId(10)), + eqSprql(IntId(10), varY))))))); // !(!(?x != 10 OR !(?y >= 10.00 AND ?z <= 10))) // is equal to // ?x != 10 OR ?y < 10.00 OR ?z > 10 - evalToEmptyCheck(notSparqlExpr(notSparqlExpr(orSparqlExpr( - notEqSparqlExpr(varX, IntId(10)), - notSparqlExpr(andSparqlExpr(greaterEqSparqlExpr(varY, DoubleId(10.00)), - lessEqSparqlExpr(varZ, IntId(10)))))))); + evalToEmptyCheck(notSprqlExpr(notSprqlExpr( + orSprqlExpr(neqSprql(varX, IntId(10)), + notSprqlExpr(andSprqlExpr(geSprql(varY, DoubleId(10.00)), + leSprql(varZ, IntId(10)))))))); // !(!(?x == VocabId(10) OR ?y >= 25) AND !(!(?z == true AND ?country == // VocabId(20)))) // is equal to // ?x == VocabId(10) OR ?y >= 25 OR ?z == true AND ?country == VocabId(20) - evalToEmptyCheck(notSparqlExpr(andSparqlExpr( - notSparqlExpr(orSparqlExpr(eqSparqlExpr(varX, VocabId(10)), - greaterEqSparqlExpr(varY, IntId(25)))), - notSparqlExpr(notSparqlExpr( - andSparqlExpr(eqSparqlExpr(varZ, BoolId(true)), - eqSparqlExpr(Variable{"?country"}, VocabId(20)))))))); + evalToEmptyCheck(notSprqlExpr(andSprqlExpr( + notSprqlExpr( + orSprqlExpr(eqSprql(varX, VocabId(10)), geSprql(varY, IntId(25)))), + notSprqlExpr(notSprqlExpr( + andSprqlExpr(eqSprql(varZ, BoolId(true)), + eqSprql(Variable{"?country"}, VocabId(20)))))))); } From 9f0efc6298c9a4b783b0d2b4fab2f41c21aab15f Mon Sep 17 00:00:00 2001 From: realHannes Date: Mon, 28 Oct 2024 12:49:13 +0100 Subject: [PATCH 43/81] fix build --- src/index/CompressedBlockPrefiltering.cpp | 2 +- src/index/CompressedBlockPrefiltering.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/index/CompressedBlockPrefiltering.cpp b/src/index/CompressedBlockPrefiltering.cpp index 72d4488df2..75182fd61f 100644 --- a/src/index/CompressedBlockPrefiltering.cpp +++ b/src/index/CompressedBlockPrefiltering.cpp @@ -224,7 +224,7 @@ std::vector RelationalExpression::evaluateImpl( relevantBlocks.shrink_to_fit(); // Merge mixedDatatypeBlocks into relevantBlocks while maintaining order and // avoiding duplicates. - return getSetUnion(relevantBlocks, mixedDatatypeBlocks, evaluationColumn); + return getSetUnion(relevantBlocks, mixedDatatypeBlocks); }; //______________________________________________________________________________ diff --git a/src/index/CompressedBlockPrefiltering.h b/src/index/CompressedBlockPrefiltering.h index e64eaa3b12..08fa9092c7 100644 --- a/src/index/CompressedBlockPrefiltering.h +++ b/src/index/CompressedBlockPrefiltering.h @@ -15,7 +15,7 @@ namespace prefilterExpressions { //______________________________________________________________________________ // The maximum recursion depth for `info()` / `operator<<()`. A depth of `3` -// should be sufficient for most `PrefilterExpressions` with our use case. +// should be sufficient for most `PrefilterExpressions` in our use case. constexpr size_t maxInfoRecursion = 3; //______________________________________________________________________________ From 25f325a5b102051286f834cae736b1e90445ffc2 Mon Sep 17 00:00:00 2001 From: realHannes Date: Wed, 30 Oct 2024 16:01:12 +0100 Subject: [PATCH 44/81] implement proposed changes (2) --- .../NumericBinaryExpressions.cpp | 265 +++++++++++------- .../NumericUnaryExpressions.cpp | 44 ++- .../RelationalExpressions.cpp | 89 +++--- src/index/CompressedBlockPrefiltering.cpp | 24 ++ src/index/CompressedBlockPrefiltering.h | 27 +- src/parser/data/Variable.h | 4 +- test/CompressedBlockPrefilteringTest.cpp | 83 +++++- 7 files changed, 369 insertions(+), 167 deletions(-) diff --git a/src/engine/sparqlExpressions/NumericBinaryExpressions.cpp b/src/engine/sparqlExpressions/NumericBinaryExpressions.cpp index 4ae1abc273..7ffa773a53 100644 --- a/src/engine/sparqlExpressions/NumericBinaryExpressions.cpp +++ b/src/engine/sparqlExpressions/NumericBinaryExpressions.cpp @@ -68,50 +68,53 @@ inline auto andLambda = [](TernaryBool a, TernaryBool b) { }; namespace constructPrefilterExpr { +namespace { +// MERGE AND CONJUNCTION // _____________________________________________________________________________ +// For our pre-filtering logic over index scans, we need exactly one +// corresponding PrefilterExpression for each relavant Variable. Thus, if the +// left and right child contain a PrefilterExpression w.r.t. the same Variable, +// combine them here for an AND conjunction. In the following, three examples +// are given on how the following function merges the content of the left and +// right child. +// +// EXAMPLE 1 +// left child: {<(>= 10), ?x>, <(!= 5), ?y>}; (?x >= 10 && ?y != 5) +// right child: {}; (std::nullopt) +// The AND conjunction can only evaluate to true, if both child expressions +// evaluate to true. For the given example, we have no PrefilterExpression +// for the right child, but we can certainly assume that the left child +// expression must evaluate to true. +// Thus, return {<(>= 10), ?x>, <(!= 5), ?y>}. +// +// EXAMPLE 2 +// left child: {<(= 5), ?x>}; (?x = 5) +// right child: {<(= VocabId(10)), ?y>}; (?y = VocabId(10)) +// Returns {<(= 5), ?x>, <(= VocabId(10)), ?y>}. +// +// EXAMPLE 3 +// left child: {<(>= 10 AND <= 20), ?x>}; (?x >= 10 && ?x <= 20) +// right child: {<(!= 15), ?x>, <(= 10), ?y>}; (?x != 15 && ?y = 10) +// Returns {<((>= 10 AND <= 20) AND != 15), ?x>, <(= 10), ?y>} +//______________________________________________________________________________ template -std::optional> mergeChildrenAndLogicImpl( - std::optional>&& leftChildExprs, - std::optional>&& rightChildExprs) { - if (!leftChildExprs.has_value() || !rightChildExprs.has_value()) { - // If we have two children that yield no PrefilterExpressions, then there - // is also no PrefilterExpression over the AND (&&) conjunction available. - if (!leftChildExprs.has_value() && !rightChildExprs.has_value()) { - return std::nullopt; - } - // Given that only one of the children returned a PrefilterExpression - // vector and the pre-filter construction logic of AND (&&), we can just - // return the corresponding (non-empty) vector. - // - // EXAMPLE why this works: - // Assume that left is - {(>=5, ?x), (<=6, ?y)} - // and right is - std::nullopt ({undefinable prefilter}) - // Remark: left represents the expression ?x >= 5 && ?y <= 6 - // - // For this AND conjunction {(>=5, ?x), (<=6, ?y)} && {undef. - // prefilter}, we can at least (partly) pre-define the plausible true - // evaluation range with the left child. This is because the respective - // expressions contained in left child must always yield true - // for making this AND conjunction expression true. - return leftChildExprs.has_value() ? std::move(leftChildExprs.value()) - : std::move(rightChildExprs.value()); - } +std::optional> +mergeChildrenForAndExpressionImpl( + std::vector&& leftChild, + std::vector&& rightChild) { + namespace pd = prefilterExpressions::detail; + pd::checkPropertiesForPrefilterConstruction(leftChild); + pd::checkPropertiesForPrefilterConstruction(rightChild); // Merge the PrefilterExpression vectors from the left and right child. // Remark: The vectors contain std::pairs, sorted by their respective - // variable to the PrefilterExpression. - auto& expressionsLeft = leftChildExprs.value(); - auto& expressionsRight = rightChildExprs.value(); - auto itLeft = expressionsLeft.begin(); - auto itRight = expressionsRight.begin(); + // Variable to the PrefilterExpression. + auto itLeft = leftChild.begin(); + auto itRight = rightChild.begin(); std::vector resPairs; - while (itLeft != expressionsLeft.end() && itRight != expressionsRight.end()) { + while (itLeft != leftChild.end() && itRight != rightChild.end()) { auto& [exprLeft, varLeft] = *itLeft; auto& [exprRight, varRight] = *itRight; - // For our pre-filtering logic over index scans, we need exactly one - // PrefilterExpression for each of the variables. Thus if the left and right - // child contain a PrefilterExpression w.r.t. the same variable, combine - // them here over a (new) prefilter AndExpression. if (varLeft == varRight) { resPairs.emplace_back(std::make_unique( std::move(exprLeft), std::move(exprRight)), @@ -126,67 +129,65 @@ std::optional> mergeChildrenAndLogicImpl( ++itRight; } } - std::ranges::move(itLeft, expressionsLeft.end(), - std::back_inserter(resPairs)); - std::ranges::move(itRight, expressionsRight.end(), - std::back_inserter(resPairs)); + std::ranges::move(itLeft, leftChild.end(), std::back_inserter(resPairs)); + std::ranges::move(itRight, rightChild.end(), std::back_inserter(resPairs)); + if (resPairs.empty()) { + // No PrefilterExpression(s) available with this conjunction merge on the + // contents of left and right child. + return std::nullopt; + } + pd::checkPropertiesForPrefilterConstruction(resPairs); return resPairs; } +// MERGE OR CONJUNCTION +//______________________________________________________________________________ +// Four examples on how this function (OR) merges the content of two children. +// +// EXAMPLE 1 +// left child: {} (std::nullopt) +// right child: {<(<= 10), ?y>} +// Given that it is not possible to make a fixed value assumption (true or +// false) for the left (empty) child, it is also unclear if the right child +// must evaluate to true for this OR expression to become true. +// Hence, no PrefilterExpression (std::nullopt) will be returned. +// +// EXAMPLE 2 +// left child: {<(= 5), ?y>} +// right child: {<(<= 3), ?x>} +// We want to pre-filter the blocks for the expression: ?y >= 5 || ?x <= 3 +// No PrefilterExpression will be returned. +// +// EXAMPLE 3 +// left child: {<(>= 5), ?x>} +// right child: {<(= 0), ?x>} +// We want to pre-filter the blocks to the expression: ?x >= 5 || ?x = 0 +// The resulting PrefilterExpression is {<(>= 5 OR = 0), ?x>} +// +// EXAMPLE 4 +// left child: {<(= 10), ?x), <(!= 0), ?y>} +// right child: {<(<= 0), ?x>} +// We have to construct a PrefilterExpression for (?x >= 10 && ?y != 0) || +// ?x <= 0. If this OR expression yields true, at least ?x >= 10 || ?x <= 0 +// must be staisifed; for this objective we can construct a +// PrefiterExpression. We can't make a distinct prediction w.r.t. ?y != 0 +// => not relevant for the PrefilterExpression. Thus, we return the +// PrefilterExpresion {<(>= 10 OR <= 0), ?x>}. //______________________________________________________________________________ template -std::optional> mergeChildrenOrLogicImpl( - std::optional>&& leftChildExprs, - std::optional>&& rightChildExprs) { - if (!leftChildExprs.has_value() || !rightChildExprs.has_value()) { - // If one of the children yields no PrefilterExpressions, we simply can't - // further define a PrefilterExpression with this OR (||) conjunction. - // - // EXAMPLE / REASON: - // Assume that left is - {(=10, ?x), (=10, ?y)} - // and right is - std::nullopt ({undefinable prefilter}) - // Remark: left represents the expression ?x = 10 && ?y = 10. - // We can't define a PrefilterExpression here because left must not always - // yield true when this OR expression evaluates to true (because right can - // plausibly yield true). - // And because we can't definitely say that one child (and which of the two - // children) must be true, pre-filtering w.r.t. underlying compressed blocks - // is not possible here. - // In short: OR has multiple configurations that yield true w.r.t. two child - // expressions => we can't define a distinct PrefilterExpression. - return std::nullopt; - } - auto& expressionsLeft = leftChildExprs.value(); - auto& expressionsRight = rightChildExprs.value(); - auto itLeft = expressionsLeft.begin(); - auto itRight = expressionsRight.begin(); +std::optional> +mergeChildrenForOrExpressionImpl( + std::vector&& leftChild, + std::vector&& rightChild) { + namespace pd = prefilterExpressions::detail; + pd::checkPropertiesForPrefilterConstruction(leftChild); + pd::checkPropertiesForPrefilterConstruction(rightChild); + auto itLeft = leftChild.begin(); + auto itRight = rightChild.begin(); std::vector resPairs; - while (itLeft != expressionsLeft.end() && itRight != expressionsRight.end()) { + while (itLeft != leftChild.end() && itRight != rightChild.end()) { auto& [exprLeft, varLeft] = *itLeft; auto& [exprRight, varRight] = *itRight; - // The logic that is implemented with this loop. - // - // EXAMPLE 1 - // left child: {(>=5, ?y)} - // right child: {(<=3, ?x)} - // We want to pre-filter the blocks for the expression: ?y >= 5 || ?x <= 3 - // No PrefilterExpression will be returned. - // - // EXAMPLE 2 - // left child: {(>=5, ?x)} - // right child: {(=0, ?x)} - // We want to pre-filter the blocks to the expression: ?x >= 5 || ?x = 0 - // The resulting PrefilterExpression is {((>=5 OR =0), ?x)} - // - // EXAMPLE 3 - // left child: {(>=10, ?x), (!=0, ?y)} - // right child: {(<= 0, ?x)} - // We have to construct a PrefilterExpression for (?x >= 10 && ?y != 0) || - // ?x <= 0. If this OR expression yields true, at least ?x >= 10 || ?x <= 0 - // must be staisifed; for this objective we can construct a - // PrefiterExpression. We can't make a distinct prediction w.r.t. ?y != 0 - // => not relevant for the PrefilterExpression. Thus, we return the - // PrefilterExpresion {((>= 10 OR <= 0), ?x)}. if (varLeft == varRight) { resPairs.emplace_back(std::make_unique( std::move(exprLeft), std::move(exprRight)), @@ -200,36 +201,80 @@ std::optional> mergeChildrenOrLogicImpl( } } if (resPairs.empty()) { + // No PrefilterExpression(s) available with this conjunction merge on the + // contents of left and right child. return std::nullopt; } + pd::checkPropertiesForPrefilterConstruction(resPairs); return resPairs; } +// GET TEMPLATED MERGE FUNCTION (CONJUNCTION AND / OR) +//______________________________________________________________________________ +// getMergeFunction (get the respective merging function) follows the +// principles of De Morgan's law. If this is a child of a NotExpression +// (NOT(!)), we have to swap the combination procedure (swap AND(&&) and +// OR(||) respectively). This equals a partial application of De Morgan's +// law. On the resulting PrefilterExpressions, NOT is applied in +// UnaryNegateExpression(Impl). For more details take a look at the +// implementation in NumericUnaryExpressions.cpp (see +// UnaryNegateExpressionImpl). +// +// EXAMPLE +// - How De-Morgan's law is applied in steps +// !((?y != 10 || ?x = 0) || (?x = 5 || ?x >= 10)) is the complete (Sparql +// expression). With De-Morgan's law applied: +// (?y = 10 && ?x != 0) && (?x != 5 && ?x < 10) +// +// {, ....} represents the PrefilterExpressions +// with their corresponding Variable. +// +// At the current step we have: isNegated = true; because of !(...) (NOT) +// left child: {<(!= 10), ?y>, <(!= 0), ?x>}; (?y != 10 || ?x != 0) +// +// Remark: The (child) expressions of the left and right child have been +// combined with the merging procedure AND +// (mergeChildrenForAndExpressionImpl), +// because with isNegated = true, it is implied that De-Morgan's law is applied. +// Thus, the OR is logically an AND conjunction with its application. +// +// right child: {<((= 5) OR (>= 10)), ?x>} (?x = 5 || ?x >= 10) +// +// isNegated is true, hence we know that we have to partially apply +// De-Morgan's law. Given that, OR switches to an AND. Therefore we use +// mergeChildrenForAndExpressionImpl with OrExpression (PrefilterExpression) +// as a template value (BinaryPrefilterExpr). +// Call mergeChildrenAndExpressionImpl +// with left and right child as args., we get their merged combination: +// {<(!= 10), ?y>, <((!= 0) OR ((= 5) OR (>= 10))), ?x>} +// +// Next their merged value is returned to UnaryNegateExpressionImpl +// (defined in NumericUnaryExpressions.cpp), where for each expression of +// the pairs, NOT (NotExpression) is +// applied. +// {<(!= 10), ?y>, <((!= 0) OR ((= 5) OR (>= 10))), ?x>} +// apply NotExpr.: {, = 10))), ?x>} +// This yields: {<(= 10), ?y>, <((= 0) AND ((!= 5) AND (< 10))), ?x>} +//______________________________________________________________________________ template -constexpr auto getCombineLogic(bool isNegated) { +constexpr auto getMergeFunction(bool isNegated) { if constexpr (std::is_same_v) { - // This follows the principles of De Morgan's law. - // If this is a child of a NotExpression, we have to swap the combination - // procedure (swap AND(&&) and OR(||) respectively). This equals a partial - // application of De Morgan's law. On the resulting PrefilterExpressions, - // NOT is applied in UnaryNegateExpression(Impl). For more details take a - // look at the implementation in NumericUnaryExpressions.cpp - return !isNegated ? mergeChildrenAndLogicImpl - : mergeChildrenOrLogicImpl; + return !isNegated ? mergeChildrenForAndExpressionImpl + : mergeChildrenForOrExpressionImpl; } else { static_assert(std::is_same_v); - return !isNegated ? mergeChildrenOrLogicImpl - : mergeChildrenAndLogicImpl; + return !isNegated ? mergeChildrenForOrExpressionImpl + : mergeChildrenForAndExpressionImpl; } } -} // namespace constructPrefilterExpr + +} // namespace //______________________________________________________________________________ template -requires(isOperation && - prefilterExpressions::check_is_logical_v) +requires isOperation class LogicalBinaryExpressionImpl : public NaryExpression { public: using NaryExpression::NaryExpression; @@ -243,18 +288,24 @@ class LogicalBinaryExpressionImpl : public NaryExpression { auto rightChild = this->getNthChild(1).value()->getPrefilterExpressionForMetadata( isNegated); - return constructPrefilterExpr::getCombineLogic( - isNegated)(std::move(leftChild), std::move(rightChild)); + return constructPrefilterExpr::getMergeFunction( + isNegated)( + leftChild.has_value() ? std::move(leftChild.value()) + : std::vector{}, + rightChild.has_value() ? std::move(rightChild.value()) + : std::vector{}); } }; +} // namespace constructPrefilterExpr + //______________________________________________________________________________ -using AndExpression = LogicalBinaryExpressionImpl< +using AndExpression = constructPrefilterExpr::LogicalBinaryExpressionImpl< prefilterExpressions::AndExpression, Operation<2, FV, SET>>; -using OrExpression = LogicalBinaryExpressionImpl< +using OrExpression = constructPrefilterExpr::LogicalBinaryExpressionImpl< prefilterExpressions::OrExpression, Operation<2, FV, SET>>; diff --git a/src/engine/sparqlExpressions/NumericUnaryExpressions.cpp b/src/engine/sparqlExpressions/NumericUnaryExpressions.cpp index 19a369ffd2..21a606c020 100644 --- a/src/engine/sparqlExpressions/NumericUnaryExpressions.cpp +++ b/src/engine/sparqlExpressions/NumericUnaryExpressions.cpp @@ -31,9 +31,40 @@ class UnaryNegateExpressionImpl : public NaryExpression { using NaryExpression::NaryExpression; std::optional> - getPrefilterExpressionForMetadata( - [[maybe_unused]] bool isNegated) const override { + getPrefilterExpressionForMetadata(bool isNegated) const override { AD_CORRECTNESS_CHECK(this->N == 1); + namespace p = prefilterExpressions; + // The bool flag isNegated (by default false) acts as decision variable + // to select the correct merging procedure while constructing the + // PrefilterExpression(s) for a binary expression (AND or OR). For the + // negation procedure, we apply (partially over multiple Variables) + // De-Morgans law w.r.t. the affected (lower) expression parts, and + // isNegated indicates if we should apply it (or not). For more detailed + // information see NumericBinaryExpressions.cpp. For UnaryNegate we have to + // toggle the value of isNegated to pass the respective negation information + // down the expression tree. + // Remark: + // - Expression sub-tree has an even number of NOT expressions as + // parents: the negation cancels out (isNegated = false). + // - For an uneven number of NOT expressions as parent nodes: the + // sub-tree is actually negated (isNegated = true). + // + // Example - Apply De-Morgans law in two steps (see (1) and (2)) on + // expression !(?x >= IntId(10) || ?y >= IntId(10)) (SparqlExpression) + // With De-Morgan's rule we retrieve: ?x < IntId(10) && ?y < IntId(10) + // + // (1) Merge {<(>= IntId(10)), ?x>} and {<(>= IntId(10)), ?y>} + // with mergeChildrenForAndExpressionImpl (defined in + // NumericBinaryExpressions.cpp), which we select based on isNegated = true + // (first part of De-Morgans law). + // Result (1): {<(>= IntId(10)), ?x>, <(>= IntId(10)), ?y>} + // + // (2) On each pair given the result from + // (1), apply NotExpression (see the following implementation part). + // Step by step for the given example: + // {<(>= IntId(10)), ?x>, <(>= IntId(10)), ?y>} (apply NotExpression) => + // {<(!(>= IntId(10))), ?x>, <(!(>= IntId(10))), ?y>} + // => Result (2): {<(< IntId(10)), ?x>, <(< IntId(10)), ?y>} auto optExprVarVec = this->getNthChild(0).value()->getPrefilterExpressionForMetadata( !isNegated); @@ -41,11 +72,12 @@ class UnaryNegateExpressionImpl : public NaryExpression { return std::nullopt; } std::ranges::for_each( - optExprVarVec.value(), [](PrefilterExprVariablePair& exprVarPair) { - exprVarPair.first = - std::make_unique( - std::move(exprVarPair.first)); + optExprVarVec.value() | std::views::keys, + [](std::unique_ptr& expression) { + expression = + std::make_unique(std::move(expression)); }); + p::detail::checkPropertiesForPrefilterConstruction(optExprVarVec.value()); return optExprVarVec; } }; diff --git a/src/engine/sparqlExpressions/RelationalExpressions.cpp b/src/engine/sparqlExpressions/RelationalExpressions.cpp index 66180b41e5..a7c615aab8 100644 --- a/src/engine/sparqlExpressions/RelationalExpressions.cpp +++ b/src/engine/sparqlExpressions/RelationalExpressions.cpp @@ -414,46 +414,69 @@ template std::optional> RelationalExpression::getPrefilterExpressionForMetadata( [[maybe_unused]] bool isNegated) const { + namespace p = prefilterExpressions; AD_CORRECTNESS_CHECK(children_.size() == 2); const SparqlExpression::Ptr& child0 = children_.at(0); const SparqlExpression::Ptr& child1 = children_.at(1); - const auto checkBinarySearchEvaluable = - [](const SparqlExpression::Ptr& child0, - const SparqlExpression::Ptr& child1) - -> std::optional> { - if (const auto* literalValueId = - dynamic_cast(child0.get())) { - if (const auto* literalVariable = - dynamic_cast(child1.get())) { - return std::make_pair(literalValueId->value(), - literalVariable->value()); + const auto getPrefilterExprVariablePairVec = + [](const Variable& variable, const ValueId valueId, + bool reverse) -> std::vector { + const auto mirroredExpression = + [](const ValueId valueId) -> std::unique_ptr { + using enum Comparison; + switch (comp) { + case LT: + // Id < ?var -> ?var > Id + return std::make_unique(valueId); + case LE: + // Id <= ?var -> ?var >= Id + return std::make_unique(valueId); + case GE: + // Id >= ?var -> ?var <= Id + return std::make_unique(valueId); + case GT: + // Id > ?var -> ?var < Id + return std::make_unique(valueId); + default: + // EQ(==) or NE(!=) + // Given that these two relations are symmetric w.r.t. ?var and Id, + // no swap regarding the relational operator is neccessary. + return std::make_unique>(valueId); } - } - return std::nullopt; - }; - - const auto createPrefilterExprVariablePair = - [](const std::pair& valuePair) - -> std::vector { - std::vector pairVec; - pairVec.emplace_back( - std::make_unique>( - valuePair.first), - valuePair.second); - return pairVec; + }; + std::vector resVec; + resVec.emplace_back(std::make_pair( + reverse ? mirroredExpression(valueId) + : std::make_unique>(valueId), + variable)); + return resVec; }; - // Option 1: child0 is a (constant) reference ValueId, while child1 contains a - // Variable to the respective column on which we want to filter. - const auto& optionalPair1 = checkBinarySearchEvaluable(child0, child1); - if (optionalPair1.has_value()) { - return createPrefilterExprVariablePair(optionalPair1.value()); + // Option 1: + // RelationalExpression containing a VariableExpression as the first child + // and an IdExpression as the second child. + // E.g. for ?x >= 10 (RelationalExpression Sparql), we obtain the following + // pair with PrefilterExpression and Variable: <(>= 10), ?x> + if (const auto* variable = + dynamic_cast(child0.get())) { + if (const auto* valueId = dynamic_cast(child1.get())) { + return getPrefilterExprVariablePairVec(variable->value(), + valueId->value(), false); + } } - // Option 2: child1 is a (constant) reference ValueId, while child0 contains a - // Variable to the respective column on which we want to filter. - const auto& optionalPair2 = checkBinarySearchEvaluable(child1, child0); - if (optionalPair2.has_value()) { - return createPrefilterExprVariablePair(optionalPair2.value()); + // Option 2: + // RelationalExpression containing a IdExpression as the first child and an + // VariableExpression as the second child. + // (1) 10 >= ?x (RelationalExpression Sparql), we obtain the following + // pair with PrefilterExpression and Variable: <(<= 10), ?x> + // (2) 10 != ?x (RelationalExpression Sparql), we obtain the following + // pair with PrefilterExpression and Variable: <(!= 10), ?x> + if (const auto* valueId = dynamic_cast(child0.get())) { + if (const auto* variable = + dynamic_cast(child1.get())) { + return getPrefilterExprVariablePairVec(variable->value(), + valueId->value(), true); + } } return std::nullopt; } diff --git a/src/index/CompressedBlockPrefiltering.cpp b/src/index/CompressedBlockPrefiltering.cpp index 75182fd61f..ab75064463 100644 --- a/src/index/CompressedBlockPrefiltering.cpp +++ b/src/index/CompressedBlockPrefiltering.cpp @@ -4,6 +4,8 @@ #include "index/CompressedBlockPrefiltering.h" +#include + #include "global/ValueIdComparators.h" namespace prefilterExpressions { @@ -347,4 +349,26 @@ std::string NotExpression::info(size_t depth) const { return stream.str(); } +namespace detail { +//______________________________________________________________________________ +void checkPropertiesForPrefilterConstruction( + const std::vector& vec) { + if (!std::ranges::is_sorted(vec, [](const auto& pair1, const auto& pair2) { + return pair1.second < pair2.second; + })) { + throw std::runtime_error( + "The vector must contain the pairs in " + "sorted order w.r.t. Variable value."); + }; + if (auto it = std::ranges::adjacent_find( + vec, [](const auto& pair1, + const auto& pair2) { return pair1.second == pair2.second; }); + it != vec.end()) { + throw std::runtime_error( + "For each relevant Variable must exist exactly one " + " pair."); + } +}; + +} // namespace detail } // namespace prefilterExpressions diff --git a/src/index/CompressedBlockPrefiltering.h b/src/index/CompressedBlockPrefiltering.h index 08fa9092c7..998b302f35 100644 --- a/src/index/CompressedBlockPrefiltering.h +++ b/src/index/CompressedBlockPrefiltering.h @@ -196,19 +196,18 @@ using AndExpression = prefilterExpressions::LogicalExpression< using OrExpression = prefilterExpressions::LogicalExpression< prefilterExpressions::LogicalOperators::OR>; +namespace detail { //______________________________________________________________________________ -// Helpers to check for the respective type of `PrefilterExpression` -template -struct check_is : std::false_type {}; - -template -struct check_is> : std::true_type {}; -template -struct check_is> : std::true_type {}; - -template -constexpr bool check_is_logical_v = check_is::value; -template -constexpr bool check_is_relational_v = check_is::value; - +// Pair containing a `PrefilterExpression` and its corresponding `Variable`. +using PrefilterExprVariablePair = + std::pair, Variable>; +//______________________________________________________________________________ +// Helper function to perform a check regarding the following conditions. +// (1) For each relevant Variable, the vector contains exactly one pair. +// (2) The vector contains those pairs (``) in +// sorted order w.r.t. the Variable value. +void checkPropertiesForPrefilterConstruction( + const std::vector& vec); + +} // namespace detail } // namespace prefilterExpressions diff --git a/src/parser/data/Variable.h b/src/parser/data/Variable.h index ffa39b5e12..a378cfd903 100644 --- a/src/parser/data/Variable.h +++ b/src/parser/data/Variable.h @@ -57,9 +57,7 @@ class Variable { bool operator==(const Variable&) const = default; // The construction of PrefilterExpressions requires a defined < order. - bool operator<(const Variable& other) const { - return this->_name < other._name; - }; + bool operator<(const Variable& other) const { return _name < other._name; }; // Make the type hashable for absl, see // https://abseil.io/docs/cpp/guides/hash. diff --git a/test/CompressedBlockPrefilteringTest.cpp b/test/CompressedBlockPrefilteringTest.cpp index 8529b68df2..80412184fe 100644 --- a/test/CompressedBlockPrefilteringTest.cpp +++ b/test/CompressedBlockPrefilteringTest.cpp @@ -806,15 +806,54 @@ TEST(SparqlExpression, testGetPrefilterExpressionDefault) { TEST(SparqlExpression, getPrefilterExpressionFromSparqlRelational) { const TestDates dt{}; const Variable var = Variable{"?x"}; + // ?x == BooldId(true) (RelationalExpression Sparql) + // expected: <(== Boold(true)), ?x> (PrefilterExpression, Variable) evalAndEqualityCheck(eqSprql(var, BoolId(true)), pr(eq(BoolId(true)), var)); + // For BoolId(true) == ?x we expect the same PrefilterExpression pair. + evalAndEqualityCheck(eqSprql(BoolId(true), var), pr(eq(BoolId(true)), var)); + // ?x != BooldId(true) (RelationalExpression Sparql) + // expected: <(!= Boold(true)), ?x> (PrefilterExpression, Variable) + evalAndEqualityCheck(neqSprql(var, BoolId(false)), + pr(neq(BoolId(false)), var)); + // Same expected value for BoolId(true) != ?x. + evalAndEqualityCheck(neqSprql(BoolId(false), var), + pr(neq(BoolId(false)), var)); + // ?x >= IntId(1) + // expected: <(>= IntId(1)), ?x> evalAndEqualityCheck(geSprql(var, IntId(1)), pr(ge(IntId(1)), var)); + // IntId(1) <= ?x + // expected: <(>= IntId(1)), ?x> + evalAndEqualityCheck(leSprql(IntId(1), var), pr(ge(IntId(1)), var)); + // ?x > IntId(1) + // expected: <(> IntId(1)), ?x> + evalAndEqualityCheck(gtSprql(var, IntId(1)), pr(gt(IntId(1)), var)); + // VocabId(10) != ?x + // expected: <(!= VocabId(10)), ?x> evalAndEqualityCheck(neqSprql(VocabId(10), var), pr(neq(VocabId(10)), var)); + // BlankNodeId(1) > ?x + // expected: <(< BlankNodeId(1)), ?x> evalAndEqualityCheck(geSprql(BlankNodeId(1), var), - pr(ge(BlankNodeId(1)), var)); + pr(le(BlankNodeId(1)), var)); + // ?x < BlankNodeId(1) + // expected: <(< BlankNodeId(1)), ?x> + evalAndEqualityCheck(ltSprql(var, BlankNodeId(1)), + pr(lt(BlankNodeId(1)), var)); + // ?x <= referenceDate1 + // expected: <(<= referenceDate1), ?x> evalAndEqualityCheck(leSprql(var, dt.referenceDate1), pr(le(dt.referenceDate1), var)); + // referenceDate1 >= ?x + // expected: <(<= referenceDate1), ?x> + evalAndEqualityCheck(geSprql(dt.referenceDate1, var), + pr(le(dt.referenceDate1), var)); + // DoubleId(10.2) < ?x + // expected: <(> DoubleId(10.2)), ?x> evalAndEqualityCheck(ltSprql(DoubleId(10.2), var), - pr(lt(DoubleId(10.2)), var)); + pr(gt(DoubleId(10.2)), var)); + // ?x > DoubleId(10.2) + // expected: <(> DoubleId(10.2)), ?x> + evalAndEqualityCheck(gtSprql(var, DoubleId(10.2)), + pr(gt(DoubleId(10.2)), var)); } //______________________________________________________________________________ @@ -984,7 +1023,7 @@ TEST(SparqlExpression, getPrefilterExpressionsToComplexSparqlExpressions) { evalAndEqualityCheck( orSprqlExpr(geSprql(varX, IntId(10)), andSprqlExpr(geSprql(varX, IntId(-10)), - ltSprql(DoubleId(0.00), varX))), + ltSprql(varX, DoubleId(0.00)))), pr(orExpr(ge(IntId(10)), andExpr(ge(IntId(-10)), lt(DoubleId(0.00)))), varX)); // !(!(?x >= 10) OR !!(?x >= -10 AND ?x < 0.00)) @@ -994,7 +1033,7 @@ TEST(SparqlExpression, getPrefilterExpressionsToComplexSparqlExpressions) { notSprqlExpr(orSprqlExpr( notSprqlExpr(geSprql(varX, IntId(10))), notSprqlExpr(notSprqlExpr(andSprqlExpr( - geSprql(varX, IntId(-10)), ltSprql(DoubleId(0.00), varX)))))), + geSprql(varX, IntId(-10)), ltSprql(varX, DoubleId(0.00))))))), pr(notExpr(orExpr( notExpr(ge(IntId(10))), notExpr(notExpr(andExpr(ge(IntId(-10)), lt(DoubleId(0.00))))))), @@ -1113,3 +1152,39 @@ TEST(SparqlExpression, getEmptyPrefilterForMoreComplexSparqlExpressions) { andSprqlExpr(eqSprql(varZ, BoolId(true)), eqSprql(Variable{"?country"}, VocabId(20)))))))); } + +// Test that the conditions required for a correct merge of child +// PrefilterExpressions are properly checked during the PrefilterExpression +// construction procedure. This check is applied in the SparqlExpression (for +// NOT, AND and OR) counter-expressions, while constructing their corresponding +// PrefilterExpression. +//______________________________________________________________________________ +TEST(SparqlExpression, checkPropertiesForPrefilterConstruction) { + namespace pd = prefilterExpressions::detail; + const Variable varX = Variable{"?x"}; + const Variable varY = Variable{"?y"}; + const Variable varZ = Variable{"?z"}; + const Variable varW = Variable{"?w"}; + std::vector vec{}; + vec.push_back(pr(andExpr(lt(IntId(5)), gt(DoubleId(-0.01))), varX)); + vec.push_back(pr(gt(VocabId(0)), varY)); + EXPECT_NO_THROW(pd::checkPropertiesForPrefilterConstruction(vec)); + vec.push_back(pr(eq(VocabId(33)), varZ)); + EXPECT_NO_THROW(pd::checkPropertiesForPrefilterConstruction(vec)); + // Add a pair with duplicate Variable. + vec.push_back(pr(gt(VocabId(0)), varZ)); + AD_EXPECT_THROW_WITH_MESSAGE( + pd::checkPropertiesForPrefilterConstruction(vec), + ::testing::HasSubstr("For each relevant Variable must exist exactly " + "one pair.")); + // Remove the last two pairs and add a pair + // which violates the order on Variable(s). + vec.pop_back(); + vec.pop_back(); + vec.push_back(pr(eq(VocabId(0)), varW)); + AD_EXPECT_THROW_WITH_MESSAGE( + pd::checkPropertiesForPrefilterConstruction(vec), + ::testing::HasSubstr( + "The vector must contain the " + "pairs in sorted order w.r.t. Variable value.")); +} From d65acc34392e92c52979c0eef68024a8f63a83e0 Mon Sep 17 00:00:00 2001 From: realHannes Date: Wed, 30 Oct 2024 16:05:46 +0100 Subject: [PATCH 45/81] correction for codespell check --- src/engine/sparqlExpressions/NumericBinaryExpressions.cpp | 2 +- src/engine/sparqlExpressions/RelationalExpressions.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/engine/sparqlExpressions/NumericBinaryExpressions.cpp b/src/engine/sparqlExpressions/NumericBinaryExpressions.cpp index 7ffa773a53..8afb6b7593 100644 --- a/src/engine/sparqlExpressions/NumericBinaryExpressions.cpp +++ b/src/engine/sparqlExpressions/NumericBinaryExpressions.cpp @@ -73,7 +73,7 @@ namespace { // MERGE AND CONJUNCTION // _____________________________________________________________________________ // For our pre-filtering logic over index scans, we need exactly one -// corresponding PrefilterExpression for each relavant Variable. Thus, if the +// corresponding PrefilterExpression for each relevant Variable. Thus, if the // left and right child contain a PrefilterExpression w.r.t. the same Variable, // combine them here for an AND conjunction. In the following, three examples // are given on how the following function merges the content of the left and diff --git a/src/engine/sparqlExpressions/RelationalExpressions.cpp b/src/engine/sparqlExpressions/RelationalExpressions.cpp index a7c615aab8..4324a2cc06 100644 --- a/src/engine/sparqlExpressions/RelationalExpressions.cpp +++ b/src/engine/sparqlExpressions/RelationalExpressions.cpp @@ -441,7 +441,7 @@ RelationalExpression::getPrefilterExpressionForMetadata( default: // EQ(==) or NE(!=) // Given that these two relations are symmetric w.r.t. ?var and Id, - // no swap regarding the relational operator is neccessary. + // no swap regarding the relational operator is necessary. return std::make_unique>(valueId); } }; From f85a96f11564be55bffefd062d149d4392f3e53d Mon Sep 17 00:00:00 2001 From: realHannes Date: Wed, 30 Oct 2024 16:08:08 +0100 Subject: [PATCH 46/81] fix for codespell --- test/CompressedBlockPrefilteringTest.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/CompressedBlockPrefilteringTest.cpp b/test/CompressedBlockPrefilteringTest.cpp index 80412184fe..b9d17b3713 100644 --- a/test/CompressedBlockPrefilteringTest.cpp +++ b/test/CompressedBlockPrefilteringTest.cpp @@ -807,12 +807,12 @@ TEST(SparqlExpression, getPrefilterExpressionFromSparqlRelational) { const TestDates dt{}; const Variable var = Variable{"?x"}; // ?x == BooldId(true) (RelationalExpression Sparql) - // expected: <(== Boold(true)), ?x> (PrefilterExpression, Variable) + // expected: <(== BoolId(true)), ?x> (PrefilterExpression, Variable) evalAndEqualityCheck(eqSprql(var, BoolId(true)), pr(eq(BoolId(true)), var)); // For BoolId(true) == ?x we expect the same PrefilterExpression pair. evalAndEqualityCheck(eqSprql(BoolId(true), var), pr(eq(BoolId(true)), var)); // ?x != BooldId(true) (RelationalExpression Sparql) - // expected: <(!= Boold(true)), ?x> (PrefilterExpression, Variable) + // expected: <(!= BoolId(true)), ?x> (PrefilterExpression, Variable) evalAndEqualityCheck(neqSprql(var, BoolId(false)), pr(neq(BoolId(false)), var)); // Same expected value for BoolId(true) != ?x. From 5f4e586fb7c6d7689ede18cf218fee564ad57d1f Mon Sep 17 00:00:00 2001 From: realHannes Date: Thu, 31 Oct 2024 13:10:58 +0100 Subject: [PATCH 47/81] remove optionality (std::optional) for return value --- .../NumericBinaryExpressions.cpp | 26 +--- .../NumericUnaryExpressions.cpp | 15 +-- .../RelationalExpressions.cpp | 4 +- .../sparqlExpressions/RelationalExpressions.h | 3 +- .../sparqlExpressions/SparqlExpression.cpp | 4 +- .../sparqlExpressions/SparqlExpression.h | 2 +- .../SparqlExpressionPimpl.cpp | 2 +- .../sparqlExpressions/SparqlExpressionPimpl.h | 4 +- test/CompressedBlockPrefilteringTest.cpp | 127 ++++++++---------- 9 files changed, 73 insertions(+), 114 deletions(-) diff --git a/src/engine/sparqlExpressions/NumericBinaryExpressions.cpp b/src/engine/sparqlExpressions/NumericBinaryExpressions.cpp index 8afb6b7593..7f8319e17c 100644 --- a/src/engine/sparqlExpressions/NumericBinaryExpressions.cpp +++ b/src/engine/sparqlExpressions/NumericBinaryExpressions.cpp @@ -99,8 +99,7 @@ namespace { // Returns {<((>= 10 AND <= 20) AND != 15), ?x>, <(= 10), ?y>} //______________________________________________________________________________ template -std::optional> -mergeChildrenForAndExpressionImpl( +std::vector mergeChildrenForAndExpressionImpl( std::vector&& leftChild, std::vector&& rightChild) { namespace pd = prefilterExpressions::detail; @@ -131,11 +130,6 @@ mergeChildrenForAndExpressionImpl( } std::ranges::move(itLeft, leftChild.end(), std::back_inserter(resPairs)); std::ranges::move(itRight, rightChild.end(), std::back_inserter(resPairs)); - if (resPairs.empty()) { - // No PrefilterExpression(s) available with this conjunction merge on the - // contents of left and right child. - return std::nullopt; - } pd::checkPropertiesForPrefilterConstruction(resPairs); return resPairs; } @@ -175,8 +169,7 @@ mergeChildrenForAndExpressionImpl( // PrefilterExpresion {<(>= 10 OR <= 0), ?x>}. //______________________________________________________________________________ template -std::optional> -mergeChildrenForOrExpressionImpl( +std::vector mergeChildrenForOrExpressionImpl( std::vector&& leftChild, std::vector&& rightChild) { namespace pd = prefilterExpressions::detail; @@ -200,11 +193,6 @@ mergeChildrenForOrExpressionImpl( ++itRight; } } - if (resPairs.empty()) { - // No PrefilterExpression(s) available with this conjunction merge on the - // contents of left and right child. - return std::nullopt; - } pd::checkPropertiesForPrefilterConstruction(resPairs); return resPairs; } @@ -279,8 +267,8 @@ class LogicalBinaryExpressionImpl : public NaryExpression { public: using NaryExpression::NaryExpression; - std::optional> - getPrefilterExpressionForMetadata(bool isNegated) const override { + std::vector getPrefilterExpressionForMetadata( + bool isNegated) const override { AD_CORRECTNESS_CHECK(this->N == 2); auto leftChild = this->getNthChild(0).value()->getPrefilterExpressionForMetadata( @@ -289,11 +277,7 @@ class LogicalBinaryExpressionImpl : public NaryExpression { this->getNthChild(1).value()->getPrefilterExpressionForMetadata( isNegated); return constructPrefilterExpr::getMergeFunction( - isNegated)( - leftChild.has_value() ? std::move(leftChild.value()) - : std::vector{}, - rightChild.has_value() ? std::move(rightChild.value()) - : std::vector{}); + isNegated)(std::move(leftChild), std::move(rightChild)); } }; diff --git a/src/engine/sparqlExpressions/NumericUnaryExpressions.cpp b/src/engine/sparqlExpressions/NumericUnaryExpressions.cpp index 21a606c020..cb72da969a 100644 --- a/src/engine/sparqlExpressions/NumericUnaryExpressions.cpp +++ b/src/engine/sparqlExpressions/NumericUnaryExpressions.cpp @@ -30,8 +30,8 @@ class UnaryNegateExpressionImpl : public NaryExpression { public: using NaryExpression::NaryExpression; - std::optional> - getPrefilterExpressionForMetadata(bool isNegated) const override { + std::vector getPrefilterExpressionForMetadata( + bool isNegated) const override { AD_CORRECTNESS_CHECK(this->N == 1); namespace p = prefilterExpressions; // The bool flag isNegated (by default false) acts as decision variable @@ -65,20 +65,17 @@ class UnaryNegateExpressionImpl : public NaryExpression { // {<(>= IntId(10)), ?x>, <(>= IntId(10)), ?y>} (apply NotExpression) => // {<(!(>= IntId(10))), ?x>, <(!(>= IntId(10))), ?y>} // => Result (2): {<(< IntId(10)), ?x>, <(< IntId(10)), ?y>} - auto optExprVarVec = + auto child = this->getNthChild(0).value()->getPrefilterExpressionForMetadata( !isNegated); - if (!optExprVarVec.has_value()) { - return std::nullopt; - } std::ranges::for_each( - optExprVarVec.value() | std::views::keys, + child | std::views::keys, [](std::unique_ptr& expression) { expression = std::make_unique(std::move(expression)); }); - p::detail::checkPropertiesForPrefilterConstruction(optExprVarVec.value()); - return optExprVarVec; + p::detail::checkPropertiesForPrefilterConstruction(child); + return child; } }; diff --git a/src/engine/sparqlExpressions/RelationalExpressions.cpp b/src/engine/sparqlExpressions/RelationalExpressions.cpp index 4324a2cc06..fde2ad1490 100644 --- a/src/engine/sparqlExpressions/RelationalExpressions.cpp +++ b/src/engine/sparqlExpressions/RelationalExpressions.cpp @@ -411,7 +411,7 @@ ExpressionResult InExpression::evaluate( // _____________________________________________________________________________ template -std::optional> +std::vector RelationalExpression::getPrefilterExpressionForMetadata( [[maybe_unused]] bool isNegated) const { namespace p = prefilterExpressions; @@ -478,7 +478,7 @@ RelationalExpression::getPrefilterExpressionForMetadata( valueId->value(), true); } } - return std::nullopt; + return {}; } // _____________________________________________________________________________ diff --git a/src/engine/sparqlExpressions/RelationalExpressions.h b/src/engine/sparqlExpressions/RelationalExpressions.h index 6448435c0c..d406afc936 100644 --- a/src/engine/sparqlExpressions/RelationalExpressions.h +++ b/src/engine/sparqlExpressions/RelationalExpressions.h @@ -42,8 +42,7 @@ class RelationalExpression : public SparqlExpression { // corresponding `PrefilterExpression` for the pre-filtering procedure on // `CompressedBlockMetadata`. In addition we return the `Variable` that // corresponds to the sorted column. - std::optional> - getPrefilterExpressionForMetadata( + std::vector getPrefilterExpressionForMetadata( [[maybe_unused]] bool isNegated) const override; // These expressions are typically used inside `FILTER` clauses, so we need diff --git a/src/engine/sparqlExpressions/SparqlExpression.cpp b/src/engine/sparqlExpressions/SparqlExpression.cpp index 4482bf7182..dab49835a8 100644 --- a/src/engine/sparqlExpressions/SparqlExpression.cpp +++ b/src/engine/sparqlExpressions/SparqlExpression.cpp @@ -109,10 +109,10 @@ Estimates SparqlExpression::getEstimatesForFilterExpression( } // _____________________________________________________________________________ -std::optional> +std::vector SparqlExpression::getPrefilterExpressionForMetadata( [[maybe_unused]] bool isNegated) const { - return std::nullopt; + return {}; }; // _____________________________________________________________________________ diff --git a/src/engine/sparqlExpressions/SparqlExpression.h b/src/engine/sparqlExpressions/SparqlExpression.h index a864a809b5..83b3ff89ad 100644 --- a/src/engine/sparqlExpressions/SparqlExpression.h +++ b/src/engine/sparqlExpressions/SparqlExpression.h @@ -101,7 +101,7 @@ class SparqlExpression { // column is required to be sorted, and hence is as a consequence also binary // evaluable regarding the relational (e.g. `>=`) / logical (`&&`, `||` and // `!`) expressions. - virtual std::optional> + virtual std::vector getPrefilterExpressionForMetadata( [[maybe_unused]] bool isNegated = false) const; diff --git a/src/engine/sparqlExpressions/SparqlExpressionPimpl.cpp b/src/engine/sparqlExpressions/SparqlExpressionPimpl.cpp index 8a05ec147d..39dd932231 100644 --- a/src/engine/sparqlExpressions/SparqlExpressionPimpl.cpp +++ b/src/engine/sparqlExpressions/SparqlExpressionPimpl.cpp @@ -93,7 +93,7 @@ auto SparqlExpressionPimpl::getEstimatesForFilterExpression( } //_____________________________________________________________________________ -std::optional> +std::vector SparqlExpressionPimpl::getPrefilterExpressionForMetadata(bool isNegated) const { return _pimpl->getPrefilterExpressionForMetadata(isNegated); } diff --git a/src/engine/sparqlExpressions/SparqlExpressionPimpl.h b/src/engine/sparqlExpressions/SparqlExpressionPimpl.h index 80a7766260..48ee2b2407 100644 --- a/src/engine/sparqlExpressions/SparqlExpressionPimpl.h +++ b/src/engine/sparqlExpressions/SparqlExpressionPimpl.h @@ -118,8 +118,8 @@ class SparqlExpressionPimpl { uint64_t inputSizeEstimate, const std::optional& primarySortKeyVariable); - std::optional> - getPrefilterExpressionForMetadata(bool isNegated = false) const; + std::vector getPrefilterExpressionForMetadata( + bool isNegated = false) const; SparqlExpression* getPimpl() { return _pimpl.get(); } [[nodiscard]] const SparqlExpression* getPimpl() const { diff --git a/test/CompressedBlockPrefilteringTest.cpp b/test/CompressedBlockPrefilteringTest.cpp index b9d17b3713..995a627791 100644 --- a/test/CompressedBlockPrefilteringTest.cpp +++ b/test/CompressedBlockPrefilteringTest.cpp @@ -576,7 +576,6 @@ TEST_F(TestPrefilterExprOnBlockMetadata, testWithOneBlockMetadataValue) { namespace { using namespace sparqlExpression; -using optPrefilterVec = std::optional>; using Literal = ad_utility::triple_component::Literal; using Iri = ad_utility::triple_component::Iri; using MakeBinarySprqlExprFunc = std::function& result, - const std::vector& expected) -> bool { - if (result.size() != expected.size()) { - ADD_FAILURE() << "Expected vectors (result vs. expected) of equal length"; - return false; - }; + const std::vector& expected) -> void { + ASSERT_EQ(result.size(), expected.size()); const auto isEqualImpl = [](const PrefilterExprVariablePair& resPair, const PrefilterExprVariablePair& expPair) { if (*resPair.first != *expPair.first || resPair.second != expPair.second) { @@ -687,46 +684,26 @@ const auto checkVectorPrefilterExprVariablePair = } return true; }; - return std::equal(result.begin(), result.end(), expected.begin(), - isEqualImpl); -}; - -//______________________________________________________________________________ -const auto checkEqualityPrefilterMethodT = - [](const optPrefilterVec& result, const optPrefilterVec& expected) { - const auto testEquality = - [](const optPrefilterVec& result, const optPrefilterVec& expected) { - if (!result.has_value() && !expected.has_value()) { - return true; - } else if (result.has_value() && expected.has_value()) { - return checkVectorPrefilterExprVariablePair(result.value(), - expected.value()); - } else { - ADD_FAILURE() - << "Expected both values to either contain a value or to be " - "std::optional."; - return false; - } - }; - ASSERT_TRUE(testEquality(result, expected)); - }; - -//______________________________________________________________________________ -const auto evalToEmptyCheck = [](std::unique_ptr sparqlExpr) { - std::vector prefilterVarPair; - checkEqualityPrefilterMethodT(sparqlExpr->getPrefilterExpressionForMetadata(), - std::nullopt); + ASSERT_TRUE( + std::equal(result.begin(), result.end(), expected.begin(), isEqualImpl)); }; //______________________________________________________________________________ +// `evalAndEqualityCheck` evaluates the provided `SparqlExpression` and checks +// in the following if the resulting vector contains the same +// `` pairs in the correct order. If no +// `` pair is provided, the expected value for +// the `SparqlExpression` is an empty vector. const auto evalAndEqualityCheck = [](std::unique_ptr sparqlExpr, std::convertible_to auto&&... prefilterArgs) { - std::vector prefilterVarPair; - (prefilterVarPair.emplace_back( - std::forward(prefilterArgs)), - ...); - checkEqualityPrefilterMethodT( + std::vector prefilterVarPair = {}; + if constexpr (sizeof...(prefilterArgs) > 0) { + (prefilterVarPair.emplace_back( + std::forward(prefilterArgs)), + ...); + } + equalityCheckPrefilterVectors( sparqlExpr->getPrefilterExpressionForMetadata(), std::move(prefilterVarPair)); }; @@ -787,15 +764,17 @@ TEST(PrefilterExpression, checkPrintFormattedPrefilterExpression) { // Test coverage for the default implementation of // getPrefilterExpressionForMetadata. TEST(SparqlExpression, testGetPrefilterExpressionDefault) { - evalToEmptyCheck(makeUnaryMinusExpression(makeLiteralSparqlExpr(IntId(0)))); - evalToEmptyCheck(makeMultiplyExpression(makeLiteralSparqlExpr(DoubleId(11)), - makeLiteralSparqlExpr(DoubleId(3)))); - evalToEmptyCheck( + evalAndEqualityCheck( + makeUnaryMinusExpression(makeLiteralSparqlExpr(IntId(0)))); + evalAndEqualityCheck(makeMultiplyExpression( + makeLiteralSparqlExpr(DoubleId(11)), makeLiteralSparqlExpr(DoubleId(3)))); + evalAndEqualityCheck( makeStrEndsExpression(makeLiteralSparqlExpr(L("\"Freiburg\"")), makeLiteralSparqlExpr(L("\"burg\"")))); - evalToEmptyCheck(makeIsIriExpression(makeLiteralSparqlExpr(I("")))); - evalToEmptyCheck(makeLogExpression(makeLiteralSparqlExpr(DoubleId(8)))); - evalToEmptyCheck( + evalAndEqualityCheck( + makeIsIriExpression(makeLiteralSparqlExpr(I("")))); + evalAndEqualityCheck(makeLogExpression(makeLiteralSparqlExpr(DoubleId(8)))); + evalAndEqualityCheck( makeStrIriDtExpression(makeLiteralSparqlExpr(L("\"test\"")), makeLiteralSparqlExpr(I("")))); } @@ -1055,17 +1034,17 @@ TEST(SparqlExpression, getEmptyPrefilterFromSparqlRelational) { const Variable var = Variable{"?x"}; const Iri iri = I(""); const Literal lit = L("\"lit\""); - evalToEmptyCheck(leSprql(var, var)); - evalToEmptyCheck(neqSprql(iri, var)); - evalToEmptyCheck(eqSprql(var, iri)); - evalToEmptyCheck(neqSprql(IntId(10), DoubleId(23.3))); - evalToEmptyCheck(gtSprql(DoubleId(10), lit)); - evalToEmptyCheck(ltSprql(VocabId(10), BoolId(10))); - evalToEmptyCheck(geSprql(lit, lit)); - evalToEmptyCheck(eqSprql(iri, iri)); - evalToEmptyCheck(orSprqlExpr(eqSprql(var, var), gtSprql(var, IntId(0)))); - evalToEmptyCheck(orSprqlExpr(eqSprql(var, var), gtSprql(var, var))); - evalToEmptyCheck(andSprqlExpr(eqSprql(var, var), gtSprql(var, var))); + evalAndEqualityCheck(leSprql(var, var)); + evalAndEqualityCheck(neqSprql(iri, var)); + evalAndEqualityCheck(eqSprql(var, iri)); + evalAndEqualityCheck(neqSprql(IntId(10), DoubleId(23.3))); + evalAndEqualityCheck(gtSprql(DoubleId(10), lit)); + evalAndEqualityCheck(ltSprql(VocabId(10), BoolId(10))); + evalAndEqualityCheck(geSprql(lit, lit)); + evalAndEqualityCheck(eqSprql(iri, iri)); + evalAndEqualityCheck(orSprqlExpr(eqSprql(var, var), gtSprql(var, IntId(0)))); + evalAndEqualityCheck(orSprqlExpr(eqSprql(var, var), gtSprql(var, var))); + evalAndEqualityCheck(andSprqlExpr(eqSprql(var, var), gtSprql(var, var))); } //______________________________________________________________________________ @@ -1076,68 +1055,68 @@ TEST(SparqlExpression, getEmptyPrefilterForMoreComplexSparqlExpressions) { const Variable varY = Variable{"?y"}; const Variable varZ = Variable{"?z"}; // ?x <= 10.00 OR ?y > 10 - evalToEmptyCheck( + evalAndEqualityCheck( orSprqlExpr(leSprql(DoubleId(10), varX), gtSprql(IntId(10), varY))); // ?x >= VocabId(23) OR ?z == VocabId(1) - evalToEmptyCheck( + evalAndEqualityCheck( orSprqlExpr(geSprql(varX, VocabId(23)), eqSprql(varZ, VocabId(1)))); // (?x < VocabId(10) OR ?z <= VocabId(4)) OR ?z != 5.00 - evalToEmptyCheck(orSprqlExpr( + evalAndEqualityCheck(orSprqlExpr( orSprqlExpr(ltSprql(varX, VocabId(10)), leSprql(VocabId(4), varZ)), neqSprql(varZ, DoubleId(5)))); // !(?z > 10.20 AND ?x < 0.001) // is equal to // ?z <= 10.20 OR ?x >= 0.001 - evalToEmptyCheck(notSprqlExpr(andSprqlExpr(gtSprql(DoubleId(10.2), varZ), - ltSprql(DoubleId(0.001), varX)))); + evalAndEqualityCheck(notSprqlExpr(andSprqlExpr( + gtSprql(DoubleId(10.2), varZ), ltSprql(DoubleId(0.001), varX)))); // !(?x > 10.20 AND ?z != VocabId(22)) // is equal to // ?x <= 10.20 OR ?z == VocabId(22) - evalToEmptyCheck(notSprqlExpr(andSprqlExpr(gtSprql(DoubleId(10.2), varX), - neqSprql(VocabId(22), varZ)))); + evalAndEqualityCheck(notSprqlExpr(andSprqlExpr(gtSprql(DoubleId(10.2), varX), + neqSprql(VocabId(22), varZ)))); // !(!((?x < VocabId(10) OR ?x <= VocabId(4)) OR ?z != 5.00)) // is equal to // (?x < VocabId(10) OR ?x <= VocabId(4)) OR ?z != 5.00 - evalToEmptyCheck(notSprqlExpr(notSprqlExpr(orSprqlExpr( + evalAndEqualityCheck(notSprqlExpr(notSprqlExpr(orSprqlExpr( orSprqlExpr(ltSprql(varX, VocabId(10)), leSprql(VocabId(4), varX)), neqSprql(varZ, DoubleId(5)))))); // !(?x != 10 AND !(?y >= 10.00 OR ?z <= 10)) // is equal to // ?x == 10 OR ?y >= 10.00 OR ?z <= 10 - evalToEmptyCheck(notSprqlExpr( + evalAndEqualityCheck(notSprqlExpr( andSprqlExpr(neqSprql(varX, IntId(10)), notSprqlExpr(orSprqlExpr(geSprql(varY, DoubleId(10.00)), leSprql(varZ, IntId(10))))))); // !((?x != 10 AND ?z != 10) AND (?y == 10 AND ?x >= 20)) // is equal to //?x == 10 OR ?z == 10 OR ?y != 10 OR ?x < 20 - evalToEmptyCheck(notSprqlExpr(andSprqlExpr( + evalAndEqualityCheck(notSprqlExpr(andSprqlExpr( andSprqlExpr(neqSprql(varX, IntId(10)), neqSprql(varZ, IntId(10))), andSprqlExpr(eqSprql(varY, IntId(10)), geSprql(varX, IntId(20)))))); // !(?z >= 40 AND (?z != 10.00 AND ?y != VocabId(1))) // is equal to // ?z <= 40 OR ?z == 10.00 OR ?y == VocabId(1) - evalToEmptyCheck(notSprqlExpr(andSprqlExpr( + evalAndEqualityCheck(notSprqlExpr(andSprqlExpr( geSprql(varZ, IntId(40)), andSprqlExpr(neqSprql(varZ, DoubleId(10.00)), neqSprql(varY, VocabId(1)))))); // ?z <= true OR !(?x == 10 AND ?y == 10) // is equal to // ?z <= true OR ?x != 10 OR ?y != 10 - evalToEmptyCheck( + evalAndEqualityCheck( orSprqlExpr(leSprql(varZ, BoolId(true)), notSprqlExpr(andSprqlExpr(eqSprql(varX, IntId(10)), eqSprql(IntId(10), varY))))); // !(!(?z <= true OR !(?x == 10 AND ?y == 10))) // is equal to // ?z <= true OR ?x != 10 OR ?y != 10 - evalToEmptyCheck(notSprqlExpr(notSprqlExpr( + evalAndEqualityCheck(notSprqlExpr(notSprqlExpr( orSprqlExpr(leSprql(varZ, BoolId(true)), notSprqlExpr(andSprqlExpr(eqSprql(varX, IntId(10)), eqSprql(IntId(10), varY))))))); // !(!(?x != 10 OR !(?y >= 10.00 AND ?z <= 10))) // is equal to // ?x != 10 OR ?y < 10.00 OR ?z > 10 - evalToEmptyCheck(notSprqlExpr(notSprqlExpr( + evalAndEqualityCheck(notSprqlExpr(notSprqlExpr( orSprqlExpr(neqSprql(varX, IntId(10)), notSprqlExpr(andSprqlExpr(geSprql(varY, DoubleId(10.00)), leSprql(varZ, IntId(10)))))))); @@ -1145,7 +1124,7 @@ TEST(SparqlExpression, getEmptyPrefilterForMoreComplexSparqlExpressions) { // VocabId(20)))) // is equal to // ?x == VocabId(10) OR ?y >= 25 OR ?z == true AND ?country == VocabId(20) - evalToEmptyCheck(notSprqlExpr(andSprqlExpr( + evalAndEqualityCheck(notSprqlExpr(andSprqlExpr( notSprqlExpr( orSprqlExpr(eqSprql(varX, VocabId(10)), geSprql(varY, IntId(25)))), notSprqlExpr(notSprqlExpr( From c06de607c9408d1d159bb3c616567062fa1c61fd Mon Sep 17 00:00:00 2001 From: realHannes Date: Mon, 4 Nov 2024 20:39:03 +0100 Subject: [PATCH 48/81] implement proposed improvements (4) --- .../RelationalExpressions.cpp | 104 +++++++++--------- src/index/CompressedBlockPrefiltering.cpp | 67 ++++++++--- src/index/CompressedBlockPrefiltering.h | 12 +- test/CompressedBlockPrefilteringTest.cpp | 20 +++- 4 files changed, 125 insertions(+), 78 deletions(-) diff --git a/src/engine/sparqlExpressions/RelationalExpressions.cpp b/src/engine/sparqlExpressions/RelationalExpressions.cpp index fde2ad1490..de035dce71 100644 --- a/src/engine/sparqlExpressions/RelationalExpressions.cpp +++ b/src/engine/sparqlExpressions/RelationalExpressions.cpp @@ -416,69 +416,75 @@ RelationalExpression::getPrefilterExpressionForMetadata( [[maybe_unused]] bool isNegated) const { namespace p = prefilterExpressions; AD_CORRECTNESS_CHECK(children_.size() == 2); - const SparqlExpression::Ptr& child0 = children_.at(0); - const SparqlExpression::Ptr& child1 = children_.at(1); - - const auto getPrefilterExprVariablePairVec = - [](const Variable& variable, const ValueId valueId, - bool reverse) -> std::vector { - const auto mirroredExpression = - [](const ValueId valueId) -> std::unique_ptr { - using enum Comparison; - switch (comp) { - case LT: - // Id < ?var -> ?var > Id - return std::make_unique(valueId); - case LE: - // Id <= ?var -> ?var >= Id - return std::make_unique(valueId); - case GE: - // Id >= ?var -> ?var <= Id - return std::make_unique(valueId); - case GT: - // Id > ?var -> ?var < Id - return std::make_unique(valueId); - default: - // EQ(==) or NE(!=) - // Given that these two relations are symmetric w.r.t. ?var and Id, - // no swap regarding the relational operator is necessary. - return std::make_unique>(valueId); + const SparqlExpression* child0 = children_.at(0).get(); + const SparqlExpression* child1 = children_.at(1).get(); + + const auto mirroredExpression = + [](const ValueId valueId) -> std::unique_ptr { + using enum Comparison; + switch (comp) { + case LT: + // Id < ?var -> ?var > Id + return std::make_unique(valueId); + case LE: + // Id <= ?var -> ?var >= Id + return std::make_unique(valueId); + case GE: + // Id >= ?var -> ?var <= Id + return std::make_unique(valueId); + case GT: + // Id > ?var -> ?var < Id + return std::make_unique(valueId); + case EQ: + case NE: + // EQ(==) or NE(!=) + // Given that these two relations are symmetric w.r.t. ?var and Id, + // no swap regarding the relational operator is necessary. + return std::make_unique>(valueId); + default: + // Unchecked / new valueIdComparators::Comparison case. + AD_FAIL(); + } + }; + + const auto tryGetPrefilterExprVariablePairVec = + [&mirroredExpression]( + const SparqlExpression* child0, const SparqlExpression* child1, + bool reversed) -> std::vector { + std::vector resVec{}; + if (const auto* variable = + dynamic_cast(child0)) { + if (const auto* valueId = dynamic_cast(child1)) { + resVec.emplace_back(std::make_pair( + reversed ? mirroredExpression(valueId->value()) + : std::make_unique>( + valueId->value()), + variable->value())); } - }; - std::vector resVec; - resVec.emplace_back(std::make_pair( - reverse ? mirroredExpression(valueId) - : std::make_unique>(valueId), - variable)); + } return resVec; }; + std::vector resVec; // Option 1: // RelationalExpression containing a VariableExpression as the first child // and an IdExpression as the second child. // E.g. for ?x >= 10 (RelationalExpression Sparql), we obtain the following // pair with PrefilterExpression and Variable: <(>= 10), ?x> - if (const auto* variable = - dynamic_cast(child0.get())) { - if (const auto* valueId = dynamic_cast(child1.get())) { - return getPrefilterExprVariablePairVec(variable->value(), - valueId->value(), false); - } + resVec = tryGetPrefilterExprVariablePairVec(child0, child1, false); + if (!resVec.empty()) { + return resVec; } // Option 2: - // RelationalExpression containing a IdExpression as the first child and an + // RelationalExpression containing an IdExpression as the first child and a // VariableExpression as the second child. // (1) 10 >= ?x (RelationalExpression Sparql), we obtain the following // pair with PrefilterExpression and Variable: <(<= 10), ?x> // (2) 10 != ?x (RelationalExpression Sparql), we obtain the following - // pair with PrefilterExpression and Variable: <(!= 10), ?x> - if (const auto* valueId = dynamic_cast(child0.get())) { - if (const auto* variable = - dynamic_cast(child1.get())) { - return getPrefilterExprVariablePairVec(variable->value(), - valueId->value(), true); - } - } - return {}; + // pair with PrefilterExpression and Variable: <(!= 10), ?x>; + // Option 3: + // If no PrefilterExpressions could be constructed for this + // RelationalExpression, just return the empty vector. + return tryGetPrefilterExprVariablePairVec(child1, child0, true); } // _____________________________________________________________________________ diff --git a/src/index/CompressedBlockPrefiltering.cpp b/src/index/CompressedBlockPrefiltering.cpp index ab75064463..51e20d6bc3 100644 --- a/src/index/CompressedBlockPrefiltering.cpp +++ b/src/index/CompressedBlockPrefiltering.cpp @@ -123,6 +123,42 @@ static auto getSetUnion(const std::vector& blocks1, return mergedVectors; } +//______________________________________________________________________________ +// Return `CompOp`s as string. +static std::string getRelationalOpStr(const CompOp relOp) { + using enum CompOp; + switch (relOp) { + case LT: + return "LT(<)"; + case LE: + return "LE(<=)"; + case EQ: + return "EQ(=)"; + case NE: + return "NE(!=)"; + case GE: + return "GE(>=)"; + case GT: + return "GT(>)"; + default: + AD_FAIL(); + } +} + +//______________________________________________________________________________ +// Return `LogicalOperator`s as string. +static std::string getLogicalOpStr(const LogicalOperator logOp) { + using enum LogicalOperator; + switch (logOp) { + case AND: + return "AND(&&)"; + case OR: + return "OR(||)"; + default: + AD_FAIL(); + } +} + // SECTION PREFILTER EXPRESSION (BASE CLASS) //______________________________________________________________________________ std::vector PrefilterExpression::evaluate( @@ -246,17 +282,17 @@ template std::string RelationalExpression::info( [[maybe_unused]] size_t depth) const { std::stringstream stream; - stream << "Prefilter RelationalExpression<" << static_cast(Comparison) + stream << "Prefilter RelationalExpression<" << getRelationalOpStr(Comparison) << ">\nValueId: " << referenceId_ << std::endl; return stream.str(); }; // SECTION LOGICAL OPERATIONS //______________________________________________________________________________ -template +template std::unique_ptr LogicalExpression::logicalComplement() const { - using enum LogicalOperators; + using enum LogicalOperator; // Source De-Morgan's laws: De Morgan's laws, Wikipedia. // Reference: https://en.wikipedia.org/wiki/De_Morgan%27s_laws if constexpr (Operation == OR) { @@ -272,10 +308,10 @@ LogicalExpression::logicalComplement() const { }; //______________________________________________________________________________ -template +template std::vector LogicalExpression::evaluateImpl( const std::vector& input, size_t evaluationColumn) const { - using enum LogicalOperators; + using enum LogicalOperator; if constexpr (Operation == AND) { auto resultChild1 = child1_->evaluate(input, evaluationColumn); return child2_->evaluate(resultChild1, evaluationColumn); @@ -287,7 +323,7 @@ std::vector LogicalExpression::evaluateImpl( }; //______________________________________________________________________________ -template +template bool LogicalExpression::operator==( const PrefilterExpression& other) const { const LogicalExpression* otherlogical = @@ -300,14 +336,14 @@ bool LogicalExpression::operator==( }; //______________________________________________________________________________ -template +template std::string LogicalExpression::info(size_t depth) const { std::string child1Info = depth < maxInfoRecursion ? child1_->info(depth + 1) : "MAX_DEPTH"; std::string child2Info = depth < maxInfoRecursion ? child2_->info(depth + 1) : "MAX_DEPTH"; std::stringstream stream; - stream << "Prefilter LogicalExpression<" << static_cast(Operation) + stream << "Prefilter LogicalExpression<" << getLogicalOpStr(Operation) << ">\n" << "child1 {" << child1Info << "}" << "child2 {" << child2Info << "}" << std::endl; @@ -353,22 +389,19 @@ namespace detail { //______________________________________________________________________________ void checkPropertiesForPrefilterConstruction( const std::vector& vec) { - if (!std::ranges::is_sorted(vec, [](const auto& pair1, const auto& pair2) { - return pair1.second < pair2.second; - })) { + auto viewVariable = vec | std::views::values; + if (!std::ranges::is_sorted(viewVariable, std::less<>{})) { throw std::runtime_error( "The vector must contain the pairs in " "sorted order w.r.t. Variable value."); - }; - if (auto it = std::ranges::adjacent_find( - vec, [](const auto& pair1, - const auto& pair2) { return pair1.second == pair2.second; }); - it != vec.end()) { + } + if (auto it = std::ranges::adjacent_find(viewVariable); + it != std::ranges::end(viewVariable)) { throw std::runtime_error( "For each relevant Variable must exist exactly one " " pair."); } -}; +} } // namespace detail } // namespace prefilterExpressions diff --git a/src/index/CompressedBlockPrefiltering.h b/src/index/CompressedBlockPrefiltering.h index 998b302f35..d7d158d01f 100644 --- a/src/index/CompressedBlockPrefiltering.h +++ b/src/index/CompressedBlockPrefiltering.h @@ -118,10 +118,10 @@ class RelationalExpression : public PrefilterExpression { // Helper struct for a compact class implementation regarding the logical // operations `AND` and `OR`. `NOT` is implemented separately given that the // expression is unary (single child expression). -enum struct LogicalOperators { AND, OR }; +enum struct LogicalOperator { AND, OR }; //______________________________________________________________________________ -template +template class LogicalExpression : public PrefilterExpression { private: std::unique_ptr child1_; @@ -171,8 +171,8 @@ template class RelationalExpression; template class RelationalExpression; template class RelationalExpression; -template class LogicalExpression; -template class LogicalExpression; +template class LogicalExpression; +template class LogicalExpression; //______________________________________________________________________________ // Definition of the RelationalExpression for LT, LE, EQ, NE, GE and GT. @@ -192,9 +192,9 @@ using GreaterThanExpression = prefilterExpressions::RelationalExpression< //______________________________________________________________________________ // Definition of the LogicalExpression for AND and OR. using AndExpression = prefilterExpressions::LogicalExpression< - prefilterExpressions::LogicalOperators::AND>; + prefilterExpressions::LogicalOperator::AND>; using OrExpression = prefilterExpressions::LogicalExpression< - prefilterExpressions::LogicalOperators::OR>; + prefilterExpressions::LogicalOperator::OR>; namespace detail { //______________________________________________________________________________ diff --git a/test/CompressedBlockPrefilteringTest.cpp b/test/CompressedBlockPrefilteringTest.cpp index 995a627791..72d7c6754f 100644 --- a/test/CompressedBlockPrefilteringTest.cpp +++ b/test/CompressedBlockPrefilteringTest.cpp @@ -748,16 +748,24 @@ TEST(PrefilterExpression, testEqualityOperator) { TEST(PrefilterExpression, checkPrintFormattedPrefilterExpression) { auto expr = lt(IntId(10)); EXPECT_EQ((std::stringstream() << *expr).str(), - "Prefilter RelationalExpression<0>\nValueId: I:10\n.\n"); + "Prefilter RelationalExpression\nValueId: I:10\n.\n"); + expr = neq(DoubleId(8.21)); + EXPECT_EQ((std::stringstream() << *expr).str(), + "Prefilter RelationalExpression\nValueId: D:8.210000\n.\n"); expr = notExpr(eq(VocabId(0))); EXPECT_EQ((std::stringstream() << *expr).str(), "Prefilter NotExpression:\nchild {Prefilter " - "RelationalExpression<3>\nValueId: V:0\n}\n.\n"); - expr = orExpr(le(IntId(0)), eq(IntId(5))); + "RelationalExpression\nValueId: V:0\n}\n.\n"); + expr = orExpr(le(IntId(0)), ge(IntId(5))); + EXPECT_EQ((std::stringstream() << *expr).str(), + "Prefilter LogicalExpression\nchild1 {Prefilter " + "RelationalExpression\nValueId: I:0\n}child2 {Prefilter " + "RelationalExpression=)>\nValueId: I:5\n}\n.\n"); + expr = andExpr(lt(IntId(20)), gt(IntId(10))); EXPECT_EQ((std::stringstream() << *expr).str(), - "Prefilter LogicalExpression<1>\nchild1 {Prefilter " - "RelationalExpression<1>\nValueId: I:0\n}child2 {Prefilter " - "RelationalExpression<2>\nValueId: I:5\n}\n.\n"); + "Prefilter LogicalExpression\nchild1 {Prefilter " + "RelationalExpression\nValueId: I:20\n}child2 {Prefilter " + "RelationalExpression)>\nValueId: I:10\n}\n.\n"); } //______________________________________________________________________________ From 95e9027e06f0cb470be81c738ec3a1c388ef4fcc Mon Sep 17 00:00:00 2001 From: realHannes Date: Mon, 11 Nov 2024 16:53:52 +0100 Subject: [PATCH 49/81] further adjustments + apply PrefilterExpressions in IndexScan --- src/engine/Filter.cpp | 17 +- src/engine/Filter.h | 5 +- src/engine/IndexScan.cpp | 104 ++++++++---- src/engine/IndexScan.h | 33 +++- src/engine/Operation.cpp | 26 ++- src/engine/Operation.h | 16 +- src/engine/QueryExecutionTree.cpp | 7 + src/engine/QueryExecutionTree.h | 15 +- .../NumericBinaryExpressions.cpp | 155 ++++++++++++------ .../NumericUnaryExpressions.cpp | 2 +- .../RelationalExpressions.cpp | 3 +- .../sparqlExpressions/StringExpressions.cpp | 60 ++++++- src/index/CompressedBlockPrefiltering.cpp | 20 +++ src/index/CompressedBlockPrefiltering.h | 14 +- src/index/Permutation.cpp | 18 +- src/index/Permutation.h | 4 +- test/CompressedBlockPrefilteringTest.cpp | 59 ++++++- 17 files changed, 432 insertions(+), 126 deletions(-) diff --git a/src/engine/Filter.cpp b/src/engine/Filter.cpp index 79ef7572ad..265fb4b89c 100644 --- a/src/engine/Filter.cpp +++ b/src/engine/Filter.cpp @@ -27,7 +27,9 @@ Filter::Filter(QueryExecutionContext* qec, sparqlExpression::SparqlExpressionPimpl expression) : Operation(qec), _subtree(std::move(subtree)), - _expression{std::move(expression)} {} + _expression{std::move(expression)} { + setPrefilterExpressionForIndexScanChildren(); +} // _____________________________________________________________________________ string Filter::getCacheKeyImpl() const { @@ -37,10 +39,23 @@ string Filter::getCacheKeyImpl() const { return std::move(os).str(); } +//______________________________________________________________________________ string Filter::getDescriptor() const { return absl::StrCat("Filter ", _expression.getDescriptor()); } +//______________________________________________________________________________ +void Filter::setPrefilterExpressionForIndexScanChildren() { + const std::vector& prefilterVec = + _expression.getPrefilterExpressionForMetadata(); + this->forAllDescendants( + [&prefilterVec](const QueryExecutionTree* ptr) -> void { + if (ptr) { + ptr->setPrefilterExpression(prefilterVec); + } + }); +} + // _____________________________________________________________________________ ProtoResult Filter::computeResult(bool requestLaziness) { LOG(DEBUG) << "Getting sub-result for Filter result computation..." << endl; diff --git a/src/engine/Filter.h b/src/engine/Filter.h index eaa9303bb7..a475758e0f 100644 --- a/src/engine/Filter.h +++ b/src/engine/Filter.h @@ -10,10 +10,11 @@ #include "engine/Operation.h" #include "engine/QueryExecutionTree.h" -#include "engine/sparqlExpressions/SparqlExpressionPimpl.h" #include "parser/ParsedQuery.h" class Filter : public Operation { + using PrefilterVariablePair = sparqlExpression::PrefilterExprVariablePair; + private: std::shared_ptr _subtree; sparqlExpression::SparqlExpressionPimpl _expression; @@ -58,6 +59,8 @@ class Filter : public Operation { return _subtree->getVariableColumns(); } + void setPrefilterExpressionForIndexScanChildren(); + ProtoResult computeResult(bool requestLaziness) override; // Perform the actual filter operation of the data provided. diff --git a/src/engine/IndexScan.cpp b/src/engine/IndexScan.cpp index 9217e91ac0..8a0066b4d5 100644 --- a/src/engine/IndexScan.cpp +++ b/src/engine/IndexScan.cpp @@ -17,13 +17,15 @@ using std::string; // _____________________________________________________________________________ IndexScan::IndexScan(QueryExecutionContext* qec, Permutation::Enum permutation, - const SparqlTripleSimple& triple, Graphs graphsToFilter) + const SparqlTripleSimple& triple, Graphs graphsToFilter, + PrefilterIndexPairs prefilters) : Operation(qec), permutation_(permutation), subject_(triple.s_), predicate_(triple.p_), object_(triple.o_), graphsToFilter_{std::move(graphsToFilter)}, + prefilters_{std::move(prefilters)}, numVariables_(static_cast(subject_.isVariable()) + static_cast(predicate_.isVariable()) + static_cast(object_.isVariable())) { @@ -51,9 +53,10 @@ IndexScan::IndexScan(QueryExecutionContext* qec, Permutation::Enum permutation, // _____________________________________________________________________________ IndexScan::IndexScan(QueryExecutionContext* qec, Permutation::Enum permutation, - const SparqlTriple& triple, Graphs graphsToFilter) - : IndexScan(qec, permutation, triple.getSimple(), - std::move(graphsToFilter)) {} + const SparqlTriple& triple, Graphs graphsToFilter, + PrefilterIndexPairs prefilters) + : IndexScan(qec, permutation, triple.getSimple(), std::move(graphsToFilter), + std::move(prefilters)) {} // _____________________________________________________________________________ string IndexScan::getCacheKeyImpl() const { @@ -115,6 +118,24 @@ vector IndexScan::resultSortedOn() const { return result; } +// _____________________________________________________________________________ +void IndexScan::setPrefilterExpression( + const std::vector& prefilterVariablePairs) { + const std::vector& sortedColumns = resultSortedOn(); + VariableToColumnMap varToColMap = computeVariableToColumnMap(); + + const auto addPrefilterIfSorted = [&](const PrefilterVariablePair& pair) { + const Variable& variable = pair.second; + if (varToColMap.find(variable) != varToColMap.end()) { + const ColumnIndex colIdx = varToColMap[variable].columnIndex_; + if (std::ranges::find(sortedColumns, colIdx) != sortedColumns.end()) { + prefilters_.push_back(std::make_pair(pair.first->clone(), colIdx)); + } + } + }; + std::ranges::for_each(prefilterVariablePairs, addPrefilterIfSorted); +} + // _____________________________________________________________________________ VariableToColumnMap IndexScan::computeVariableToColumnMap() const { VariableToColumnMap variableToColumnMap; @@ -134,40 +155,63 @@ VariableToColumnMap IndexScan::computeVariableToColumnMap() const { return variableToColumnMap; } +//______________________________________________________________________________ +std::vector IndexScan::applyFilterBlockMetadata( + const std::vector& blocks) const { + std::ranges::for_each(prefilters_, [&blocks](const PrefilterIndexPair& pair) { + pair.first->evaluate(blocks, pair.second); + }); + return blocks; +}; + // _____________________________________________________________________________ -Result::Generator IndexScan::scanInChunks() const { +Result::Generator IndexScan::chunkedIndexScan() const { auto metadata = getMetadataForScan(); if (!metadata.has_value()) { co_return; } auto blocksSpan = CompressedRelationReader::getBlocksFromMetadata(metadata.value()); - std::vector blocks{blocksSpan.begin(), - blocksSpan.end()}; - for (IdTable& idTable : getLazyScan(std::move(blocks))) { + for (IdTable& idTable : getLazyScan({blocksSpan.begin(), blocksSpan.end()})) { co_yield {std::move(idTable), LocalVocab{}}; } } // _____________________________________________________________________________ -ProtoResult IndexScan::computeResult(bool requestLaziness) { - LOG(DEBUG) << "IndexScan result computation...\n"; - if (requestLaziness) { - return {scanInChunks(), resultSortedOn()}; - } - IdTable idTable{getExecutionContext()->getAllocator()}; - +IdTable IndexScan::completeIndexScan() const { + // Get the blocks. + auto metadata = getMetadataForScan(); + auto blockSpan = + metadata.has_value() + ? CompressedRelationReader::getBlocksFromMetadata(metadata.value()) + : std::span{}; + auto optFilteredBlocks = getLimit().isUnconstrained() + ? std::optional{applyFilterBlockMetadata( + {blockSpan.begin(), blockSpan.end()})} + : std::nullopt; + // Create IdTable, fill it with content by performing scan(). using enum Permutation::Enum; + IdTable idTable{getExecutionContext()->getAllocator()}; idTable.setNumColumns(numVariables_); const auto& index = _executionContext->getIndex(); - idTable = - index.scan(getScanSpecification(), permutation_, additionalColumns(), - cancellationHandle_, deltaTriples(), getLimit()); + idTable = index.getImpl() + .getPermutation(permutation_) + .scan(getScanSpecification(), additionalColumns(), + cancellationHandle_, deltaTriples(), getLimit(), + optFilteredBlocks); AD_CORRECTNESS_CHECK(idTable.numColumns() == getResultWidth()); LOG(DEBUG) << "IndexScan result computation done.\n"; checkCancellation(); + return idTable; +} - return {std::move(idTable), resultSortedOn(), LocalVocab{}}; +// _____________________________________________________________________________ +ProtoResult IndexScan::computeResult(bool requestLaziness) { + LOG(DEBUG) << "IndexScan result computation...\n"; + if (requestLaziness) { + return {chunkedIndexScan(), resultSortedOn()}; + } + return {completeIndexScan(), getResultSortedOn(), LocalVocab{}}; } // _____________________________________________________________________________ @@ -208,7 +252,7 @@ void IndexScan::determineMultiplicities() { AD_CONTRACT_CHECK(multiplicity_.size() == getResultWidth()); } -// ___________________________________________________________________________ +// _____________________________________________________________________________ std::array IndexScan::getPermutedTriple() const { std::array triple{&subject_, &predicate_, &object_}; @@ -217,30 +261,30 @@ std::array IndexScan::getPermutedTriple() triple[permutation[2]]}; } -// ___________________________________________________________________________ +// _____________________________________________________________________________ ScanSpecification IndexScan::getScanSpecification() const { const IndexImpl& index = getIndex().getImpl(); return getScanSpecificationTc().toScanSpecification(index); } -// ___________________________________________________________________________ +// _____________________________________________________________________________ ScanSpecificationAsTripleComponent IndexScan::getScanSpecificationTc() const { auto permutedTriple = getPermutedTriple(); return {*permutedTriple[0], *permutedTriple[1], *permutedTriple[2], graphsToFilter_}; } -// ___________________________________________________________________________ +// _____________________________________________________________________________ Permutation::IdTableGenerator IndexScan::getLazyScan( std::vector blocks) const { // If there is a LIMIT or OFFSET clause that constrains the scan // (which can happen with an explicit subquery), we cannot use the prefiltered // blocks, as we currently have no mechanism to include limits and offsets // into the prefiltering (`std::nullopt` means `scan all blocks`). - auto actualBlocks = getLimit().isUnconstrained() - ? std::optional{std::move(blocks)} - : std::nullopt; - + auto actualBlocks = + getLimit().isUnconstrained() + ? std::optional{applyFilterBlockMetadata(std::move(blocks))} + : std::nullopt; return getIndex() .getImpl() .getPermutation(permutation()) @@ -249,7 +293,7 @@ Permutation::IdTableGenerator IndexScan::getLazyScan( getLimit()); }; -// ________________________________________________________________ +// _____________________________________________________________________________ std::optional IndexScan::getMetadataForScan() const { const auto& index = getExecutionContext()->getIndex().getImpl(); @@ -257,7 +301,7 @@ std::optional IndexScan::getMetadataForScan() .getMetadataAndBlocks(getScanSpecification(), deltaTriples()); }; -// ________________________________________________________________ +// _____________________________________________________________________________ std::array IndexScan::lazyScanForJoinOfTwoScans(const IndexScan& s1, const IndexScan& s2) { AD_CONTRACT_CHECK(s1.numVariables_ <= 3 && s2.numVariables_ <= 3); @@ -306,7 +350,7 @@ IndexScan::lazyScanForJoinOfTwoScans(const IndexScan& s1, const IndexScan& s2) { return result; } -// ________________________________________________________________ +// _____________________________________________________________________________ Permutation::IdTableGenerator IndexScan::lazyScanForJoinOfColumnWithScan( std::span joinColumn) const { AD_EXPENSIVE_CHECK(std::ranges::is_sorted(joinColumn)); diff --git a/src/engine/IndexScan.h b/src/engine/IndexScan.h index 9e75c89d35..6055b5a1a0 100644 --- a/src/engine/IndexScan.h +++ b/src/engine/IndexScan.h @@ -11,13 +11,21 @@ class SparqlTriple; class SparqlTripleSimple; class IndexScan final : public Operation { + using Graphs = ScanSpecificationAsTripleComponent::Graphs; + // Pair containing a `PrefilterExpression` with `ColumnIndex` (eval. index) + using PrefilterIndexPair = + std::pair, + ColumnIndex>; + // Vector with `PrefilterIndexPair` values. + using PrefilterIndexPairs = std::vector; + private: Permutation::Enum permutation_; TripleComponent subject_; TripleComponent predicate_; TripleComponent object_; - using Graphs = ScanSpecificationAsTripleComponent::Graphs; Graphs graphsToFilter_; + PrefilterIndexPairs prefilters_; size_t numVariables_; size_t sizeEstimate_; vector multiplicity_; @@ -30,10 +38,12 @@ class IndexScan final : public Operation { public: IndexScan(QueryExecutionContext* qec, Permutation::Enum permutation, - const SparqlTriple& triple, Graphs graphsToFilter = std::nullopt); + const SparqlTriple& triple, Graphs graphsToFilter = std::nullopt, + PrefilterIndexPairs prefilters = {}); IndexScan(QueryExecutionContext* qec, Permutation::Enum permutation, const SparqlTripleSimple& triple, - Graphs graphsToFilter = std::nullopt); + Graphs graphsToFilter = std::nullopt, + PrefilterIndexPairs prefilters = {}); ~IndexScan() override = default; @@ -55,6 +65,10 @@ class IndexScan final : public Operation { vector resultSortedOn() const override; + // Set `PrefilterExpression`s. + void setPrefilterExpression(const std::vector& + prefilterVariablePairs) override; + size_t numVariables() const { return numVariables_; } // Return the exact result size of the index scan. This is always known as it @@ -130,9 +144,18 @@ class IndexScan final : public Operation { VariableToColumnMap computeVariableToColumnMap() const override; - Result::Generator scanInChunks() const; + // Filter relevant `CompressedBlockMetadata` blocks by applying the + // `PrefilterExpression`s from `prefilters_`. + std::vector applyFilterBlockMetadata( + const std::vector& blocks) const; + + // Return the (lazy) `IdTable` for this `IndexScan` in chunks. + Result::Generator chunkedIndexScan() const; + // Get the `IdTable` for this `IndexScan` in one piece. + IdTable completeIndexScan() const; - // Helper functions for the public `getLazyScanFor...` functions (see above). + // Helper functions for the public `getLazyScanFor...` methods and + // `chunkedIndexScan` (see above). Permutation::IdTableGenerator getLazyScan( std::vector blocks) const; std::optional getMetadataForScan() const; diff --git a/src/engine/Operation.cpp b/src/engine/Operation.cpp index bb7d8fb0af..4d10cffc87 100644 --- a/src/engine/Operation.cpp +++ b/src/engine/Operation.cpp @@ -10,31 +10,39 @@ using namespace std::chrono_literals; +//______________________________________________________________________________ template -void Operation::forAllDescendants(F f) { +void Operation::forAllDescendantsImpl(F&& f) { static_assert( std::is_same_v>); for (auto ptr : getChildren()) { if (ptr) { - f(ptr); - ptr->forAllDescendants(f); + std::forward(f)(ptr); + ptr->forAllDescendants(std::forward(f)); } } } +//______________________________________________________________________________ template -void Operation::forAllDescendants(F f) const { +void Operation::forAllDescendantsImpl(F&& f) const { static_assert( std::is_same_v>); for (auto ptr : getChildren()) { if (ptr) { - f(ptr); - ptr->forAllDescendants(f); + std::forward(f)(ptr); + ptr->forAllDescendants(std::forward(f)); } } } -// __________________________________________________________________________________________________________ +// _____________________________________________________________________________ +void Operation::forAllDescendants( + std::function&& func) { + forAllDescendantsImpl(std::move(func)); +} + +// _____________________________________________________________________________ vector Operation::collectWarnings() const { vector res = getWarnings(); for (auto child : getChildren()) { @@ -53,7 +61,7 @@ vector Operation::collectWarnings() const { void Operation::recursivelySetCancellationHandle( SharedCancellationHandle cancellationHandle) { AD_CORRECTNESS_CHECK(cancellationHandle); - forAllDescendants([&cancellationHandle](auto child) { + forAllDescendantsImpl([&cancellationHandle](auto child) { child->getRootOperation()->cancellationHandle_ = cancellationHandle; }); cancellationHandle_ = std::move(cancellationHandle); @@ -64,7 +72,7 @@ void Operation::recursivelySetCancellationHandle( void Operation::recursivelySetTimeConstraint( std::chrono::steady_clock::time_point deadline) { deadline_ = deadline; - forAllDescendants([deadline](auto child) { + forAllDescendantsImpl([deadline](auto child) { child->getRootOperation()->deadline_ = deadline; }); } diff --git a/src/engine/Operation.h b/src/engine/Operation.h index 61702c7766..10f7df07a0 100644 --- a/src/engine/Operation.h +++ b/src/engine/Operation.h @@ -14,6 +14,7 @@ #include "engine/Result.h" #include "engine/RuntimeInformation.h" #include "engine/VariableToColumnMap.h" +#include "engine/sparqlExpressions/SparqlExpressionPimpl.h" #include "parser/data/LimitOffsetClause.h" #include "parser/data/Variable.h" #include "util/CancellationHandle.h" @@ -34,6 +35,9 @@ class Operation { using Milliseconds = std::chrono::milliseconds; public: + // Holds a `PrefilterExpression` with its corresponding `Variable`. + using PrefilterVariablePair = sparqlExpression::PrefilterExprVariablePair; + // Default Constructor. Operation() : _executionContext(nullptr) {} @@ -73,6 +77,14 @@ class Operation { return _executionContext->deltaTriples(); } + // Set `PrefilterExpression`s (for `IndexScan`). + virtual void setPrefilterExpression( + const std::vector&){}; + + // Apply the provided void function for all descendants. The given function + // must be invocable with a `const QueryExecutionTree*` object. + void forAllDescendants(std::function&& func); + // Get a unique, not ambiguous string representation for a subtree. // This should act like an ID for each subtree. // Calls `getCacheKeyImpl` and adds the information about the `LIMIT` clause. @@ -354,11 +366,11 @@ class Operation { // Recursively call a function on all children. template - void forAllDescendants(F f); + void forAllDescendantsImpl(F&& f); // Recursively call a function on all children. template - void forAllDescendants(F f) const; + void forAllDescendantsImpl(F&& f) const; // Holds a precomputed Result of this operation if it is the sibling of a // Service operation. diff --git a/src/engine/QueryExecutionTree.cpp b/src/engine/QueryExecutionTree.cpp index aed58d4fde..65642ae5af 100644 --- a/src/engine/QueryExecutionTree.cpp +++ b/src/engine/QueryExecutionTree.cpp @@ -96,6 +96,13 @@ size_t QueryExecutionTree::getSizeEstimate() { return sizeEstimate_.value(); } +//_____________________________________________________________________________ +void QueryExecutionTree::setPrefilterExpression( + const std::vector& prefilterVec) const { + AD_CONTRACT_CHECK(rootOperation_); + rootOperation_->setPrefilterExpression(prefilterVec); +} + // _____________________________________________________________________________ bool QueryExecutionTree::knownEmptyResult() { if (cachedResult_) { diff --git a/src/engine/QueryExecutionTree.h b/src/engine/QueryExecutionTree.h index 0519082b78..7df9a6bed4 100644 --- a/src/engine/QueryExecutionTree.h +++ b/src/engine/QueryExecutionTree.h @@ -85,6 +85,9 @@ class QueryExecutionTree { return rootOperation_->getMultiplicity(col); } + void setPrefilterExpression( + const std::vector& prefilterVec) const; + size_t getDistinctEstimate(size_t col) const { return static_cast(rootOperation_->getSizeEstimate() / rootOperation_->getMultiplicity(col)); @@ -106,26 +109,26 @@ class QueryExecutionTree { } template - void forAllDescendants(F f) { + void forAllDescendants(F&& f) { static_assert( std::is_same_v>); for (auto ptr : rootOperation_->getChildren()) { if (ptr) { - f(ptr); - ptr->forAllDescendants(f); + std::forward(f)(ptr); + ptr->forAllDescendants(std::forward(f)); } } } template - void forAllDescendants(F f) const { + void forAllDescendants(F&& f) const { static_assert( std::is_same_v>); for (auto ptr : rootOperation_->getChildren()) { if (ptr) { - f(ptr); - ptr->forAllDescendants(f); + std::forward(f)(ptr); + ptr->forAllDescendants(std::forward(f)); } } } diff --git a/src/engine/sparqlExpressions/NumericBinaryExpressions.cpp b/src/engine/sparqlExpressions/NumericBinaryExpressions.cpp index 7f8319e17c..e8bf9a450f 100644 --- a/src/engine/sparqlExpressions/NumericBinaryExpressions.cpp +++ b/src/engine/sparqlExpressions/NumericBinaryExpressions.cpp @@ -70,7 +70,10 @@ inline auto andLambda = [](TernaryBool a, TernaryBool b) { namespace constructPrefilterExpr { namespace { -// MERGE AND CONJUNCTION +// `BinaryOperator` defines the operations `AND` and `OR`. +using BinaryOperator = prefilterExpressions::LogicalOperator; + +// MERGE // _____________________________________________________________________________ // For our pre-filtering logic over index scans, we need exactly one // corresponding PrefilterExpression for each relevant Variable. Thus, if the @@ -97,44 +100,34 @@ namespace { // left child: {<(>= 10 AND <= 20), ?x>}; (?x >= 10 && ?x <= 20) // right child: {<(!= 15), ?x>, <(= 10), ?y>}; (?x != 15 && ?y = 10) // Returns {<((>= 10 AND <= 20) AND != 15), ?x>, <(= 10), ?y>} +// +// +// MERGE +// (Partial De-Morgan applied w.r.t. Or-conjunction) //______________________________________________________________________________ -template -std::vector mergeChildrenForAndExpressionImpl( - std::vector&& leftChild, - std::vector&& rightChild) { - namespace pd = prefilterExpressions::detail; - pd::checkPropertiesForPrefilterConstruction(leftChild); - pd::checkPropertiesForPrefilterConstruction(rightChild); - // Merge the PrefilterExpression vectors from the left and right child. - // Remark: The vectors contain std::pairs, sorted by their respective - // Variable to the PrefilterExpression. - auto itLeft = leftChild.begin(); - auto itRight = rightChild.begin(); - std::vector resPairs; - while (itLeft != leftChild.end() && itRight != rightChild.end()) { - auto& [exprLeft, varLeft] = *itLeft; - auto& [exprRight, varRight] = *itRight; - if (varLeft == varRight) { - resPairs.emplace_back(std::make_unique( - std::move(exprLeft), std::move(exprRight)), - varLeft); - ++itLeft; - ++itRight; - } else if (varLeft < varRight) { - resPairs.emplace_back(std::move(*itLeft)); - ++itLeft; - } else { - resPairs.emplace_back(std::move(*itRight)); - ++itRight; - } - } - std::ranges::move(itLeft, leftChild.end(), std::back_inserter(resPairs)); - std::ranges::move(itRight, rightChild.end(), std::back_inserter(resPairs)); - pd::checkPropertiesForPrefilterConstruction(resPairs); - return resPairs; -} - -// MERGE OR CONJUNCTION +// Merge the pair values of leftChild and rightChild with the combine logic for +// AND. Given we have a respective pair from leftChild and rightChild for which the Variable value is +// equal, conjunct the two PrefilterExpressions over an OrExpression. For the +// resulting pairs, NOT (NotExpression) is applied later on. (As a result we +// have fully applied De-Morgan on the available PrefilterExpression) +// +// EXAMPLE 1 +// !...(?x = 5 OR ?y = VocabId(10)) +// partial De-Morgan: ...(?x = 5 AND ?y = VocabId(10)) +// (complete De-Morgan later on with NotExpression: +// ...(?x != 5 AND ?y != VocabId(10))) (in NumericUnaryExpression.cpp) +// The merge function implements the following: +// left child: {<(= 5), ?x>} +// right child: {<(= VocabId(10)), ?y>} +// merged: {<(= 5), ?x>, <(= VocabId(10)), ?y>} +// +// EXAMPLE 2 +// left child: {<(>= 10 OR <= 20), ?x>} +// right child: {<(!= 15), ?x>, <(= 10), ?y>} +// merged: {<((>= 10 OR <= 20) OR ?x != 15), ?x>, <(= 10), ?y>} +// +// MERGE //______________________________________________________________________________ // Four examples on how this function (OR) merges the content of two children. // @@ -167,14 +160,44 @@ std::vector mergeChildrenForAndExpressionImpl( // PrefiterExpression. We can't make a distinct prediction w.r.t. ?y != 0 // => not relevant for the PrefilterExpression. Thus, we return the // PrefilterExpresion {<(>= 10 OR <= 0), ?x>}. +// +// +// MERGE +// (Partial De-Morgan applied w.r.t. And-conjunction) //______________________________________________________________________________ -template -std::vector mergeChildrenForOrExpressionImpl( +// Merge the pair values of leftChild and rightChild with the combine logic for +// OR. The difference, given we have a respective pair from leftChild and rightChild for which the Variable value is +// equal, conjunct the two PrefilterExpressions over an AndExpression. For the +// resulting pairs, NOT (NotExpression) is applied later on. As a result we +// have fully applied De-Morgan on the available PrefilterExpression. +// +// EXAMPLE 1 +// !...(?y = 5 AND ?x <= 3); apply partial De-Morgan given NOT(!): Thus OR +// instead of AND merge (here). +// left child: {<(= 5), ?y>} +// right child: {<(<= 3), ?x>} +// merged result: {} +// +// EXAMPLE 2 +// !...((?x = 10 OR ?y != 0) AND ?x <= 0); apply partial De-Morgan given NOT(!): +// Thus OR instead of AND merge (here). +// left child: {<(= 10), ?x), <(!= 0), ?y>}; +// right child: {<(<= 0), ?x>} +// merged result: {<((= 10) AND (<= 0)), ?x>} +// (with NOT applied later on: {<((!= 10) OR (> 0)), ?x>}) +//______________________________________________________________________________ +template +std::vector mergeChildrenForBinaryOpExpressionImpl( std::vector&& leftChild, std::vector&& rightChild) { + using enum BinaryOperator; namespace pd = prefilterExpressions::detail; pd::checkPropertiesForPrefilterConstruction(leftChild); pd::checkPropertiesForPrefilterConstruction(rightChild); + // Merge the PrefilterExpression vectors from the left and right child. + // Remark: The vectors contain std::pairs, sorted by their respective + // Variable to the PrefilterExpression. auto itLeft = leftChild.begin(); auto itRight = rightChild.begin(); std::vector resPairs; @@ -188,15 +211,42 @@ std::vector mergeChildrenForOrExpressionImpl( ++itLeft; ++itRight; } else if (varLeft < varRight) { + if constexpr (binOp == AND) { + resPairs.emplace_back(std::move(*itLeft)); + } ++itLeft; } else { + if constexpr (binOp == AND) { + resPairs.emplace_back(std::move(*itRight)); + } ++itRight; } } + if constexpr (binOp == AND) { + std::ranges::move(itLeft, leftChild.end(), std::back_inserter(resPairs)); + std::ranges::move(itRight, rightChild.end(), std::back_inserter(resPairs)); + } pd::checkPropertiesForPrefilterConstruction(resPairs); return resPairs; } +// TEMPLATED STANDARD MERGE +//______________________________________________________________________________ +constexpr auto makeAndMergeWithAndConjunction = + mergeChildrenForBinaryOpExpressionImpl; +constexpr auto makeOrMergeWithOrConjunction = + mergeChildrenForBinaryOpExpressionImpl; +// TEMPLATED (PARTIAL) DE-MORGAN MERGE +//______________________________________________________________________________ +constexpr auto makeAndMergeWithOrConjunction = + mergeChildrenForBinaryOpExpressionImpl; +constexpr auto makeOrMergeWithAndConjunction = + mergeChildrenForBinaryOpExpressionImpl; + // GET TEMPLATED MERGE FUNCTION (CONJUNCTION AND / OR) //______________________________________________________________________________ // getMergeFunction (get the respective merging function) follows the @@ -221,8 +271,7 @@ std::vector mergeChildrenForOrExpressionImpl( // left child: {<(!= 10), ?y>, <(!= 0), ?x>}; (?y != 10 || ?x != 0) // // Remark: The (child) expressions of the left and right child have been -// combined with the merging procedure AND -// (mergeChildrenForAndExpressionImpl), +// combined with the merging procedure AND (makeAndMergeWithOrConjunction), // because with isNegated = true, it is implied that De-Morgan's law is applied. // Thus, the OR is logically an AND conjunction with its application. // @@ -230,10 +279,10 @@ std::vector mergeChildrenForOrExpressionImpl( // // isNegated is true, hence we know that we have to partially apply // De-Morgan's law. Given that, OR switches to an AND. Therefore we use -// mergeChildrenForAndExpressionImpl with OrExpression (PrefilterExpression) -// as a template value (BinaryPrefilterExpr). -// Call mergeChildrenAndExpressionImpl -// with left and right child as args., we get their merged combination: +// mergeChildrenForBinaryOpExpressionImpl with OrExpression +// (PrefilterExpression) as a template value (BinaryPrefilterExpr). Call +// makeAndMergeWithOrConjunction with left and right child as arguments, we get +// their merged combination: // {<(!= 10), ?y>, <((!= 0) OR ((= 5) OR (>= 10))), ?x>} // // Next their merged value is returned to UnaryNegateExpressionImpl @@ -242,19 +291,23 @@ std::vector mergeChildrenForOrExpressionImpl( // applied. // {<(!= 10), ?y>, <((!= 0) OR ((= 5) OR (>= 10))), ?x>} // apply NotExpr.: {, = 10))), ?x>} -// This yields: {<(= 10), ?y>, <((= 0) AND ((!= 5) AND (< 10))), ?x>} +// This procedure yields: {<(= 10), ?y>, <((= 0) AND ((!= 5) AND (< 10))), ?x>} //______________________________________________________________________________ template constexpr auto getMergeFunction(bool isNegated) { if constexpr (std::is_same_v) { - return !isNegated ? mergeChildrenForAndExpressionImpl - : mergeChildrenForOrExpressionImpl; + return !isNegated ? makeAndMergeWithAndConjunction + // negated, partially apply De-Morgan: + // change AND to OR + : makeOrMergeWithAndConjunction; } else { static_assert(std::is_same_v); - return !isNegated ? mergeChildrenForOrExpressionImpl - : mergeChildrenForAndExpressionImpl; + return !isNegated ? makeOrMergeWithOrConjunction + // negated, partially apply De-Morgan: + // change OR to AND + : makeAndMergeWithOrConjunction; } } diff --git a/src/engine/sparqlExpressions/NumericUnaryExpressions.cpp b/src/engine/sparqlExpressions/NumericUnaryExpressions.cpp index cb72da969a..a9d46d645e 100644 --- a/src/engine/sparqlExpressions/NumericUnaryExpressions.cpp +++ b/src/engine/sparqlExpressions/NumericUnaryExpressions.cpp @@ -54,7 +54,7 @@ class UnaryNegateExpressionImpl : public NaryExpression { // With De-Morgan's rule we retrieve: ?x < IntId(10) && ?y < IntId(10) // // (1) Merge {<(>= IntId(10)), ?x>} and {<(>= IntId(10)), ?y>} - // with mergeChildrenForAndExpressionImpl (defined in + // with mergeChildrenForBinaryOpExpressionImpl (defined in // NumericBinaryExpressions.cpp), which we select based on isNegated = true // (first part of De-Morgans law). // Result (1): {<(>= IntId(10)), ?x>, <(>= IntId(10)), ?y>} diff --git a/src/engine/sparqlExpressions/RelationalExpressions.cpp b/src/engine/sparqlExpressions/RelationalExpressions.cpp index de035dce71..4f2f28f80e 100644 --- a/src/engine/sparqlExpressions/RelationalExpressions.cpp +++ b/src/engine/sparqlExpressions/RelationalExpressions.cpp @@ -464,13 +464,12 @@ RelationalExpression::getPrefilterExpressionForMetadata( } return resVec; }; - std::vector resVec; // Option 1: // RelationalExpression containing a VariableExpression as the first child // and an IdExpression as the second child. // E.g. for ?x >= 10 (RelationalExpression Sparql), we obtain the following // pair with PrefilterExpression and Variable: <(>= 10), ?x> - resVec = tryGetPrefilterExprVariablePairVec(child0, child1, false); + auto resVec = tryGetPrefilterExprVariablePairVec(child0, child1, false); if (!resVec.empty()) { return resVec; } diff --git a/src/engine/sparqlExpressions/StringExpressions.cpp b/src/engine/sparqlExpressions/StringExpressions.cpp index 47ee97bd23..85753e96f8 100644 --- a/src/engine/sparqlExpressions/StringExpressions.cpp +++ b/src/engine/sparqlExpressions/StringExpressions.cpp @@ -4,6 +4,7 @@ #include +#include "engine/sparqlExpressions/LiteralExpression.h" #include "engine/sparqlExpressions/NaryExpressionImpl.h" #include "engine/sparqlExpressions/VariadicExpression.h" @@ -231,9 +232,62 @@ using SubstrExpression = return Id::makeFromBool(text.starts_with(pattern)); }; -using StrStartsExpression = - StringExpressionImpl<2, LiftStringFunction, - StringValueGetter>; +namespace { + +template +requires(isOperation) +class StrStartsExpressionImpl : public NaryExpression { + public: + using NaryExpression::NaryExpression; + std::vector getPrefilterExpressionForMetadata( + [[maybe_unused]] bool isNegated) const override { + AD_CORRECTNESS_CHECK(this->N == 2); + const SparqlExpression* child0 = this->getNthChild(0).value(); + const SparqlExpression* child1 = this->getNthChild(1).value(); + const auto getPrefilterExprVariableVec = + [](const SparqlExpression* child0, const SparqlExpression* child1, + bool startsWithVar) -> std::vector { + using namespace prefilterExpressions; + std::vector resVec{}; + if (const auto* variable = + dynamic_cast(child0)) { + if (const auto* valueId = dynamic_cast(child1)) { + !startsWithVar + // make {<(>= VocabId(n)), ?var>} (Option 1) + ? resVec.emplace_back(std::make_pair( + std::make_unique(valueId->value()), + variable->value())) + // make {<(<= VocabId(n)), ?var>} (Option 2) + : resVec.emplace_back(std::make_pair( + std::make_unique(valueId->value()), + variable->value())); + } + } + return resVec; + }; + // Remark: With the current implementation we only prefilter w.r.t. one + // bound. + // TODO: It is technically possible to pre-filter more precisely by + // introducing a second bound. + // + // Option 1: STRSTARTS(?var, VocabId(n)); startsWithVar = false + // Return PrefilterExpression vector: {<(>= VocabId(n)), ?var>} + auto resVec = getPrefilterExprVariableVec(child0, child1, false); + if (!resVec.empty()) { + return resVec; + } + // Option 2: STRTSTARTS(VocabId(n), ?var); startsWithVar = true + // Return PrefilterExpression vector: {<(<= VocabId(n)), ?var>} + // Option 3: + // child0 or/and child1 are unsuitable SparqlExpression types, return {}. + return getPrefilterExprVariableVec(child1, child0, true); + } +}; + +} // namespace + +using StrStartsExpression = StrStartsExpressionImpl, StringValueGetter>>>; // STRENDS [[maybe_unused]] auto strEndsImpl = [](std::string_view text, diff --git a/src/index/CompressedBlockPrefiltering.cpp b/src/index/CompressedBlockPrefiltering.cpp index 51e20d6bc3..6300d12307 100644 --- a/src/index/CompressedBlockPrefiltering.cpp +++ b/src/index/CompressedBlockPrefiltering.cpp @@ -277,6 +277,13 @@ bool RelationalExpression::operator==( return referenceId_ == otherRelational->referenceId_; }; +//______________________________________________________________________________ +template +std::unique_ptr RelationalExpression::clone() + const { + return std::make_unique>(referenceId_); +}; + //______________________________________________________________________________ template std::string RelationalExpression::info( @@ -335,6 +342,14 @@ bool LogicalExpression::operator==( *child2_ == *otherlogical->child2_; }; +//______________________________________________________________________________ +template +std::unique_ptr LogicalExpression::clone() + const { + return std::make_unique>(child1_->clone(), + child2_->clone()); +}; + //______________________________________________________________________________ template std::string LogicalExpression::info(size_t depth) const { @@ -375,6 +390,11 @@ bool NotExpression::operator==(const PrefilterExpression& other) const { return *child_ == *otherNotExpression->child_; } +//______________________________________________________________________________ +std::unique_ptr NotExpression::clone() const { + return std::make_unique((child_->clone()), true); +}; + //______________________________________________________________________________ std::string NotExpression::info(size_t depth) const { std::string childInfo = diff --git a/src/index/CompressedBlockPrefiltering.h b/src/index/CompressedBlockPrefiltering.h index d7d158d01f..2419d9aea9 100644 --- a/src/index/CompressedBlockPrefiltering.h +++ b/src/index/CompressedBlockPrefiltering.h @@ -45,6 +45,9 @@ class PrefilterExpression { virtual bool operator==(const PrefilterExpression& other) const = 0; + // Create a copy of this `PrefilterExpression`. + virtual std::unique_ptr clone() const = 0; + // Format content for debugging. virtual std::string info(size_t depth) const = 0; @@ -106,6 +109,7 @@ class RelationalExpression : public PrefilterExpression { std::unique_ptr logicalComplement() const override; bool operator==(const PrefilterExpression& other) const override; + std::unique_ptr clone() const override; std::string info([[maybe_unused]] size_t depth) const override; private: @@ -135,6 +139,7 @@ class LogicalExpression : public PrefilterExpression { std::unique_ptr logicalComplement() const override; bool operator==(const PrefilterExpression& other) const override; + std::unique_ptr clone() const override; std::string info(size_t depth) const override; private: @@ -149,11 +154,16 @@ class NotExpression : public PrefilterExpression { std::unique_ptr child_; public: - explicit NotExpression(std::unique_ptr child) - : child_(child->logicalComplement()) {} + explicit NotExpression(std::unique_ptr child, + bool makeCopy = false) + // If we create a copy, the child expression has already been + // complemented while creating the original expression. Thus, just move + // the child. + : child_(makeCopy ? std::move(child) : child->logicalComplement()) {} std::unique_ptr logicalComplement() const override; bool operator==(const PrefilterExpression& other) const override; + std::unique_ptr clone() const override; std::string info(size_t depth) const override; private: diff --git a/src/index/Permutation.cpp b/src/index/Permutation.cpp index 16f5113d68..64a6290216 100644 --- a/src/index/Permutation.cpp +++ b/src/index/Permutation.cpp @@ -52,11 +52,11 @@ void Permutation::loadFromDisk(const std::string& onDiskBase, } // _____________________________________________________________________ -IdTable Permutation::scan(const ScanSpecification& scanSpec, - ColumnIndicesRef additionalColumns, - const CancellationHandle& cancellationHandle, - const DeltaTriples& deltaTriples, - const LimitOffsetClause& limitOffset) const { +IdTable Permutation::scan( + const ScanSpecification& scanSpec, ColumnIndicesRef additionalColumns, + const CancellationHandle& cancellationHandle, + const DeltaTriples& deltaTriples, const LimitOffsetClause& limitOffset, + std::optional> blocks) const { if (!isLoaded_) { throw std::runtime_error("This query requires the permutation " + readableName_ + ", which was not loaded"); @@ -64,9 +64,11 @@ IdTable Permutation::scan(const ScanSpecification& scanSpec, const auto& p = getActualPermutation(scanSpec); - return p.reader().scan(scanSpec, p.meta_.blockData(), additionalColumns, - cancellationHandle, locatedTriples(deltaTriples), - limitOffset); + return p.reader().scan( + scanSpec, + blocks.has_value() ? std::move(blocks.value()) : p.meta_.blockData(), + additionalColumns, cancellationHandle, locatedTriples(deltaTriples), + limitOffset); } // _____________________________________________________________________ diff --git a/src/index/Permutation.h b/src/index/Permutation.h index 93cad7e775..5512fe2a5a 100644 --- a/src/index/Permutation.h +++ b/src/index/Permutation.h @@ -67,7 +67,9 @@ class Permutation { ColumnIndicesRef additionalColumns, const CancellationHandle& cancellationHandle, const DeltaTriples& deltaTriples, - const LimitOffsetClause& limitOffset = {}) const; + const LimitOffsetClause& limitOffset = {}, + std::optional> blocks = + std::nullopt) const; // For a given relation, determine the `col1Id`s and their counts. This is // used for `computeGroupByObjectWithCount`. The `col0Id` must have metadata diff --git a/test/CompressedBlockPrefilteringTest.cpp b/test/CompressedBlockPrefilteringTest.cpp index 72d7c6754f..6089ba4ca0 100644 --- a/test/CompressedBlockPrefilteringTest.cpp +++ b/test/CompressedBlockPrefilteringTest.cpp @@ -180,6 +180,12 @@ class TestPrefilterExprOnBlockMetadata : public ::testing::Test { ::testing::HasSubstr(expected)); } + // Assert that the PrefilterExpression tree is properly copied when calling + // method clone. + auto makeTestClone(std::unique_ptr expr) { + ASSERT_EQ(*expr, *expr->clone()); + } + // Check that the provided expression prefilters the correct blocks. auto makeTest(std::unique_ptr expr, std::vector&& expected) { @@ -568,6 +574,26 @@ TEST_F(TestPrefilterExprOnBlockMetadata, testWithOneBlockMetadataValue) { EXPECT_EQ(expr->evaluate(input, 2), std::vector{}); } +//______________________________________________________________________________ +// Test method clone. clone() creates a copy of the complete PrefilterExpression +// tree. +TEST_F(TestPrefilterExprOnBlockMetadata, testMethodClonePrefilterExpression) { + makeTestClone(lt(VocabId(10))); + makeTestClone(gt(referenceDate2)); + makeTestClone(andExpr(lt(VocabId(20)), gt(VocabId(10)))); + makeTestClone(neq(IntId(10))); + makeTestClone(orExpr(eq(IntId(10)), neq(DoubleId(10)))); + makeTestClone(notExpr(ge(referenceDate1))); + makeTestClone(notExpr(notExpr(neq(VocabId(0))))); + makeTestClone(notExpr(andExpr(eq(IntId(10)), neq(DoubleId(10))))); + makeTestClone(orExpr(orExpr(eq(VocabId(101)), lt(IntId(100))), + andExpr(gt(referenceDate1), lt(referenceDate2)))); + makeTestClone(andExpr(andExpr(neq(IntId(10)), neq(DoubleId(100.23))), + orExpr(gt(DoubleId(0.001)), lt(IntId(250))))); + makeTestClone(orExpr(orExpr(eq(VocabId(101)), lt(IntId(100))), + notExpr(andExpr(lt(VocabId(0)), neq(IntId(100)))))); +} + //______________________________________________________________________________ //______________________________________________________________________________ // TEST SECTION 2 @@ -619,18 +645,26 @@ const auto makeLiteralSparqlExpr = } }; +//______________________________________________________________________________ +auto getExpr = [](const auto& variantVal) -> std::unique_ptr { + return makeLiteralSparqlExpr(variantVal); +}; + //______________________________________________________________________________ template std::unique_ptr makeRelationalSparqlExprImpl( const RelValues& child0, const RelValues& child1) { - auto getExpr = - [](const auto& variantVal) -> std::unique_ptr { - return makeLiteralSparqlExpr(variantVal); - }; return std::make_unique(std::array{ std::visit(getExpr, child0), std::visit(getExpr, child1)}); }; +//______________________________________________________________________________ +std::unique_ptr makeStringStartsWithSparqlExpression( + const RelValues& child0, const RelValues& child1) { + return makeStrStartsExpression(std::visit(getExpr, child0), + std::visit(getExpr, child1)); +}; + //______________________________________________________________________________ template std::unique_ptr makeLogicalBinaryPrefilterExprImpl( @@ -665,6 +699,10 @@ constexpr auto orSprqlExpr = &makeOrExpression; // NOT (`!`, `SparqlExpression`) constexpr auto notSprqlExpr = &makeUnaryNegateExpression; +//______________________________________________________________________________ +// Create SparqlExpression `STRSTARTS`. +constexpr auto strStartsSprql = &makeStringStartsWithSparqlExpression; + // ASSERT EQUALITY //______________________________________________________________________________ const auto equalityCheckPrefilterVectors = @@ -1140,6 +1178,19 @@ TEST(SparqlExpression, getEmptyPrefilterForMoreComplexSparqlExpressions) { eqSprql(Variable{"?country"}, VocabId(20)))))))); } +// Test PrefilterExpression creation for SparqlExpression STRSTARTS +//______________________________________________________________________________ +TEST(SparqlExpression, getPrefilterExprForStrStartsExpr) { + const auto varX = Variable{"?x"}; + const auto varY = Variable{"?y"}; + evalAndEqualityCheck(strStartsSprql(varX, VocabId(0)), + pr(ge(VocabId(0)), varX)); + evalAndEqualityCheck(strStartsSprql(VocabId(0), varX), + pr(le(VocabId(0)), varX)); + evalAndEqualityCheck(strStartsSprql(varX, varY)); + evalAndEqualityCheck(strStartsSprql(VocabId(0), VocabId(10))); +} + // Test that the conditions required for a correct merge of child // PrefilterExpressions are properly checked during the PrefilterExpression // construction procedure. This check is applied in the SparqlExpression (for From f489b247f12b29c3a556f39fdb40ff6583d1fa47 Mon Sep 17 00:00:00 2001 From: realHannes Date: Mon, 11 Nov 2024 23:42:30 +0100 Subject: [PATCH 50/81] few additional adaptations --- src/engine/Filter.cpp | 11 +++---- src/engine/IndexScan.cpp | 9 +++--- src/engine/IndexScan.h | 4 +-- src/engine/Operation.h | 10 +++++- .../RelationalExpressions.cpp | 31 +++++++++---------- .../sparqlExpressions/StringExpressions.cpp | 18 +++++------ src/index/CompressedBlockPrefiltering.cpp | 7 ++--- src/index/Permutation.cpp | 3 +- 8 files changed, 49 insertions(+), 44 deletions(-) diff --git a/src/engine/Filter.cpp b/src/engine/Filter.cpp index 1e0dd2b443..be8009b617 100644 --- a/src/engine/Filter.cpp +++ b/src/engine/Filter.cpp @@ -48,12 +48,11 @@ string Filter::getDescriptor() const { void Filter::setPrefilterExpressionForIndexScanChildren() { const std::vector& prefilterVec = _expression.getPrefilterExpressionForMetadata(); - this->forAllDescendants( - [&prefilterVec](const QueryExecutionTree* ptr) -> void { - if (ptr) { - ptr->setPrefilterExpression(prefilterVec); - } - }); + this->forAllDescendants([&prefilterVec](const QueryExecutionTree* ptr) { + if (ptr) { + ptr->setPrefilterExpression(prefilterVec); + } + }); } // _____________________________________________________________________________ diff --git a/src/engine/IndexScan.cpp b/src/engine/IndexScan.cpp index 80f593d4d9..650fc3adc3 100644 --- a/src/engine/IndexScan.cpp +++ b/src/engine/IndexScan.cpp @@ -157,7 +157,7 @@ VariableToColumnMap IndexScan::computeVariableToColumnMap() const { //______________________________________________________________________________ std::vector IndexScan::applyFilterBlockMetadata( - const std::vector& blocks) const { + std::vector&& blocks) const { std::ranges::for_each(prefilters_, [&blocks](const PrefilterIndexPair& pair) { pair.first->evaluate(blocks, pair.second); }); @@ -276,7 +276,7 @@ ScanSpecificationAsTripleComponent IndexScan::getScanSpecificationTc() const { // _____________________________________________________________________________ Permutation::IdTableGenerator IndexScan::getLazyScan( - std::vector blocks) const { + std::vector&& blocks) const { // If there is a LIMIT or OFFSET clause that constrains the scan // (which can happen with an explicit subquery), we cannot use the prefiltered // blocks, as we currently have no mechanism to include limits and offsets @@ -344,7 +344,8 @@ IndexScan::lazyScanForJoinOfTwoScans(const IndexScan& s1, const IndexScan& s2) { auto [blocks1, blocks2] = CompressedRelationReader::getBlocksForJoin( metaBlocks1.value(), metaBlocks2.value()); - std::array result{s1.getLazyScan(blocks1), s2.getLazyScan(blocks2)}; + std::array result{s1.getLazyScan(std::move(blocks1)), + s2.getLazyScan(std::move(blocks2))}; result[0].details().numBlocksAll_ = metaBlocks1.value().blockMetadata_.size(); result[1].details().numBlocksAll_ = metaBlocks2.value().blockMetadata_.size(); return result; @@ -365,7 +366,7 @@ Permutation::IdTableGenerator IndexScan::lazyScanForJoinOfColumnWithScan( auto blocks = CompressedRelationReader::getBlocksForJoin(joinColumn, metaBlocks1.value()); - auto result = getLazyScan(blocks); + auto result = getLazyScan(std::move(blocks)); result.details().numBlocksAll_ = metaBlocks1.value().blockMetadata_.size(); return result; } diff --git a/src/engine/IndexScan.h b/src/engine/IndexScan.h index 6055b5a1a0..540acd5fa1 100644 --- a/src/engine/IndexScan.h +++ b/src/engine/IndexScan.h @@ -147,7 +147,7 @@ class IndexScan final : public Operation { // Filter relevant `CompressedBlockMetadata` blocks by applying the // `PrefilterExpression`s from `prefilters_`. std::vector applyFilterBlockMetadata( - const std::vector& blocks) const; + std::vector&& blocks) const; // Return the (lazy) `IdTable` for this `IndexScan` in chunks. Result::Generator chunkedIndexScan() const; @@ -157,6 +157,6 @@ class IndexScan final : public Operation { // Helper functions for the public `getLazyScanFor...` methods and // `chunkedIndexScan` (see above). Permutation::IdTableGenerator getLazyScan( - std::vector blocks) const; + std::vector&& blocks) const; std::optional getMetadataForScan() const; }; diff --git a/src/engine/Operation.h b/src/engine/Operation.h index d1004ceace..8e642ed442 100644 --- a/src/engine/Operation.h +++ b/src/engine/Operation.h @@ -79,7 +79,15 @@ class Operation { // Set `PrefilterExpression`s (for `IndexScan`). virtual void setPrefilterExpression( - const std::vector&){}; + const std::vector&){ + // The prefiltering procedure is implemented by applying the + // PrefilterExpressions directly on the CompressedBlockMetadata. + // This is currently done while performing the result computation for + // IndexScan. + // (1) Overwrite this method for the derived IndexScan class. + // (2) The default method for all other derived classes is implemented + // here, no PrefilterExpressions need to be set. + }; // Apply the provided void function for all descendants. The given function // must be invocable with a `const QueryExecutionTree*` object. diff --git a/src/engine/sparqlExpressions/RelationalExpressions.cpp b/src/engine/sparqlExpressions/RelationalExpressions.cpp index 4f2f28f80e..bb349878da 100644 --- a/src/engine/sparqlExpressions/RelationalExpressions.cpp +++ b/src/engine/sparqlExpressions/RelationalExpressions.cpp @@ -448,22 +448,21 @@ RelationalExpression::getPrefilterExpressionForMetadata( }; const auto tryGetPrefilterExprVariablePairVec = - [&mirroredExpression]( - const SparqlExpression* child0, const SparqlExpression* child1, - bool reversed) -> std::vector { - std::vector resVec{}; - if (const auto* variable = - dynamic_cast(child0)) { - if (const auto* valueId = dynamic_cast(child1)) { - resVec.emplace_back(std::make_pair( - reversed ? mirroredExpression(valueId->value()) - : std::make_unique>( - valueId->value()), - variable->value())); - } - } - return resVec; - }; + [&mirroredExpression](const SparqlExpression* child0, + const SparqlExpression* child1, bool reversed) { + std::vector resVec{}; + if (const auto* variable = + dynamic_cast(child0)) { + if (const auto* valueId = dynamic_cast(child1)) { + resVec.emplace_back( + reversed ? mirroredExpression(valueId->value()) + : std::make_unique>( + valueId->value()), + variable->value()); + } + } + return resVec; + }; // Option 1: // RelationalExpression containing a VariableExpression as the first child // and an IdExpression as the second child. diff --git a/src/engine/sparqlExpressions/StringExpressions.cpp b/src/engine/sparqlExpressions/StringExpressions.cpp index 85753e96f8..2392ec2b18 100644 --- a/src/engine/sparqlExpressions/StringExpressions.cpp +++ b/src/engine/sparqlExpressions/StringExpressions.cpp @@ -244,23 +244,23 @@ class StrStartsExpressionImpl : public NaryExpression { AD_CORRECTNESS_CHECK(this->N == 2); const SparqlExpression* child0 = this->getNthChild(0).value(); const SparqlExpression* child1 = this->getNthChild(1).value(); - const auto getPrefilterExprVariableVec = - [](const SparqlExpression* child0, const SparqlExpression* child1, - bool startsWithVar) -> std::vector { + const auto getPrefilterExprVariableVec = [](const SparqlExpression* child0, + const SparqlExpression* child1, + bool startsWithVar) { using namespace prefilterExpressions; std::vector resVec{}; if (const auto* variable = dynamic_cast(child0)) { if (const auto* valueId = dynamic_cast(child1)) { !startsWithVar - // make {<(>= VocabId(n)), ?var>} (Option 1) - ? resVec.emplace_back(std::make_pair( + // Will return: {<(>= VocabId(n)), ?var>} (Option 1) + ? resVec.emplace_back( std::make_unique(valueId->value()), - variable->value())) - // make {<(<= VocabId(n)), ?var>} (Option 2) - : resVec.emplace_back(std::make_pair( + variable->value()) + // Will return: {<(<= VocabId(n)), ?var>} (Option 2) + : resVec.emplace_back( std::make_unique(valueId->value()), - variable->value())); + variable->value()); } } return resVec; diff --git a/src/index/CompressedBlockPrefiltering.cpp b/src/index/CompressedBlockPrefiltering.cpp index 6300d12307..f7b7af5381 100644 --- a/src/index/CompressedBlockPrefiltering.cpp +++ b/src/index/CompressedBlockPrefiltering.cpp @@ -269,7 +269,7 @@ std::vector RelationalExpression::evaluateImpl( template bool RelationalExpression::operator==( const PrefilterExpression& other) const { - const RelationalExpression* otherRelational = + const auto* otherRelational = dynamic_cast*>(&other); if (!otherRelational) { return false; @@ -333,7 +333,7 @@ std::vector LogicalExpression::evaluateImpl( template bool LogicalExpression::operator==( const PrefilterExpression& other) const { - const LogicalExpression* otherlogical = + const auto* otherlogical = dynamic_cast*>(&other); if (!otherlogical) { return false; @@ -382,8 +382,7 @@ std::vector NotExpression::evaluateImpl( //______________________________________________________________________________ bool NotExpression::operator==(const PrefilterExpression& other) const { - const NotExpression* otherNotExpression = - dynamic_cast(&other); + const auto* otherNotExpression = dynamic_cast(&other); if (!otherNotExpression) { return false; } diff --git a/src/index/Permutation.cpp b/src/index/Permutation.cpp index cbae8ce499..c331c3a296 100644 --- a/src/index/Permutation.cpp +++ b/src/index/Permutation.cpp @@ -66,8 +66,7 @@ IdTable Permutation::scan( const auto& p = getActualPermutation(scanSpec); return p.reader().scan( - scanSpec, - blocks.has_value() ? std::move(blocks.value()) : p.meta_.blockData(), + scanSpec, blocks.has_value() ? blocks.value() : p.meta_.blockData(), additionalColumns, cancellationHandle, getLocatedTriplesForPermutation(locatedTriplesSnapshot), limitOffset); } From 134002175104e9fac226fc66cb2e2e742baed3a5 Mon Sep 17 00:00:00 2001 From: RobinTF <83676088+RobinTF@users.noreply.github.com> Date: Wed, 13 Nov 2024 09:50:33 +0100 Subject: [PATCH 51/81] Make `CartesianProductJoin` lazy. (#1609) This allows the `CartesianProductJoin` operation to produce lazy results as well as to consume a single lazy result itself. Currently only the last child of the Cartesian product can be consumed lazily, but this can in principle be changed in the future. In particular, the most memory-saving way would be to lazily consume the largest child that supports lazy evaluation. --- src/engine/CartesianProductJoin.cpp | 182 ++++++++-- src/engine/CartesianProductJoin.h | 48 ++- test/engine/CartesianProductJoinTest.cpp | 413 ++++++++++++++++++++++- 3 files changed, 602 insertions(+), 41 deletions(-) diff --git a/src/engine/CartesianProductJoin.cpp b/src/engine/CartesianProductJoin.cpp index b9bb514ba7..401ba6df58 100644 --- a/src/engine/CartesianProductJoin.cpp +++ b/src/engine/CartesianProductJoin.cpp @@ -5,11 +5,15 @@ #include "engine/CartesianProductJoin.h" #include "engine/CallFixedSize.h" +#include "util/Views.h" // ____________________________________________________________________________ CartesianProductJoin::CartesianProductJoin( - QueryExecutionContext* executionContext, Children children) - : Operation{executionContext}, children_{std::move(children)} { + QueryExecutionContext* executionContext, Children children, + size_t chunkSize) + : Operation{executionContext}, + children_{std::move(children)}, + chunkSize_{chunkSize} { AD_CONTRACT_CHECK(!children_.empty()); AD_CONTRACT_CHECK(std::ranges::all_of( children_, [](auto& child) { return child != nullptr; })); @@ -80,6 +84,8 @@ float CartesianProductJoin::getMultiplicity([[maybe_unused]] size_t col) { // ____________________________________________________________________________ bool CartesianProductJoin::knownEmptyResult() { + // If children were empty, returning false would be the wrong behavior. + AD_CORRECTNESS_CHECK(!children_.empty()); return std::ranges::any_of(childView(), &Operation::knownEmptyResult); } @@ -122,18 +128,40 @@ void CartesianProductJoin::writeResultColumn(std::span targetColumn, } // ____________________________________________________________________________ -ProtoResult CartesianProductJoin::computeResult( - [[maybe_unused]] bool requestLaziness) { - std::vector> subResults = calculateSubResults(); - - IdTable result = writeAllColumns(subResults); - - // Dereference all the subresult pointers because `getSharedLocalVocabFrom...` - // requires a range of references, not pointers. - auto subResultsDeref = std::views::transform( - subResults, [](auto& x) -> decltype(auto) { return *x; }); - return {std::move(result), resultSortedOn(), - Result::getMergedLocalVocab(subResultsDeref)}; +ProtoResult CartesianProductJoin::computeResult(bool requestLaziness) { + if (knownEmptyResult()) { + return {IdTable{getResultWidth(), getExecutionContext()->getAllocator()}, + resultSortedOn(), LocalVocab{}}; + } + auto [subResults, lazyResult] = calculateSubResults(requestLaziness); + + LocalVocab staticMergedVocab{}; + staticMergedVocab.mergeWith( + subResults | + std::views::transform([](const auto& result) -> const LocalVocab& { + return result->localVocab(); + })); + + if (!requestLaziness) { + AD_CORRECTNESS_CHECK(!lazyResult); + return { + writeAllColumns(subResults | std::views::transform(&Result::idTable), + getLimit()._offset, getLimit().limitOrDefault()), + resultSortedOn(), std::move(staticMergedVocab)}; + } + + if (lazyResult) { + return {createLazyConsumer(std::move(staticMergedVocab), + std::move(subResults), std::move(lazyResult)), + resultSortedOn()}; + } + + // Owning view wrapper to please gcc 11. + return {produceTablesLazily(std::move(staticMergedVocab), + ad_utility::OwningView{std::move(subResults)} | + std::views::transform(&Result::idTable), + getLimit()._offset, getLimit().limitOrDefault()), + resultSortedOn()}; } // ____________________________________________________________________________ @@ -156,19 +184,27 @@ VariableToColumnMap CartesianProductJoin::computeVariableToColumnMap() const { // _____________________________________________________________________________ IdTable CartesianProductJoin::writeAllColumns( - const std::vector>& subResults) const { + std::ranges::random_access_range auto 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 // single result is left. This can probably be done by using the // `ProtoResult`. - auto sizesView = std::views::transform( - subResults, [](const auto& child) { return child->idTable().size(); }); + auto sizesView = std::views::transform(idTables, &IdTable::size); auto totalResultSize = std::reduce(sizesView.begin(), sizesView.end(), 1UL, std::multiplies{}); - size_t totalSizeIncludingLimit = getLimit().actualSize(totalResultSize); - size_t offset = getLimit().actualOffset(totalResultSize); + if (!std::ranges::empty(idTables) && sizesView.back() != 0) { + totalResultSize += (totalResultSize / sizesView.back()) * lastTableOffset; + } else { + AD_CORRECTNESS_CHECK(lastTableOffset == 0); + } + + LimitOffsetClause limitOffset{limit, offset}; + size_t totalSizeIncludingLimit = limitOffset.actualSize(totalResultSize); + offset = limitOffset.actualOffset(totalResultSize); try { result.resize(totalSizeIncludingLimit); @@ -187,11 +223,12 @@ IdTable CartesianProductJoin::writeAllColumns( // The index of the next column in the output that hasn't been written so // far. size_t resultColIdx = 0; - for (auto& subResultPtr : subResults) { - const auto& input = subResultPtr->idTable(); + for (const auto& input : idTables) { + size_t extraOffset = + &input == &idTables.back() ? lastTableOffset * groupSize : 0; for (const auto& inputCol : input.getColumns()) { decltype(auto) resultCol = result.getColumn(resultColIdx); - writeResultColumn(resultCol, inputCol, groupSize, offset); + writeResultColumn(resultCol, inputCol, groupSize, offset - extraOffset); ++resultColIdx; } groupSize *= input.numRows(); @@ -201,8 +238,9 @@ IdTable CartesianProductJoin::writeAllColumns( } // _____________________________________________________________________________ -std::vector> -CartesianProductJoin::calculateSubResults() { +std::pair>, + std::shared_ptr> +CartesianProductJoin::calculateSubResults(bool requestLaziness) { std::vector> subResults; // We don't need to fully materialize the child results if we have a LIMIT // specified and an OFFSET of 0. @@ -214,23 +252,40 @@ CartesianProductJoin::calculateSubResults() { limitIfPresent = std::nullopt; } + std::shared_ptr lazyResult = nullptr; + auto children = childView(); + AD_CORRECTNESS_CHECK(!std::ranges::empty(children)); // Get all child results (possibly with limit, see above). - for (auto& child : childView()) { + for (Operation& child : children) { if (limitIfPresent.has_value() && child.supportsLimit()) { child.setLimit(limitIfPresent.value()); } - subResults.push_back(child.getResult()); + // 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. + bool isLast = &child == &children.back(); + bool requestLazy = requestLaziness && isLast; + auto result = child.getResult( + false, requestLazy ? ComputationMode::LAZY_IF_SUPPORTED + : ComputationMode::FULLY_MATERIALIZED); + + if (!result->isFullyMaterialized()) { + AD_CORRECTNESS_CHECK(isLast); + lazyResult = std::move(result); + continue; + } - const auto& table = subResults.back()->idTable(); + const auto& table = result->idTable(); // Early stopping: If one of the results is empty, we can stop early. if (table.empty()) { + // Push so the total size will be zero. + subResults.push_back(std::move(result)); break; } // If one of the children is the neutral element (because of a triple with // zero variables), we can simply ignore it here. if (table.numRows() == 1 && table.numColumns() == 0) { - subResults.pop_back(); continue; } // Example for the following calculation: If we have a LIMIT of 1000 and @@ -238,10 +293,73 @@ CartesianProductJoin::calculateSubResults() { // needs to evaluate only its first 10 results. The +1 is because integer // divisions are rounded down by default. if (limitIfPresent.has_value()) { - limitIfPresent.value()._limit = limitIfPresent.value()._limit.value() / - subResults.back()->idTable().size() + - 1; + limitIfPresent.value()._limit = + limitIfPresent.value()._limit.value() / result->idTable().size() + 1; + } + subResults.push_back(std::move(result)); + } + + return {std::move(subResults), std::move(lazyResult)}; +} + +// _____________________________________________________________________________ +Result::Generator CartesianProductJoin::produceTablesLazily( + LocalVocab mergedVocab, std::ranges::range auto 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, + limitWithChunkSize, lastTableOffset); + size_t tableSize = idTable.size(); + AD_CORRECTNESS_CHECK(tableSize <= limit); + if (!idTable.empty()) { + offset += tableSize; + limit -= tableSize; + co_yield {std::move(idTable), mergedVocab.clone()}; + } + if (tableSize < limitWithChunkSize) { + break; + } + } +} + +// _____________________________________________________________________________ +Result::Generator CartesianProductJoin::createLazyConsumer( + LocalVocab staticMergedVocab, + std::vector> subresults, + std::shared_ptr lazyResult) const { + AD_CONTRACT_CHECK(lazyResult); + size_t limit = getLimit().limitOrDefault(); + size_t offset = getLimit()._offset; + std::vector> idTables; + idTables.reserve(subresults.size() + 1); + for (const auto& result : subresults) { + idTables.emplace_back(result->idTable()); + } + size_t lastTableOffset = 0; + for (auto& [idTable, localVocab] : lazyResult->idTables()) { + if (idTable.empty()) { + continue; + } + idTables.emplace_back(idTable); + localVocab.mergeWith(std::span{&staticMergedVocab, 1}); + size_t producedTableSize = 0; + for (auto& idTableAndVocab : produceTablesLazily( + std::move(localVocab), + std::views::transform( + idTables, + [](const auto& wrapper) -> const IdTable& { return wrapper; }), + offset, limit, lastTableOffset)) { + producedTableSize += idTableAndVocab.idTable_.size(); + co_yield idTableAndVocab; + } + AD_CORRECTNESS_CHECK(limit >= producedTableSize); + limit -= producedTableSize; + if (limit == 0) { + break; } + offset += producedTableSize; + lastTableOffset += idTable.size(); + idTables.pop_back(); } - return subResults; } diff --git a/src/engine/CartesianProductJoin.h b/src/engine/CartesianProductJoin.h index de130a739e..72f5ff9a12 100644 --- a/src/engine/CartesianProductJoin.h +++ b/src/engine/CartesianProductJoin.h @@ -15,6 +15,7 @@ class CartesianProductJoin : public Operation { private: Children children_; + size_t chunkSize_; // Access to the actual operations of the children. // TODO We can move this whole children management into a base class @@ -33,9 +34,11 @@ class CartesianProductJoin : public Operation { public: // Constructor. `children` must not be empty and the variables of all the - // children must be disjoint, else an `AD_CONTRACT_CHECK` fails. + // children must be disjoint, else an `AD_CONTRACT_CHECK` fails. Accept a + // custom `chunkSize` for chunking lazy results. explicit CartesianProductJoin(QueryExecutionContext* executionContext, - Children children); + Children children, + size_t chunkSize = 1'000'000); /// get non-owning pointers to all the held subtrees to actually use the /// Execution Trees as trees @@ -77,7 +80,7 @@ class CartesianProductJoin : public Operation { private: //! Compute the result of the query-subtree rooted at this element.. - ProtoResult computeResult([[maybe_unused]] bool requestLaziness) override; + ProtoResult computeResult(bool requestLaziness) override; // Copy each element from the `inputColumn` `groupSize` times to the // `targetColumn`. Repeat until the `targetColumn` is completely filled. Skip @@ -88,9 +91,38 @@ class CartesianProductJoin : public Operation { size_t offset) const; // Write all columns of the subresults into an `IdTable` and return it. - IdTable writeAllColumns( - const std::vector>& subResults) const; - - // Calculate the subresults of the children and store them into a vector. - std::vector> calculateSubResults(); + // `offset` indicates how many rows to skip in the result and `limit` how many + // 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; + + // 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 + // the vector and returned as the first element of the pair. Otherwise this + // will be an empty shared_ptr. The vector is guaranteed to only contain fully + // materialized results. + std::pair>, + std::shared_ptr> + calculateSubResults(bool requestLaziness); + + // Take a range of `IdTable`s and a corresponding `LocalVocab` and yield + // `IdTable`s with sizes up to `chunkSize_` until the limit is reached. + // `offset` indicates the total offset of the desired result. + // `limit` is the maximum number of rows to yield. + // `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; + + // Similar to `produceTablesLazily` but can handle a single lazy result. + Result::Generator createLazyConsumer( + LocalVocab staticMergedVocab, + std::vector> subresults, + std::shared_ptr lazyResult) const; }; diff --git a/test/engine/CartesianProductJoinTest.cpp b/test/engine/CartesianProductJoinTest.cpp index cd05e31b9b..90488dca5c 100644 --- a/test/engine/CartesianProductJoinTest.cpp +++ b/test/engine/CartesianProductJoinTest.cpp @@ -3,7 +3,6 @@ // Author: Johannes Kalmbach #include -#include #include "../engine/ValuesForTesting.h" #include "../util/GTestHelpers.h" @@ -14,6 +13,8 @@ using namespace ad_utility::testing; using ad_utility::source_location; +constexpr size_t CHUNK_SIZE = 1'000; +using O = std::optional; // Create a `CartesianProductJoin` the children of which are `ValuesForTesting` // with results create from the `inputs`. The children will have disjoint sets @@ -218,3 +219,413 @@ TEST(CartesianProductJoin, variableColumnMap) { EXPECT_THAT(join.getExternallyVisibleVariableColumns(), ::testing::UnorderedElementsAreArray(expectedVariables)); } + +// First parameter indicates "seed" for splitting the last input table. +// Second parameter indicates the offset for the LIMIT clause. +// Third parameter indicates the limit for the LIMIT clause. +class CartesianProductJoinLazyTest + : public testing::TestWithParam< + std::tuple>> { + // Randomly split `idTable` into equivalent subsets of itself at pseudo-random + // positions. + static std::vector splitIntoRandomSubtables(const IdTable& idTable) { + // Ensure results are reproducible. + std::mt19937_64 generator{std::get<0>(GetParam())}; + // The average size of splits. + size_t averageSplitSize = + std::uniform_int_distribution{0, idTable.size()}(generator); + std::uniform_int_distribution distribution{0, averageSplitSize}; + std::vector result; + result.emplace_back(idTable.numColumns(), idTable.getAllocator()); + for (const auto& row : idTable) { + result.back().emplace_back(); + result.back().back() = row; + if (distribution(generator) == 0) { + result.emplace_back(idTable.numColumns(), idTable.getAllocator()); + } + } + return result; + } + + // Counter to ensure unique variables. + static size_t varIndex; + + // Create a unique variable with the scheme: ?v0, ?v1, ... + static std::vector> makeUniqueVariables( + const IdTable& idTable) { + std::vector> result; + for (size_t i = 0; i < idTable.numColumns(); ++i) { + result.emplace_back(Variable{"?v" + std::to_string(varIndex++)}); + } + return result; + } + + protected: + // Create a join instance with the given `tables`. The last table is split for + // tests cases with a non-zero seed. + static CartesianProductJoin makeJoin(std::vector tables) { + AD_CONTRACT_CHECK(tables.size() >= 2); + auto* qec = ad_utility::testing::getQec(); + CartesianProductJoin::Children children{}; + for (IdTable& table : std::span{tables}.subspan(0, tables.size() - 1)) { + children.push_back(ad_utility::makeExecutionTree( + qec, table.clone(), makeUniqueVariables(table))); + } + if (std::get<0>(GetParam()) == 0) { + children.push_back(ad_utility::makeExecutionTree( + qec, tables.back().clone(), makeUniqueVariables(tables.back()), false, + std::vector{}, LocalVocab{}, std::nullopt, true)); + } else { + children.push_back(ad_utility::makeExecutionTree( + qec, splitIntoRandomSubtables(tables.back()), + makeUniqueVariables(tables.back()))); + } + CartesianProductJoin join{qec, std::move(children), CHUNK_SIZE}; + join.setLimit(LimitOffsetClause{std::get<2>(GetParam()), getOffset()}); + return join; + } + + // Get the offset. + static size_t getOffset() { return std::get<1>(GetParam()); } + + // Get the limit if present, otherwise return the maximum size_t value. + static size_t getLimit() { + return std::get<2>(GetParam()).has_value() + ? std::get<2>(GetParam()).value() + : std::numeric_limits::max(); + } + + // Clamp `maximum` size to account for the limit and offset. + static size_t clampSize(size_t maximum) { + // Apply offset + maximum -= std::min(maximum, getOffset()); + // Apply limit + maximum = std::min(maximum, getLimit()); + return maximum; + } + + // Calculate the expected size of the Cartesian product without considering + // limit and offset. + static size_t getExpectedSize(const std::vector& idTables) { + size_t totalExpectedSize = 1; + for (const auto& idTable : idTables) { + totalExpectedSize *= idTable.size(); + } + return totalExpectedSize; + } + + // Count how many times a certain id is expected to occur in the Cartesian + // product within the respective column without considering limit and offset. + static std::vector getOccurenceCountWithoutLimit( + const std::vector& idTables) { + size_t totalExpectedSize = getExpectedSize(idTables); + std::vector result; + for (const auto& idTable : idTables) { + for (size_t i = 0; i < idTable.numColumns(); ++i) { + result.push_back(totalExpectedSize / idTable.size()); + } + } + return result; + } + + // Return the expected number of unique for every column in the result set. + static std::vector getValueCount( + const std::vector& idTables) { + std::vector result; + for (const auto& idTable : idTables) { + for (size_t i = 0; i < idTable.numColumns(); ++i) { + result.push_back(idTable.size()); + } + } + return result; + } + + // Trim the `idTable` to the given `offset` and `limit`. + static IdTable trimToLimitAndOffset(IdTable idTable, size_t offset, + size_t limit) { + idTable.erase(idTable.begin(), + idTable.begin() + std::min(idTable.size(), offset)); + idTable.erase(idTable.begin() + std::min(idTable.size(), limit), + idTable.end()); + return idTable; + } + + // Count the individual entries in the result set and check those counts match + // the expected counts. For results with limit and offset it can't make strict + // checks because the order is not guaranteed by the operation so we have to + // check if the error is within a certain error margin. + static void expectCorrectResult( + CartesianProductJoin& join, size_t expectedSize, + const std::vector& occurenceCounts, + const std::vector& valueCount, + source_location loc = source_location::current()) { + auto trace = generateLocationTrace(loc); + join.getExecutionContext()->getQueryTreeCache().clearAll(); + Result result = join.computeResultOnlyForTesting(true); + ASSERT_FALSE(result.isFullyMaterialized()); + std::vector> counter{ + occurenceCounts.size()}; + size_t elementCounter = 0; + for (auto& [idTable, localVocab] : result.idTables()) { + EXPECT_FALSE(idTable.empty()); + EXPECT_LE(idTable.size(), CHUNK_SIZE); + for (size_t i = 0; i < idTable.numColumns(); ++i) { + for (Id id : idTable.getColumn(i)) { + counter.at(i)[id.getBits()]++; + } + } + elementCounter += idTable.size(); + } + EXPECT_EQ(elementCounter, clampSize(expectedSize)); + size_t penalty = expectedSize - clampSize(expectedSize); + using namespace ::testing; + for (size_t col = 0; col < occurenceCounts.size(); col++) { + for (const auto& [id, count] : counter.at(col)) { + size_t occurenceCount = occurenceCounts.at(col); + EXPECT_LE(count, occurenceCount) + << "Column " << col << " did contain too many occurrences for id " + << Id::fromBits(id); + EXPECT_GE(count, occurenceCount - std::min(occurenceCount, penalty)) + << "Column " << col << " did contain too few occurrences for id " + << Id::fromBits(id); + } + size_t expectedCount = valueCount.at(col); + EXPECT_LE(counter.at(col).size(), expectedCount) + << "The id table did contain too many unique values " + "for column " + << col; + EXPECT_GE(counter.at(col).size(), + expectedCount - std::min(expectedCount, penalty)) + << "The id table did contain too few unique values " + "for column " + << col; + } + } + + // Fills the column with index `column` in the `table` with the values from + // `start` to `end` wrapped as Ids. + static void fillColumn(IdTable& table, size_t column, int64_t start, + int64_t end) { + std::ranges::copy( + std::views::iota(start, end) | std::views::transform(Id::makeFromInt), + table.getColumn(column).begin()); + } +}; + +// Out of line initialization because C++ needs this for some reason. +size_t CartesianProductJoinLazyTest::varIndex = 0; + +// _____________________________________________________________________________ +TEST_P(CartesianProductJoinLazyTest, allTablesSmallerThanChunk) { + // All tables combined smaller than chunk size + std::vector tables; + tables.push_back(makeIdTableFromVector({{0, 10}, {1, 11}})); + tables.push_back(makeIdTableFromVector({{100}, {101}, {102}})); + tables.push_back(makeIdTableFromVector({{1000}})); + tables.push_back(makeIdTableFromVector({{10000, 100000}, {10001, 100001}})); + + auto occurenceCounts = getOccurenceCountWithoutLimit(tables); + auto valueCount = getValueCount(tables); + size_t expectedSize = getExpectedSize(tables); + auto join = makeJoin(std::move(tables)); + expectCorrectResult(join, expectedSize, occurenceCounts, valueCount); + + join.getExecutionContext()->getQueryTreeCache().clearAll(); + // For a table this small we can assert the result directly + auto result = join.computeResultOnlyForTesting(true); + ASSERT_FALSE(result.isFullyMaterialized()); + IdTable reference = makeIdTableFromVector({ + {0, 10, 100, 1000, 10000, 100000}, + {1, 11, 100, 1000, 10000, 100000}, + {0, 10, 101, 1000, 10000, 100000}, + {1, 11, 101, 1000, 10000, 100000}, + {0, 10, 102, 1000, 10000, 100000}, + {1, 11, 102, 1000, 10000, 100000}, + {0, 10, 100, 1000, 10001, 100001}, + {1, 11, 100, 1000, 10001, 100001}, + {0, 10, 101, 1000, 10001, 100001}, + {1, 11, 101, 1000, 10001, 100001}, + {0, 10, 102, 1000, 10001, 100001}, + {1, 11, 102, 1000, 10001, 100001}, + }); + + auto materializedResult = aggregateTables(std::move(result.idTables()), 6); + EXPECT_EQ( + materializedResult.first, + trimToLimitAndOffset(std::move(reference), getOffset(), getLimit())); +} + +// _____________________________________________________________________________ +TEST_P(CartesianProductJoinLazyTest, leftTableBiggerThanChunk) { + // Leftmost table individually larger than chunk size + IdTable bigTable{1, ad_utility::testing::makeAllocator()}; + bigTable.resize(CHUNK_SIZE + 1); + fillColumn(bigTable, 0, 0, CHUNK_SIZE + 1); + std::vector tables; + tables.push_back(bigTable.clone()); + tables.push_back(makeIdTableFromVector({{0, 10}, {1, 11}, {2, 12}})); + tables.push_back(makeIdTableFromVector({{100}})); + + auto occurenceCounts = getOccurenceCountWithoutLimit(tables); + auto valueCount = getValueCount(tables); + size_t expectedSize = getExpectedSize(tables); + auto join = makeJoin(std::move(tables)); + expectCorrectResult(join, expectedSize, occurenceCounts, valueCount); + + bigTable.addEmptyColumn(); + bigTable.addEmptyColumn(); + bigTable.addEmptyColumn(); + auto fillWithVocabValue = [&bigTable](size_t column, uint64_t vocabIndex) { + std::ranges::fill(bigTable.getColumn(column), + Id::makeFromVocabIndex(VocabIndex::make(vocabIndex))); + }; + fillWithVocabValue(3, 100); + + join.getExecutionContext()->getQueryTreeCache().clearAll(); + auto result = join.computeResultOnlyForTesting(true); + ASSERT_FALSE(result.isFullyMaterialized()); + + fillWithVocabValue(1, 0); + fillWithVocabValue(2, 10); + IdTable reference = bigTable.clone(); + + fillWithVocabValue(1, 1); + fillWithVocabValue(2, 11); + reference.insertAtEnd(bigTable); + + fillWithVocabValue(1, 2); + fillWithVocabValue(2, 12); + reference.insertAtEnd(bigTable); + + auto materializedResult = aggregateTables(std::move(result.idTables()), 4); + EXPECT_EQ( + materializedResult.first, + trimToLimitAndOffset(std::move(reference), getOffset(), getLimit())); +} + +// _____________________________________________________________________________ +TEST_P(CartesianProductJoinLazyTest, tablesAccumulatedBiggerThanChunk) { + // All tables individually smaller than chunk size, but larger when combined. + size_t rootSize = std::sqrt(CHUNK_SIZE) + 1; + IdTable table1{2, ad_utility::testing::makeAllocator()}; + table1.resize(rootSize); + fillColumn(table1, 0, 0, rootSize); + fillColumn(table1, 1, -rootSize, 0); + + IdTable table2{2, ad_utility::testing::makeAllocator()}; + table2.resize(rootSize); + fillColumn(table2, 0, rootSize, rootSize * 2); + fillColumn(table2, 1, -rootSize * 2, -rootSize); + + std::vector tables; + tables.push_back(std::move(table1)); + tables.push_back(std::move(table2)); + tables.push_back(makeIdTableFromVector({{0, 10}, {1, 11}, {2, 12}})); + tables.push_back( + makeIdTableFromVector({{100, 1000}, {101, 1001}, {102, 1002}})); + + auto occurenceCounts = getOccurenceCountWithoutLimit(tables); + auto valueCount = getValueCount(tables); + size_t expectedSize = getExpectedSize(tables); + auto join = makeJoin(std::move(tables)); + expectCorrectResult(join, expectedSize, occurenceCounts, valueCount); +} + +// _____________________________________________________________________________ +TEST_P(CartesianProductJoinLazyTest, twoTablesMatchChunkSize) { + // 2 tables multiplied together match chunk size exactly + size_t rootSize = std::sqrt(CHUNK_SIZE); + IdTable table1{2, ad_utility::testing::makeAllocator()}; + table1.resize(rootSize); + fillColumn(table1, 0, 0, rootSize); + fillColumn(table1, 1, -rootSize, 0); + + IdTable table2{2, ad_utility::testing::makeAllocator()}; + table2.resize(rootSize); + fillColumn(table2, 0, rootSize, rootSize * 2); + fillColumn(table2, 1, -rootSize * 2, -rootSize); + + std::vector tables; + tables.push_back(std::move(table1)); + tables.push_back(std::move(table2)); + + auto occurenceCounts = getOccurenceCountWithoutLimit(tables); + auto valueCount = getValueCount(tables); + size_t expectedSize = getExpectedSize(tables); + auto join = makeJoin(std::move(tables)); + expectCorrectResult(join, expectedSize, occurenceCounts, valueCount); +} + +// _____________________________________________________________________________ +TEST(CartesianProductJoinLazy, lazyTableTurnsOutEmpty) { + // Test we get an empty lazy result when the lazy table is empty. + auto* qec = ad_utility::testing::getQec(); + IdTable nonEmpty = makeIdTableFromVector({{1}}); + IdTable empty{1, ad_utility::testing::makeAllocator()}; + CartesianProductJoin::Children children{}; + children.push_back(ad_utility::makeExecutionTree( + qec, std::move(nonEmpty), + std::vector>{{Variable{"?a"}}})); + children.push_back( + ad_utility::makeExecutionTree( + qec, std::move(empty), + std::vector>{{Variable{"?b"}}})); + CartesianProductJoin join{qec, std::move(children)}; + + auto result = join.computeResultOnlyForTesting(true); + ASSERT_FALSE(result.isFullyMaterialized()); + auto& generator = result.idTables(); + ASSERT_EQ(generator.begin(), generator.end()); +} + +// _____________________________________________________________________________ +TEST(CartesianProductJoinLazy, lazyTableTurnsOutEmptyWithEmptyGenerator) { + // Test we get an empty lazy result when the lazy operation returns an empty + // generator. + auto* qec = ad_utility::testing::getQec(); + IdTable nonEmpty = makeIdTableFromVector({{1}}); + IdTable empty{1, ad_utility::testing::makeAllocator()}; + CartesianProductJoin::Children children{}; + children.push_back(ad_utility::makeExecutionTree( + qec, std::move(nonEmpty), + std::vector>{{Variable{"?a"}}})); + children.push_back( + ad_utility::makeExecutionTree( + qec, std::vector{}, + std::vector>{{Variable{"?b"}}})); + CartesianProductJoin join{qec, std::move(children)}; + + auto result = join.computeResultOnlyForTesting(true); + ASSERT_FALSE(result.isFullyMaterialized()); + auto& generator = result.idTables(); + ASSERT_EQ(generator.begin(), generator.end()); +} + +// _____________________________________________________________________________ + +using ::testing::Range; +using ::testing::Values; +INSTANTIATE_TEST_SUITE_P( + CartesianProdctJoinTestSuite, CartesianProductJoinLazyTest, + ::testing::Combine( + Range(0, 5), + Values(0, 1, 25, CHUNK_SIZE, CHUNK_SIZE + 1, CHUNK_SIZE * 2), + Values(O{0}, O{1}, O{25}, O{CHUNK_SIZE}, O{CHUNK_SIZE * 2}, + O{CHUNK_SIZE * 10}, std::nullopt)), + [](const testing::TestParamInfo< + std::tuple>>& info) { + std::ostringstream stream; + if (std::get<0>(info.param) == 0) { + stream << "FullyMaterialized"; + } else { + stream << "WithSplitSeed_" << std::get<0>(info.param); + } + stream << "_Offset_" << std::get<1>(info.param); + stream << "_Limit_"; + if (std::get<2>(info.param).has_value()) { + stream << std::get<2>(info.param).value(); + } else { + stream << "None"; + } + return std::move(stream).str(); + }); From 23cea273369bcc333444ff7cdb4944545b5f4db3 Mon Sep 17 00:00:00 2001 From: Hannes Baumann <116301375+realHannes@users.noreply.github.com> Date: Thu, 14 Nov 2024 10:35:11 +0100 Subject: [PATCH 52/81] Get `PrefilterExpression` from `SparqlExpression` (#1613) The `SparqlExpression` base class has been extended with the method `getPrefilterExpressionForMetadata`. This method constructs for suitable (logical) expressions which are used inside a `FILTER` a corresponding `PrefilterExpression` (see PR #1503). These `PrefilterExpression`s can be used to prefilter the blocks of an `IndexScan` by only looking at their metadata. At the moment, the following expressions provide an overriden implementation of `getPrefilterExpressionForMetadata`: `strstarts` (preliminary), `logical-or` and `logical-and` (binary), `logical-not` (unary) and the standard `RelationalExpressions (<, ==, >, <=, >=)`. --- src/engine/sparqlExpressions/CMakeLists.txt | 3 +- .../PrefilterExpressionIndex.cpp} | 26 +- .../PrefilterExpressionIndex.h} | 39 +- .../sparqlExpressions/SparqlExpression.cpp | 7 + .../sparqlExpressions/SparqlExpression.h | 24 +- .../SparqlExpressionPimpl.cpp | 4 +- .../sparqlExpressions/SparqlExpressionPimpl.h | 14 +- src/index/CMakeLists.txt | 1 - test/CMakeLists.txt | 4 +- test/CompressedBlockPrefilteringTest.cpp | 1228 ----------------- ...lterExpressionFromSparqlExpressionTest.cpp | 592 ++++++++ test/PrefilterExpressionIndexTest.cpp | 600 ++++++++ test/PrefilterExpressionTestHelpers.h | 70 + 13 files changed, 1342 insertions(+), 1270 deletions(-) rename src/{index/CompressedBlockPrefiltering.cpp => engine/sparqlExpressions/PrefilterExpressionIndex.cpp} (94%) rename src/{index/CompressedBlockPrefiltering.h => engine/sparqlExpressions/PrefilterExpressionIndex.h} (87%) delete mode 100644 test/CompressedBlockPrefilteringTest.cpp create mode 100644 test/GetPrefilterExpressionFromSparqlExpressionTest.cpp create mode 100644 test/PrefilterExpressionIndexTest.cpp create mode 100644 test/PrefilterExpressionTestHelpers.h diff --git a/src/engine/sparqlExpressions/CMakeLists.txt b/src/engine/sparqlExpressions/CMakeLists.txt index dfe34fa146..17a96d689e 100644 --- a/src/engine/sparqlExpressions/CMakeLists.txt +++ b/src/engine/sparqlExpressions/CMakeLists.txt @@ -18,6 +18,7 @@ add_library(sparqlExpressions ConvertToNumericExpression.cpp RdfTermExpressions.cpp LangExpression.cpp - CountStarExpression.cpp) + CountStarExpression.cpp + PrefilterExpressionIndex.cpp) qlever_target_link_libraries(sparqlExpressions util index Boost::url) diff --git a/src/index/CompressedBlockPrefiltering.cpp b/src/engine/sparqlExpressions/PrefilterExpressionIndex.cpp similarity index 94% rename from src/index/CompressedBlockPrefiltering.cpp rename to src/engine/sparqlExpressions/PrefilterExpressionIndex.cpp index f7b7af5381..b7a7c2fd0e 100644 --- a/src/index/CompressedBlockPrefiltering.cpp +++ b/src/engine/sparqlExpressions/PrefilterExpressionIndex.cpp @@ -2,7 +2,7 @@ // Chair of Algorithms and Data Structures // Author: Hannes Baumann -#include "index/CompressedBlockPrefiltering.h" +#include "engine/sparqlExpressions/PrefilterExpressionIndex.h" #include @@ -286,7 +286,7 @@ std::unique_ptr RelationalExpression::clone() //______________________________________________________________________________ template -std::string RelationalExpression::info( +std::string RelationalExpression::asString( [[maybe_unused]] size_t depth) const { std::stringstream stream; stream << "Prefilter RelationalExpression<" << getRelationalOpStr(Comparison) @@ -352,11 +352,11 @@ std::unique_ptr LogicalExpression::clone() //______________________________________________________________________________ template -std::string LogicalExpression::info(size_t depth) const { +std::string LogicalExpression::asString(size_t depth) const { std::string child1Info = - depth < maxInfoRecursion ? child1_->info(depth + 1) : "MAX_DEPTH"; + depth < maxInfoRecursion ? child1_->asString(depth + 1) : "MAX_DEPTH"; std::string child2Info = - depth < maxInfoRecursion ? child2_->info(depth + 1) : "MAX_DEPTH"; + depth < maxInfoRecursion ? child2_->asString(depth + 1) : "MAX_DEPTH"; std::stringstream stream; stream << "Prefilter LogicalExpression<" << getLogicalOpStr(Operation) << ">\n" @@ -395,15 +395,27 @@ std::unique_ptr NotExpression::clone() const { }; //______________________________________________________________________________ -std::string NotExpression::info(size_t depth) const { +std::string NotExpression::asString(size_t depth) const { std::string childInfo = - depth < maxInfoRecursion ? child_->info(depth + 1) : "MAX_DEPTH"; + depth < maxInfoRecursion ? child_->asString(depth + 1) : "MAX_DEPTH"; std::stringstream stream; stream << "Prefilter NotExpression:\n" << "child {" << childInfo << "}" << std::endl; return stream.str(); } +//______________________________________________________________________________ +// Instanitate templates with specializations (for linking accessibility) +template class RelationalExpression; +template class RelationalExpression; +template class RelationalExpression; +template class RelationalExpression; +template class RelationalExpression; +template class RelationalExpression; + +template class LogicalExpression; +template class LogicalExpression; + namespace detail { //______________________________________________________________________________ void checkPropertiesForPrefilterConstruction( diff --git a/src/index/CompressedBlockPrefiltering.h b/src/engine/sparqlExpressions/PrefilterExpressionIndex.h similarity index 87% rename from src/index/CompressedBlockPrefiltering.h rename to src/engine/sparqlExpressions/PrefilterExpressionIndex.h index 2419d9aea9..a5ca2c8d8d 100644 --- a/src/index/CompressedBlockPrefiltering.h +++ b/src/engine/sparqlExpressions/PrefilterExpressionIndex.h @@ -11,6 +11,16 @@ #include "global/ValueIdComparators.h" #include "index/CompressedRelation.h" +// For certain SparqlExpressions it is possible to perform a prefiltering +// procedure w.r.t. relevant data blocks / ValueId values by making use of the +// available metadata (see CompressedBlockMetadata in CompressedRelation.h) +// while performing the index scan. As a result, the actual SparqlExpression +// evaluation is performed for a smaller IdTable if a PrefilterExpression +// (declared in this file) for the respective SparqlExpression is available and +// compatible with the IndexScan. The following SparqlExpressions construct a +// PrefilterExpression if possible: logical-or, logical-and, logical-negate +// (unary), relational-ops. and strstarts. + namespace prefilterExpressions { //______________________________________________________________________________ @@ -49,7 +59,7 @@ class PrefilterExpression { virtual std::unique_ptr clone() const = 0; // Format content for debugging. - virtual std::string info(size_t depth) const = 0; + virtual std::string asString(size_t depth) const = 0; // Needed for implementing the `NotExpression`. This method is required, // because we logically operate on `BlockMetadata` values which define ranges @@ -78,7 +88,7 @@ class PrefilterExpression { // Format for debugging friend std::ostream& operator<<(std::ostream& str, const PrefilterExpression& expression) { - str << expression.info(0) << "." << std::endl; + str << expression.asString(0) << "." << std::endl; return str; } @@ -110,7 +120,7 @@ class RelationalExpression : public PrefilterExpression { std::unique_ptr logicalComplement() const override; bool operator==(const PrefilterExpression& other) const override; std::unique_ptr clone() const override; - std::string info([[maybe_unused]] size_t depth) const override; + std::string asString(size_t depth) const override; private: std::vector evaluateImpl( @@ -140,7 +150,7 @@ class LogicalExpression : public PrefilterExpression { std::unique_ptr logicalComplement() const override; bool operator==(const PrefilterExpression& other) const override; std::unique_ptr clone() const override; - std::string info(size_t depth) const override; + std::string asString(size_t depth) const override; private: std::vector evaluateImpl( @@ -154,17 +164,18 @@ class NotExpression : public PrefilterExpression { std::unique_ptr child_; public: + // `makeCopy` should always be set to `false`, except when it is called within + // the implementation of the `clone()` method. For the copy construction, + // the `logicalComplement` for the child is omitted because it has + // already been complemented for the original expression. explicit NotExpression(std::unique_ptr child, bool makeCopy = false) - // If we create a copy, the child expression has already been - // complemented while creating the original expression. Thus, just move - // the child. : child_(makeCopy ? std::move(child) : child->logicalComplement()) {} std::unique_ptr logicalComplement() const override; bool operator==(const PrefilterExpression& other) const override; std::unique_ptr clone() const override; - std::string info(size_t depth) const override; + std::string asString(size_t depth) const override; private: std::vector evaluateImpl( @@ -172,18 +183,6 @@ class NotExpression : public PrefilterExpression { size_t evaluationColumn) const override; }; -//______________________________________________________________________________ -// Instanitate templates with specializations (for linking accessibility) -template class RelationalExpression; -template class RelationalExpression; -template class RelationalExpression; -template class RelationalExpression; -template class RelationalExpression; -template class RelationalExpression; - -template class LogicalExpression; -template class LogicalExpression; - //______________________________________________________________________________ // Definition of the RelationalExpression for LT, LE, EQ, NE, GE and GT. using LessThanExpression = prefilterExpressions::RelationalExpression< diff --git a/src/engine/sparqlExpressions/SparqlExpression.cpp b/src/engine/sparqlExpressions/SparqlExpression.cpp index dab49835a8..7e8fa1648c 100644 --- a/src/engine/sparqlExpressions/SparqlExpression.cpp +++ b/src/engine/sparqlExpressions/SparqlExpression.cpp @@ -109,6 +109,13 @@ Estimates SparqlExpression::getEstimatesForFilterExpression( } // _____________________________________________________________________________ +// The default implementation returns an empty vector given that for most +// `SparqlExpressions` no pre-filter procedure is available. Only specific +// expressions that yield boolean values support the construction of +// <`PrefilterExpression`, `Variable`> pairs. For more information, refer to the +// declaration of this method in SparqlExpression.h. `SparqlExpression`s for +// which pre-filtering over the `IndexScan` is supported, override the virtual +// `getPrefilterExpressionForMetadata` method declared there. std::vector SparqlExpression::getPrefilterExpressionForMetadata( [[maybe_unused]] bool isNegated) const { diff --git a/src/engine/sparqlExpressions/SparqlExpression.h b/src/engine/sparqlExpressions/SparqlExpression.h index 83b3ff89ad..1378f10520 100644 --- a/src/engine/sparqlExpressions/SparqlExpression.h +++ b/src/engine/sparqlExpressions/SparqlExpression.h @@ -96,14 +96,24 @@ class SparqlExpression { [[maybe_unused]] const std::optional& primarySortKeyVariable) const; - // Returns a vector with pairs, each containing a `PrefilterExpression` and - // the corresponding `Variable`. The `Variable` indicates which - // column is required to be sorted, and hence is as a consequence also binary - // evaluable regarding the relational (e.g. `>=`) / logical (`&&`, `||` and - // `!`) expressions. + // Returns a vector of pairs, each containing a `PrefilterExpression` and its + // corresponding `Variable`. The `Variable` corresponds to the column (index + // column) for which we want to perform the pre-filter procedure. + // For the following SparqlExpressions, a pre-filter procedure can be + // performed given a suitable PrefilterExpression can be constructed: + // `logical-or`, `logical-and`, `logical-negate` (unary), `relational` and + // `strstarts`. + // `isNegated` is set to `false` by default. This boolean flag is toggled to + // `true` if a `logical-negate` (!) expression (see + // `UnaryNegateExpressionImpl` in NumericUnaryExpressions.cpp) is visited, + // allowing this negation information to be passed to the children of the + // respective expression tree. `isNegated` is used to select the suitable + // merge procedure on the children's `PrefilterExpression`s for `logical-and` + // and `logical-or` when constructing their corresponding vector of + // <`PrefilterExpression`, `Variable`> pairs (see `getMergeFunction` in + // NumericBinaryExpression.cpp). virtual std::vector - getPrefilterExpressionForMetadata( - [[maybe_unused]] bool isNegated = false) const; + getPrefilterExpressionForMetadata(bool isNegated = false) const; // Returns true iff this expression is a simple constant. Default // implementation returns `false`. diff --git a/src/engine/sparqlExpressions/SparqlExpressionPimpl.cpp b/src/engine/sparqlExpressions/SparqlExpressionPimpl.cpp index 39dd932231..99d3f19e3f 100644 --- a/src/engine/sparqlExpressions/SparqlExpressionPimpl.cpp +++ b/src/engine/sparqlExpressions/SparqlExpressionPimpl.cpp @@ -94,8 +94,8 @@ auto SparqlExpressionPimpl::getEstimatesForFilterExpression( //_____________________________________________________________________________ std::vector -SparqlExpressionPimpl::getPrefilterExpressionForMetadata(bool isNegated) const { - return _pimpl->getPrefilterExpressionForMetadata(isNegated); +SparqlExpressionPimpl::getPrefilterExpressionForMetadata() const { + return _pimpl->getPrefilterExpressionForMetadata(); } // _____________________________________________________________________________ diff --git a/src/engine/sparqlExpressions/SparqlExpressionPimpl.h b/src/engine/sparqlExpressions/SparqlExpressionPimpl.h index 48ee2b2407..3c00b7e802 100644 --- a/src/engine/sparqlExpressions/SparqlExpressionPimpl.h +++ b/src/engine/sparqlExpressions/SparqlExpressionPimpl.h @@ -9,7 +9,7 @@ #include #include "engine/VariableToColumnMap.h" -#include "index/CompressedBlockPrefiltering.h" +#include "engine/sparqlExpressions/PrefilterExpressionIndex.h" #include "parser/data/Variable.h" #include "util/HashMap.h" #include "util/HashSet.h" @@ -19,6 +19,12 @@ namespace sparqlExpression { class SparqlExpression; struct EvaluationContext; +// Improve return type readability. +// Pair containing `PrefilterExpression` pointer and a `Variable`. +using PrefilterExprVariablePair = + std::pair, + Variable>; + // Improve return type readability. // Pair containing `PrefilterExpression` pointer and a `Variable`. using PrefilterExprVariablePair = @@ -118,8 +124,10 @@ class SparqlExpressionPimpl { uint64_t inputSizeEstimate, const std::optional& primarySortKeyVariable); - std::vector getPrefilterExpressionForMetadata( - bool isNegated = false) const; + // For a concise description of this method and its functionality, refer to + // the corresponding declaration in SparqlExpression.h. + std::vector getPrefilterExpressionForMetadata() + const; SparqlExpression* getPimpl() { return _pimpl.get(); } [[nodiscard]] const SparqlExpression* getPimpl() const { diff --git a/src/index/CMakeLists.txt b/src/index/CMakeLists.txt index 9b46467bc6..1fc8773721 100644 --- a/src/index/CMakeLists.txt +++ b/src/index/CMakeLists.txt @@ -6,6 +6,5 @@ add_library(index DocsDB.cpp FTSAlgorithms.cpp PrefixHeuristic.cpp CompressedRelation.cpp PatternCreator.cpp ScanSpecification.cpp - CompressedBlockPrefiltering.cpp DeltaTriples.cpp LocalVocabEntry.cpp) qlever_target_link_libraries(index util parser vocabulary ${STXXL_LIBRARIES}) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 463b0089dd..e540b448eb 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -291,7 +291,9 @@ addLinkAndDiscoverTest(AlgorithmTest) addLinkAndDiscoverTestSerial(CompressedRelationsTest index) -addLinkAndDiscoverTestSerial(CompressedBlockPrefilteringTest sparqlExpressions index engine) +addLinkAndDiscoverTestSerial(PrefilterExpressionIndexTest sparqlExpressions index) + +addLinkAndDiscoverTestSerial(GetPrefilterExpressionFromSparqlExpressionTest sparqlExpressions index) addLinkAndDiscoverTest(ExceptionTest) diff --git a/test/CompressedBlockPrefilteringTest.cpp b/test/CompressedBlockPrefilteringTest.cpp deleted file mode 100644 index 6089ba4ca0..0000000000 --- a/test/CompressedBlockPrefilteringTest.cpp +++ /dev/null @@ -1,1228 +0,0 @@ -// Copyright 2024, University of Freiburg, -// Chair of Algorithms and Data Structures -// Author: Hannes Baumann - -#include - -#include - -#include "./SparqlExpressionTestHelpers.h" -#include "engine/sparqlExpressions/NaryExpression.h" -#include "engine/sparqlExpressions/RelationalExpressions.h" -#include "index/CompressedBlockPrefiltering.h" -#include "util/DateYearDuration.h" -#include "util/GTestHelpers.h" -#include "util/IdTestHelpers.h" - -namespace { -using ad_utility::testing::BlankNodeId; -using ad_utility::testing::BoolId; -using ad_utility::testing::DateId; -using ad_utility::testing::DoubleId; -using ad_utility::testing::IntId; -using ad_utility::testing::UndefId; -using ad_utility::testing::VocabId; -constexpr auto DateParser = &DateYearOrDuration::parseXsdDate; -using namespace prefilterExpressions; - -namespace makeFilterExpr { -//______________________________________________________________________________ -// Make RelationalExpression -template -auto relExpr = - [](const ValueId& referenceId) -> std::unique_ptr { - return std::make_unique(referenceId); -}; - -// Make AndExpression or OrExpression -template -auto logExpr = [](std::unique_ptr child1, - std::unique_ptr child2) - -> std::unique_ptr { - return std::make_unique(std::move(child1), std::move(child2)); -}; - -// Make NotExpression -auto notExpr = [](std::unique_ptr child) - -> std::unique_ptr { - return std::make_unique(std::move(child)); -}; - -} // namespace makeFilterExpr -//______________________________________________________________________________ -// instantiation relational -// LESS THAN (`<`) -constexpr auto lt = makeFilterExpr::relExpr; -// LESS EQUAL (`<=`) -constexpr auto le = makeFilterExpr::relExpr; -// GREATER EQUAL (`>=`) -constexpr auto ge = makeFilterExpr::relExpr; -// GREATER THAN (`>`) -constexpr auto gt = makeFilterExpr::relExpr; -// EQUAL (`==`) -constexpr auto eq = makeFilterExpr::relExpr; -// NOT EQUAL (`!=`) -constexpr auto neq = makeFilterExpr::relExpr; -// AND (`&&`) -constexpr auto andExpr = makeFilterExpr::logExpr; -// OR (`||`) -constexpr auto orExpr = makeFilterExpr::logExpr; -// NOT (`!`) -constexpr auto notExpr = makeFilterExpr::notExpr; - -//______________________________________________________________________________ -/* -Our pre-filtering procedure expects blocks that are in correct (ascending) -order w.r.t. their contained ValueIds given the first and last triple. - -The correct order of the ValueIds is dependent on their type and underlying -representation. - -Short overview on the ascending order logic for the underlying values: -Order ValueIds for (signed) integer values - [0... max, -max... -1] -Order ValueIds for (signed) doubles values - [0.0... max, -0.0... -max] -Order ValueIds for Vocab and LocalVocab values given the vocabulary with -indices (up to N) - [VocabId(0), .... VocabId(N)] - -COLUMN 1 and COLUMN 2 contain fixed values, this is a necessary condition -that is also checked during the pre-filtering procedure. The actual evaluation -column (we filter w.r.t. values of COLUMN 0) contains mixed types. -*/ -//______________________________________________________________________________ -class TestPrefilterExprOnBlockMetadata : public ::testing::Test { - public: - const Id referenceDate1 = DateId(DateParser, "1999-11-11"); - const Id referenceDate2 = DateId(DateParser, "2005-02-27"); - const Id undef = Id::makeUndefined(); - const Id falseId = BoolId(false); - const Id trueId = BoolId(true); - const Id referenceDateEqual = DateId(DateParser, "2000-01-01"); - - // Fixed column ValueIds - const Id VocabId10 = VocabId(10); - const Id DoubleId33 = DoubleId(33); - const Id GraphId = VocabId(0); - - // Define BlockMetadata - const BlockMetadata b1 = makeBlock(undef, undef); - const BlockMetadata b2 = makeBlock(undef, falseId); - const BlockMetadata b3 = makeBlock(falseId, falseId); - const BlockMetadata b4 = makeBlock(trueId, IntId(0)); - const BlockMetadata b5 = makeBlock(IntId(0), IntId(0)); - const BlockMetadata b6 = makeBlock(IntId(0), IntId(5)); - const BlockMetadata b7 = makeBlock(IntId(5), IntId(6)); - const BlockMetadata b8 = makeBlock(IntId(8), IntId(9)); - const BlockMetadata b9 = makeBlock(IntId(-10), IntId(-8)); - const BlockMetadata b10 = makeBlock(IntId(-4), IntId(-4)); - const BlockMetadata b11 = makeBlock(IntId(-4), DoubleId(2)); - const BlockMetadata b12 = makeBlock(DoubleId(2), DoubleId(2)); - const BlockMetadata b13 = makeBlock(DoubleId(4), DoubleId(4)); - const BlockMetadata b14 = makeBlock(DoubleId(4), DoubleId(10)); - const BlockMetadata b15 = makeBlock(DoubleId(-1.23), DoubleId(-6.25)); - const BlockMetadata b16 = makeBlock(DoubleId(-6.25), DoubleId(-6.25)); - const BlockMetadata b17 = makeBlock(DoubleId(-10.42), DoubleId(-12.00)); - const BlockMetadata b18 = makeBlock(DoubleId(-14.01), VocabId(0)); - const BlockMetadata b19 = makeBlock(VocabId(10), VocabId(14)); - const BlockMetadata b20 = makeBlock(VocabId(14), VocabId(14)); - const BlockMetadata b21 = makeBlock(VocabId(14), VocabId(17)); - const BlockMetadata b22 = - makeBlock(VocabId(20), DateId(DateParser, "1999-12-12")); - const BlockMetadata b23 = makeBlock(DateId(DateParser, "2000-01-01"), - DateId(DateParser, "2000-01-01")); - const BlockMetadata b24 = - makeBlock(DateId(DateParser, "2024-10-08"), BlankNodeId(10)); - - // All blocks that contain mixed (ValueId) types over column 0 - const std::vector mixedBlocks = {b2, b4, b11, b18, b22, b24}; - - // Ordered and unique vector with BlockMetadata - const std::vector blocks = { - b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, - b13, b14, b15, b16, b17, b18, b19, b20, b21, b22, b23, b24}; - - const std::vector blocksInvalidOrder1 = { - b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, - b13, b14, b15, b16, b17, b18, b19, b20, b21, b22, b24, b23}; - - const std::vector blocksInvalidOrder2 = { - b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, - b14, b10, b15, b16, b17, b18, b19, b20, b21, b22, b23, b24}; - - const std::vector blocksWithDuplicate1 = { - b1, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, - b13, b14, b15, b16, b17, b18, b19, b20, b21, b22, b23, b24}; - - const std::vector blocksWithDuplicate2 = { - b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, - b14, b15, b16, b17, b18, b19, b20, b21, b22, b23, b24, b24}; - - // Function to create BlockMetadata - const BlockMetadata makeBlock(const ValueId& firstId, const ValueId& lastId) { - assert(firstId <= lastId); - static size_t blockIdx = 0; - ++blockIdx; - return {{{}, - 0, - // COLUMN 0 | COLUMN 1 | COLUMN 2 - {firstId, VocabId10, DoubleId33, GraphId}, // firstTriple - {lastId, VocabId10, DoubleId33, GraphId}, // lastTriple - {}, - false}, - blockIdx}; - } - - // Check if expected error is thrown. - auto makeTestErrorCheck(std::unique_ptr expr, - const std::vector& input, - const std::string& expected, - size_t evaluationColumn = 0) { - AD_EXPECT_THROW_WITH_MESSAGE(expr->evaluate(input, evaluationColumn), - ::testing::HasSubstr(expected)); - } - - // Assert that the PrefilterExpression tree is properly copied when calling - // method clone. - auto makeTestClone(std::unique_ptr expr) { - ASSERT_EQ(*expr, *expr->clone()); - } - - // Check that the provided expression prefilters the correct blocks. - auto makeTest(std::unique_ptr expr, - std::vector&& expected) { - std::vector expectedAdjusted; - // This is for convenience, we automatically insert all mixed blocks - // which must be always returned. - std::ranges::set_union( - expected, mixedBlocks, std::back_inserter(expectedAdjusted), - [](const BlockMetadata& b1, const BlockMetadata& b2) { - return b1.blockIndex_ < b2.blockIndex_; - }); - ASSERT_EQ(expr->evaluate(blocks, 0), expectedAdjusted); - } -}; - -} // namespace - -//______________________________________________________________________________ -TEST_F(TestPrefilterExprOnBlockMetadata, testBlockFormatForDebugging) { - EXPECT_EQ( - "#BlockMetadata\n(first) Triple: I:0 V:10 D:33.000000 V:0\n(last) " - "Triple: I:0 V:10 D:33.000000 V:0\nnum. rows: 0.\n", - (std::stringstream() << b5).str()); - EXPECT_EQ( - "#BlockMetadata\n(first) Triple: I:-4 V:10 D:33.000000 V:0\n(last) " - "Triple: D:2.000000 V:10 D:33.000000 V:0\nnum. rows: 0.\n", - (std::stringstream() << b11).str()); - EXPECT_EQ( - "#BlockMetadata\n(first) Triple: V:14 V:10 D:33.000000 V:0\n(last) " - "Triple: V:17 V:10 D:33.000000 V:0\nnum. rows: 0.\n", - (std::stringstream() << b21).str()); -} - -// Test Relational Expressions -//______________________________________________________________________________ -// Test LessThanExpression -// Note: the `makeTest` function automatically adds the blocks with mixed -// datatypes to the expected result. -TEST_F(TestPrefilterExprOnBlockMetadata, testLessThanExpressions) { - makeTest(lt(IntId(5)), - {b5, b6, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18}); - makeTest(lt(IntId(-12)), {b18}); - makeTest(lt(IntId(0)), {b9, b10, b15, b16, b17, b18}); - makeTest(lt(DoubleId(-14.01)), {b18}); - makeTest(lt(DoubleId(-11.22)), {b17, b18}); - makeTest(lt(DoubleId(-4.121)), {b9, b15, b16, b17, b18}); - makeTest(lt(VocabId(0)), {b18}); - makeTest(lt(VocabId(12)), {b18, b19}); - makeTest(lt(VocabId(14)), {b18, b19}); - makeTest(lt(VocabId(16)), {b18, b19, b20, b21}); - makeTest(lt(IntId(100)), - {b5, b6, b7, b8, b9, b10, b12, b13, b14, b15, b16, b17, b18}); - makeTest(lt(undef), {}); - makeTest(lt(falseId), {}); - makeTest(lt(trueId), {b2, b3}); - makeTest(lt(referenceDate1), {}); - makeTest(lt(referenceDateEqual), {b22}); - makeTest(lt(referenceDate2), {b22, b23, b24}); - makeTest(lt(BlankNodeId(11)), {b24}); -} - -//______________________________________________________________________________ -// Test LessEqualExpression -// Note: the `makeTest` function automatically adds the blocks with mixed -// datatypes to the expected result. -TEST_F(TestPrefilterExprOnBlockMetadata, testLessEqualExpressions) { - makeTest(le(IntId(0)), {b5, b6, b9, b10, b11, b15, b16, b17, b18}); - makeTest(le(IntId(-6)), {b9, b11, b15, b16, b17, b18}); - makeTest(le(IntId(7)), - {b5, b6, b7, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18}); - makeTest(le(IntId(-9)), {b9, b11, b17, b18}); - makeTest(le(DoubleId(-9.131)), {b9, b11, b17, b18}); - makeTest(le(DoubleId(1.1415)), {b5, b6, b9, b10, b11, b15, b16, b17, b18}); - makeTest(le(DoubleId(3.1415)), - {b5, b6, b9, b10, b11, b12, b15, b16, b17, b18}); - makeTest(le(DoubleId(-11.99999999999999)), {b17, b18}); - makeTest(le(DoubleId(-14.03)), {b18}); - makeTest(le(VocabId(0)), {b18}); - makeTest(le(VocabId(11)), {b18, b19}); - makeTest(le(VocabId(14)), {b18, b19, b20, b21}); - makeTest(le(undef), {}); - makeTest(le(falseId), {b2, b3}); - makeTest(le(trueId), {b2, b3, b4}); - makeTest(le(referenceDateEqual), {b22, b23}); - makeTest(le(BlankNodeId(11)), {b24}); -} - -//______________________________________________________________________________ -// Test GreaterThanExpression -// Note: the `makeTest` function automatically adds the blocks with mixed -// datatypes to the expected result. -TEST_F(TestPrefilterExprOnBlockMetadata, testGreaterThanExpression) { - makeTest(gt(DoubleId(5.5375)), {b7, b8, b11, b14, b18}); - makeTest(gt(DoubleId(9.9994)), {b14}); - makeTest(gt(IntId(-5)), {b5, b6, b7, b8, b10, b11, b12, b13, b14, b15}); - makeTest(gt(DoubleId(-5.5375)), - {b5, b6, b7, b8, b10, b11, b12, b13, b14, b15}); - makeTest(gt(DoubleId(-6.2499999)), - {b5, b6, b7, b8, b10, b11, b12, b13, b14, b15}); - makeTest(gt(IntId(1)), {b6, b7, b8, b11, b12, b13, b14}); - makeTest(gt(IntId(3)), {b6, b7, b8, b11, b13, b14}); - makeTest(gt(IntId(4)), {b6, b7, b8, b11, b14}); - makeTest(gt(IntId(-4)), {b5, b6, b7, b8, b11, b12, b13, b14, b15}); - makeTest(gt(IntId(33)), {}); - makeTest(gt(VocabId(22)), {b22}); - makeTest(gt(VocabId(14)), {b21, b22}); - makeTest(gt(VocabId(12)), {b19, b20, b21, b22}); - makeTest(gt(undef), {}); - makeTest(gt(falseId), {b4}); - makeTest(gt(trueId), {}); - makeTest(gt(referenceDateEqual), {b24}); - makeTest(gt(referenceDate1), {b22, b23, b24}); - makeTest(gt(referenceDate2), {b24}); -} - -//______________________________________________________________________________ -// Test GreaterEqualExpression -// Note: the `makeTest` function automatically adds the blocks with mixed -// datatypes to the expected result. -TEST_F(TestPrefilterExprOnBlockMetadata, testGreaterEqualExpression) { - makeTest(ge(IntId(0)), {b5, b6, b7, b8, b11, b12, b13, b14}); - makeTest(ge(IntId(8)), {b8, b11, b14}); - makeTest(ge(DoubleId(9.98)), {b11, b14}); - makeTest(ge(IntId(-3)), {b5, b6, b7, b8, b11, b12, b13, b14, b15}); - makeTest(ge(IntId(-10)), - {b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16}); - makeTest(ge(DoubleId(-3.1415)), {b5, b6, b7, b8, b11, b12, b13, b14, b15}); - makeTest(ge(DoubleId(-4.000001)), - {b5, b6, b7, b8, b10, b11, b12, b13, b14, b15}); - makeTest(ge(DoubleId(10.000)), {b11, b14}); - makeTest(ge(DoubleId(-15.22)), - {b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18}); - makeTest(ge(DoubleId(7.999999)), {b8, b11, b14}); - makeTest(ge(DoubleId(10.0001)), {}); - makeTest(ge(VocabId(14)), {b18, b19, b20, b21, b22}); - makeTest(ge(VocabId(10)), {b18, b19, b20, b21, b22}); - makeTest(ge(VocabId(17)), {b18, b21, b22}); - makeTest(ge(undef), {}); - makeTest(ge(falseId), {b2, b3, b4}); - makeTest(ge(trueId), {b4}); - makeTest(ge(referenceDateEqual), {b23, b24}); -} - -//______________________________________________________________________________ -// Test EqualExpression -// Note: the `makeTest` function automatically adds the blocks with mixed -// datatypes to the expected result. -TEST_F(TestPrefilterExprOnBlockMetadata, testEqualExpression) { - makeTest(eq(IntId(0)), {b4, b5, b6, b11}); - makeTest(eq(IntId(5)), {b6, b7, b11, b14}); - makeTest(eq(IntId(22)), {}); - makeTest(eq(IntId(-10)), {b9, b11, b18}); - makeTest(eq(DoubleId(-6.25)), {b15, b16}); - makeTest(eq(IntId(-11)), {b17}); - makeTest(eq(DoubleId(-14.02)), {b18}); - makeTest(eq(DoubleId(-0.001)), {b11}); - makeTest(eq(DoubleId(0)), {b4, b5, b6, b11}); - makeTest(eq(IntId(2)), {b6, b11, b12}); - makeTest(eq(DoubleId(5.5)), {b7, b11, b14}); - makeTest(eq(DoubleId(1.5)), {b6, b11}); - makeTest(eq(VocabId(1)), {b18}); - makeTest(eq(VocabId(14)), {b18, b19, b20, b21}); - makeTest(eq(VocabId(11)), {b18, b19}); - makeTest(eq(VocabId(17)), {b18, b21}); - makeTest(eq(IntId(-4)), {b10, b11, b15}); - makeTest(eq(trueId), {b4}); - makeTest(eq(referenceDate1), {b22}); - makeTest(eq(referenceDateEqual), {b23}); - makeTest(eq(referenceDate2), {}); -} - -//______________________________________________________________________________ -// Test NotEqualExpression -// Note: the `makeTest` function automatically adds the blocks with mixed -// datatypes to the expected result. -TEST_F(TestPrefilterExprOnBlockMetadata, testNotEqualExpression) { - makeTest(neq(DoubleId(0.00)), - {b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18}); - makeTest(neq(IntId(-4)), - {b5, b6, b7, b8, b9, b11, b12, b13, b14, b15, b16, b17, b18}); - makeTest(neq(DoubleId(0.001)), - {b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18}); - makeTest(neq(IntId(2)), - {b5, b6, b7, b8, b9, b10, b11, b13, b14, b15, b16, b17, b18}); - makeTest(neq(DoubleId(-6.2500)), - {b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b17, b18}); - makeTest(neq(IntId(5)), - {b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18}); - makeTest(neq(DoubleId(-101.23)), - {b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18}); - makeTest(neq(VocabId(0)), {b19, b20, b21, b22}); - makeTest(neq(VocabId(7)), {b18, b19, b20, b21, b22}); - makeTest(neq(VocabId(14)), {b18, b19, b21, b22}); - makeTest(neq(VocabId(17)), {b18, b19, b20, b21, b22}); - makeTest(neq(undef), {}); - makeTest(neq(falseId), {b4}); - makeTest(neq(referenceDateEqual), {b22, b24}); - makeTest(neq(referenceDate1), {b22, b23, b24}); -} - -// Test Logical Expressions -//______________________________________________________________________________ -// Test AndExpression -// Note: the `makeTest` function automatically adds the blocks with mixed -// datatypes to the expected result. -TEST_F(TestPrefilterExprOnBlockMetadata, testAndExpression) { - makeTest(andExpr(ge(VocabId(10)), gt(VocabId(10))), {b19, b20, b21, b22}); - makeTest(andExpr(ge(VocabId(10)), ge(VocabId(10))), {b19, b20, b21, b22}); - makeTest(andExpr(ge(VocabId(12)), gt(VocabId(17))), {b22}); - makeTest(andExpr(ge(VocabId(12)), gt(VocabId(17))), {b22}); - makeTest(andExpr(ge(VocabId(10)), lt(VocabId(14))), {b19}); - makeTest(andExpr(le(VocabId(0)), lt(VocabId(10))), {b18}); - makeTest(andExpr(le(VocabId(17)), lt(VocabId(17))), {b18, b19, b20, b21}); - makeTest(andExpr(ge(DoubleId(-6.25)), lt(IntId(-7))), {}); - makeTest(andExpr(gt(DoubleId(-6.25)), lt(DoubleId(-6.25))), {}); - makeTest(andExpr(gt(IntId(0)), lt(IntId(0))), {}); - makeTest(andExpr(gt(IntId(-10)), lt(DoubleId(0))), {b9, b10, b11, b15, b16}); - makeTest(andExpr(gt(IntId(0)), eq(DoubleId(0))), {b6, b11}); - makeTest(andExpr(ge(IntId(0)), eq(IntId(0))), {b5, b6, b11}); - makeTest(andExpr(gt(DoubleId(-34.23)), ge(DoubleId(15.1))), {}); - makeTest(andExpr(lt(IntId(0)), le(DoubleId(-4))), - {b9, b10, b11, b15, b16, b17, b18}); - makeTest(andExpr(neq(IntId(0)), neq(IntId(-4))), - {b6, b7, b8, b9, b11, b12, b13, b14, b15, b16, b17, b18}); - makeTest(andExpr(neq(DoubleId(-3.141)), eq(DoubleId(4.5))), - {b6, b11, b14, b18}); - makeTest(andExpr(neq(DoubleId(-6.25)), lt(IntId(0))), - {b9, b10, b11, b15, b17, b18}); - makeTest(andExpr(le(DoubleId(-4)), ge(DoubleId(1))), {}); - makeTest(andExpr(le(DoubleId(-2)), eq(IntId(-3))), {b11, b15}); - makeTest(andExpr(andExpr(le(IntId(10)), gt(DoubleId(0))), eq(undef)), {}); - makeTest(andExpr(gt(referenceDate1), le(IntId(10))), {}); - makeTest(andExpr(gt(IntId(4)), andExpr(gt(DoubleId(8)), lt(IntId(10)))), - {b8, b14}); - makeTest(andExpr(eq(IntId(0)), andExpr(lt(IntId(-20)), gt(IntId(30)))), {}); - makeTest(andExpr(eq(IntId(0)), andExpr(le(IntId(0)), ge(IntId(0)))), - {b4, b5, b6, b11}); -} - -//______________________________________________________________________________ -// Test OrExpression -// Note: the `makeTest` function automatically adds the blocks with mixed -// datatypes to the expected result. -TEST_F(TestPrefilterExprOnBlockMetadata, testOrExpression) { - makeTest(orExpr(lt(VocabId(22)), le(VocabId(0))), {b18, b19, b20, b21}); - makeTest(orExpr(le(VocabId(0)), ge(VocabId(16))), {b18, b21, b22}); - makeTest(orExpr(gt(VocabId(17)), ge(VocabId(17))), {b21, b22}); - makeTest(orExpr(lt(DoubleId(-5.95)), eq(VocabId(14))), - {b9, b15, b16, b17, b18, b19, b20, b21}); - makeTest(orExpr(eq(DoubleId(0)), neq(VocabId(14))), - {b5, b6, b11, b18, b19, b21}); - makeTest(orExpr(eq(DoubleId(0)), eq(DoubleId(-6.25))), - {b5, b6, b11, b15, b16, b18}); - makeTest(orExpr(gt(undef), le(IntId(-6))), {b9, b15, b16, b17, b18}); - makeTest(orExpr(le(trueId), gt(referenceDate1)), {b2, b3, b4, b22, b23, b24}); - makeTest(orExpr(eq(IntId(0)), orExpr(lt(IntId(-10)), gt(IntId(8)))), - {b5, b6, b8, b11, b14, b17, b18}); - makeTest(orExpr(gt(referenceDate2), eq(trueId)), {b4}); - makeTest(orExpr(eq(VocabId(17)), orExpr(lt(VocabId(0)), gt(VocabId(20)))), - {b21, b22}); - makeTest(orExpr(eq(undef), gt(referenceDateEqual)), {b24}); - makeTest(orExpr(gt(IntId(8)), gt(DoubleId(22.1))), {b8, b14}); - makeTest(orExpr(lt(DoubleId(-8.25)), le(IntId(-10))), {b9, b17, b18}); - makeTest(orExpr(eq(IntId(0)), neq(DoubleId(0.25))), - {b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18}); - makeTest(orExpr(gt(referenceDate1), orExpr(gt(trueId), eq(IntId(0)))), - {b4, b5, b6, b11, b22, b23, b24}); - makeTest(orExpr(gt(DoubleId(-6.25)), lt(DoubleId(-6.25))), - {b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b17, b18}); - makeTest(orExpr(orExpr(eq(IntId(0)), eq(IntId(5))), - orExpr(eq(DoubleId(-6.25)), lt(DoubleId(-12)))), - {b4, b5, b6, b7, b11, b14, b15, b16, b18}); - makeTest(orExpr(le(trueId), gt(falseId)), {b2, b3, b4}); - makeTest(orExpr(eq(VocabId(0)), eq(DoubleId(0.25))), {b6, b11, b18}); -} - -//______________________________________________________________________________ -// Test NotExpression -// Note: the `makeTest` function automatically adds the blocks with mixed -// datatypes to the expected result. -TEST_F(TestPrefilterExprOnBlockMetadata, testNotExpression) { - makeTest(notExpr(eq(VocabId(2))), {b18, b19, b20, b21, b22}); - makeTest(notExpr(eq(VocabId(14))), {b18, b19, b21, b22}); - makeTest(notExpr(neq(VocabId(14))), {b19, b20, b21}); - makeTest(notExpr(gt(VocabId(2))), {b18}); - makeTest(notExpr(lt(DoubleId(-14.01))), - {b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18}); - makeTest(notExpr(ge(DoubleId(-14.01))), {b18}); - makeTest(notExpr(gt(DoubleId(-4.00))), {b9, b10, b11, b15, b16, b17, b18}); - makeTest(notExpr(ge(DoubleId(-24.4))), {b18}); - makeTest(notExpr(gt(referenceDate2)), {b22, b23}); - makeTest(notExpr(le(trueId)), {}); - makeTest(notExpr(le(IntId(0))), {b6, b7, b8, b11, b12, b13, b14}); - makeTest(notExpr(gt(undef)), {}); - makeTest(notExpr(eq(DoubleId(-6.25))), - {b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b17, b18}); - makeTest(notExpr(neq(DoubleId(4))), {b6, b11, b13, b14, b18}); - makeTest(notExpr(gt(DoubleId(0))), - {b4, b5, b6, b9, b10, b11, b15, b16, b17, b18}); - makeTest(notExpr(notExpr(eq(IntId(0)))), {b4, b5, b6, b11}); - makeTest(notExpr(notExpr(neq(DoubleId(-6.25)))), - {b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b17, b18}); - makeTest(notExpr(notExpr(lt(VocabId(10)))), {b18}); - makeTest(notExpr(notExpr(ge(DoubleId(3.99)))), {b6, b7, b8, b11, b13, b14}); - makeTest(notExpr(andExpr(le(IntId(0)), ge(IntId(0)))), - {b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18}); - makeTest(notExpr(andExpr(neq(IntId(-10)), neq(DoubleId(-14.02)))), {b9, b18}); - makeTest( - notExpr(andExpr(gt(IntId(10)), ge(DoubleId(-6.25)))), - {b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18}); - makeTest( - notExpr(andExpr(lt(DoubleId(-7)), ge(IntId(6)))), - {b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18}); - makeTest(notExpr(orExpr(le(IntId(0)), ge(DoubleId(6)))), - {b6, b7, b11, b12, b13, b14}); - makeTest(notExpr(orExpr(ge(DoubleId(0)), gt(IntId(-10)))), - {b9, b11, b17, b18}); - makeTest(notExpr(orExpr(lt(VocabId(10)), gt(VocabId(10)))), {b19}); - makeTest(notExpr(orExpr(lt(DoubleId(-4)), gt(IntId(-4)))), {b10, b11, b15}); - makeTest(notExpr(orExpr(gt(IntId(-42)), ge(VocabId(0)))), {b11}); - makeTest(notExpr(orExpr(ge(VocabId(14)), gt(VocabId(15)))), {b18, b19}); -} - -//______________________________________________________________________________ -// Test PrefilterExpressions mixed -// Note: the `makeTest` function automatically adds the blocks with mixed -// datatypes to the expected result. -TEST_F(TestPrefilterExprOnBlockMetadata, testGeneralPrefilterExprCombinations) { - makeTest(andExpr(notExpr(gt(DoubleId(-14.01))), lt(IntId(0))), {b18}); - makeTest( - orExpr(andExpr(gt(DoubleId(8.25)), le(IntId(10))), eq(DoubleId(-6.25))), - {b8, b14, b15, b16}); - makeTest( - orExpr(andExpr(gt(DoubleId(8.25)), le(IntId(10))), lt(DoubleId(-6.25))), - {b8, b9, b14, b17, b18}); - makeTest(andExpr(orExpr(ge(trueId), le(falseId)), eq(referenceDate1)), {}); - makeTest(andExpr(eq(IntId(0)), orExpr(lt(IntId(-11)), le(IntId(-12)))), {}); - makeTest( - andExpr(eq(DoubleId(-4)), orExpr(gt(IntId(-4)), lt(DoubleId(-1.25)))), - {b10, b11, b15}); - makeTest(orExpr(notExpr(andExpr(lt(IntId(10)), gt(IntId(5)))), eq(IntId(0))), - {b4, b5, b6, b7, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18}); - makeTest(andExpr(orExpr(gt(VocabId(16)), le(VocabId(5))), gt(DoubleId(7.25))), - {}); - makeTest(andExpr(lt(falseId), orExpr(lt(IntId(10)), gt(DoubleId(17.25)))), - {}); - makeTest( - orExpr(andExpr(gt(VocabId(16)), ge(VocabId(17))), gt(DoubleId(7.25))), - {b8, b14, b18, b21, b22}); - makeTest(orExpr(eq(trueId), andExpr(gt(referenceDate1), lt(referenceDate2))), - {b4, b22, b23}); -} - -//______________________________________________________________________________ -// Test that correct errors are thrown for invalid input (condition) -TEST_F(TestPrefilterExprOnBlockMetadata, testInputConditionCheck) { - makeTestErrorCheck(le(IntId(5)), blocksWithDuplicate1, - "The provided data blocks must be unique."); - makeTestErrorCheck(andExpr(gt(VocabId(10)), le(VocabId(20))), - blocksWithDuplicate2, - "The provided data blocks must be unique."); - makeTestErrorCheck(gt(DoubleId(2)), blocksInvalidOrder1, - "The blocks must be provided in sorted order."); - makeTestErrorCheck(andExpr(gt(VocabId(10)), le(VocabId(20))), - blocksInvalidOrder2, - "The blocks must be provided in sorted order."); - makeTestErrorCheck( - gt(DoubleId(2)), blocks, - "The values in the columns up to the evaluation column must be " - "consistent.", - 1); - makeTestErrorCheck( - gt(DoubleId(2)), blocks, - "The values in the columns up to the evaluation column must be " - "consistent.", - 2); -} - -//______________________________________________________________________________ -// Check for correctness given only one BlockMetadata value is provided. -TEST_F(TestPrefilterExprOnBlockMetadata, testWithOneBlockMetadataValue) { - auto expr = orExpr(eq(DoubleId(-6.25)), eq(IntId(0))); - std::vector input = {b16}; - EXPECT_EQ(expr->evaluate(input, 0), input); - EXPECT_EQ(expr->evaluate(input, 1), std::vector{}); - EXPECT_EQ(expr->evaluate(input, 2), std::vector{}); -} - -//______________________________________________________________________________ -// Test method clone. clone() creates a copy of the complete PrefilterExpression -// tree. -TEST_F(TestPrefilterExprOnBlockMetadata, testMethodClonePrefilterExpression) { - makeTestClone(lt(VocabId(10))); - makeTestClone(gt(referenceDate2)); - makeTestClone(andExpr(lt(VocabId(20)), gt(VocabId(10)))); - makeTestClone(neq(IntId(10))); - makeTestClone(orExpr(eq(IntId(10)), neq(DoubleId(10)))); - makeTestClone(notExpr(ge(referenceDate1))); - makeTestClone(notExpr(notExpr(neq(VocabId(0))))); - makeTestClone(notExpr(andExpr(eq(IntId(10)), neq(DoubleId(10))))); - makeTestClone(orExpr(orExpr(eq(VocabId(101)), lt(IntId(100))), - andExpr(gt(referenceDate1), lt(referenceDate2)))); - makeTestClone(andExpr(andExpr(neq(IntId(10)), neq(DoubleId(100.23))), - orExpr(gt(DoubleId(0.001)), lt(IntId(250))))); - makeTestClone(orExpr(orExpr(eq(VocabId(101)), lt(IntId(100))), - notExpr(andExpr(lt(VocabId(0)), neq(IntId(100)))))); -} - -//______________________________________________________________________________ -//______________________________________________________________________________ -// TEST SECTION 2 -//______________________________________________________________________________ -//______________________________________________________________________________ -namespace { - -using namespace sparqlExpression; -using Literal = ad_utility::triple_component::Literal; -using Iri = ad_utility::triple_component::Iri; -using MakeBinarySprqlExprFunc = std::function; -using RelValues = std::variant; - -// TEST HELPER SECTION -//______________________________________________________________________________ -// make `Literal` -const auto L = [](const std::string& content) -> Literal { - return Literal::fromStringRepresentation(content); -}; - -//______________________________________________________________________________ -// make `Iri` -const auto I = [](const std::string& content) -> Iri { - return Iri::fromIriref(content); -}; - -//______________________________________________________________________________ -struct TestDates { - const Id referenceDate1 = DateId(DateParser, "1999-11-11"); - const Id referenceDate2 = DateId(DateParser, "2005-02-27"); -}; - -//______________________________________________________________________________ -const auto makeLiteralSparqlExpr = - [](const auto& child) -> std::unique_ptr { - using T = std::decay_t; - if constexpr (std::is_same_v) { - return std::make_unique(child); - } else if constexpr (std::is_same_v) { - return std::make_unique(child); - } else if constexpr (std::is_same_v) { - return std::make_unique(child); - } else if constexpr (std::is_same_v) { - return std::make_unique(child); - } else { - throw std::runtime_error( - "Can't create a LiteralExpression from provided (input) type."); - } -}; - -//______________________________________________________________________________ -auto getExpr = [](const auto& variantVal) -> std::unique_ptr { - return makeLiteralSparqlExpr(variantVal); -}; - -//______________________________________________________________________________ -template -std::unique_ptr makeRelationalSparqlExprImpl( - const RelValues& child0, const RelValues& child1) { - return std::make_unique(std::array{ - std::visit(getExpr, child0), std::visit(getExpr, child1)}); -}; - -//______________________________________________________________________________ -std::unique_ptr makeStringStartsWithSparqlExpression( - const RelValues& child0, const RelValues& child1) { - return makeStrStartsExpression(std::visit(getExpr, child0), - std::visit(getExpr, child1)); -}; - -//______________________________________________________________________________ -template -std::unique_ptr makeLogicalBinaryPrefilterExprImpl( - std::unique_ptr child1, - std::unique_ptr child2) { - return std::make_unique(std::move(child1), std::move(child2)); -}; - -//______________________________________________________________________________ -// LESS THAN (`<`, `SparqlExpression`) -constexpr auto ltSprql = - &makeRelationalSparqlExprImpl; -// LESS EQUAL (`<=`, `SparqlExpression`) -constexpr auto leSprql = - &makeRelationalSparqlExprImpl; -// EQUAL (`==`, `SparqlExpression`) -constexpr auto eqSprql = - &makeRelationalSparqlExprImpl; -// NOT EQUAL (`!=`, `SparqlExpression`) -constexpr auto neqSprql = - &makeRelationalSparqlExprImpl; -// GREATER EQUAL (`>=`, `SparqlExpression`) -constexpr auto geSprql = - &makeRelationalSparqlExprImpl; -// GREATER THAN (`>`, `SparqlExpression`) -constexpr auto gtSprql = - &makeRelationalSparqlExprImpl; -// AND (`&&`, `SparqlExpression`) -constexpr auto andSprqlExpr = &makeAndExpression; -// OR (`||`, `SparqlExpression`) -constexpr auto orSprqlExpr = &makeOrExpression; -// NOT (`!`, `SparqlExpression`) -constexpr auto notSprqlExpr = &makeUnaryNegateExpression; - -//______________________________________________________________________________ -// Create SparqlExpression `STRSTARTS`. -constexpr auto strStartsSprql = &makeStringStartsWithSparqlExpression; - -// ASSERT EQUALITY -//______________________________________________________________________________ -const auto equalityCheckPrefilterVectors = - [](const std::vector& result, - const std::vector& expected) -> void { - ASSERT_EQ(result.size(), expected.size()); - const auto isEqualImpl = [](const PrefilterExprVariablePair& resPair, - const PrefilterExprVariablePair& expPair) { - if (*resPair.first != *expPair.first || resPair.second != expPair.second) { - std::stringstream stream; - stream << "The following value pairs don't match:" - << "\nRESULT: " << *resPair.first << "EXPECTED: " << *expPair.first - << "RESULT: VARIABLE" << resPair.second.name() - << "\nEXPECTED: VARIABLE" << expPair.second.name() << std::endl; - ADD_FAILURE() << stream.str(); - return false; - } - return true; - }; - ASSERT_TRUE( - std::equal(result.begin(), result.end(), expected.begin(), isEqualImpl)); -}; - -//______________________________________________________________________________ -// `evalAndEqualityCheck` evaluates the provided `SparqlExpression` and checks -// in the following if the resulting vector contains the same -// `` pairs in the correct order. If no -// `` pair is provided, the expected value for -// the `SparqlExpression` is an empty vector. -const auto evalAndEqualityCheck = - [](std::unique_ptr sparqlExpr, - std::convertible_to auto&&... prefilterArgs) { - std::vector prefilterVarPair = {}; - if constexpr (sizeof...(prefilterArgs) > 0) { - (prefilterVarPair.emplace_back( - std::forward(prefilterArgs)), - ...); - } - equalityCheckPrefilterVectors( - sparqlExpr->getPrefilterExpressionForMetadata(), - std::move(prefilterVarPair)); - }; - -//______________________________________________________________________________ -// Construct a `PAIR` with the given `PrefilterExpression` and `Variable` value. -auto pr = [](std::unique_ptr expr, - const Variable& var) -> PrefilterExprVariablePair { - return {std::move(expr), var}; -}; -} // namespace - -//______________________________________________________________________________ -// Test PrefilterExpression equality operator. -TEST(PrefilterExpression, testEqualityOperator) { - const TestDates dt{}; - // Relational PrefilterExpressions - ASSERT_FALSE(*ge(dt.referenceDate1) == *ge(dt.referenceDate2)); - ASSERT_FALSE(*neq(BoolId(true)) == *eq(BoolId(true))); - ASSERT_TRUE(*eq(IntId(1)) == *eq(IntId(1))); - ASSERT_TRUE(*ge(dt.referenceDate1) == *ge(dt.referenceDate1)); - // NotExpression - ASSERT_TRUE(*notExpr(eq(IntId(0))) == *notExpr(eq(IntId(0)))); - ASSERT_TRUE(*notExpr(notExpr(ge(VocabId(0)))) == - *notExpr(notExpr(ge(VocabId(0))))); - ASSERT_FALSE(*notExpr(gt(IntId(0))) == *eq(IntId(0))); - ASSERT_FALSE(*notExpr(andExpr(eq(IntId(1)), eq(IntId(0)))) == - *notExpr(ge(VocabId(0)))); - // Binary PrefilterExpressions (AND and OR) - ASSERT_TRUE(*orExpr(eq(IntId(0)), le(IntId(0))) == - *orExpr(eq(IntId(0)), le(IntId(0)))); - ASSERT_TRUE(*andExpr(le(VocabId(1)), le(IntId(0))) == - *andExpr(le(VocabId(1)), le(IntId(0)))); - ASSERT_FALSE(*orExpr(eq(IntId(0)), le(IntId(0))) == - *andExpr(le(VocabId(1)), le(IntId(0)))); - ASSERT_FALSE(*notExpr(orExpr(eq(IntId(0)), le(IntId(0)))) == - *orExpr(eq(IntId(0)), le(IntId(0)))); -} - -//______________________________________________________________________________ -// Test PrefilterExpression content formatting for debugging. -TEST(PrefilterExpression, checkPrintFormattedPrefilterExpression) { - auto expr = lt(IntId(10)); - EXPECT_EQ((std::stringstream() << *expr).str(), - "Prefilter RelationalExpression\nValueId: I:10\n.\n"); - expr = neq(DoubleId(8.21)); - EXPECT_EQ((std::stringstream() << *expr).str(), - "Prefilter RelationalExpression\nValueId: D:8.210000\n.\n"); - expr = notExpr(eq(VocabId(0))); - EXPECT_EQ((std::stringstream() << *expr).str(), - "Prefilter NotExpression:\nchild {Prefilter " - "RelationalExpression\nValueId: V:0\n}\n.\n"); - expr = orExpr(le(IntId(0)), ge(IntId(5))); - EXPECT_EQ((std::stringstream() << *expr).str(), - "Prefilter LogicalExpression\nchild1 {Prefilter " - "RelationalExpression\nValueId: I:0\n}child2 {Prefilter " - "RelationalExpression=)>\nValueId: I:5\n}\n.\n"); - expr = andExpr(lt(IntId(20)), gt(IntId(10))); - EXPECT_EQ((std::stringstream() << *expr).str(), - "Prefilter LogicalExpression\nchild1 {Prefilter " - "RelationalExpression\nValueId: I:20\n}child2 {Prefilter " - "RelationalExpression)>\nValueId: I:10\n}\n.\n"); -} - -//______________________________________________________________________________ -// Test coverage for the default implementation of -// getPrefilterExpressionForMetadata. -TEST(SparqlExpression, testGetPrefilterExpressionDefault) { - evalAndEqualityCheck( - makeUnaryMinusExpression(makeLiteralSparqlExpr(IntId(0)))); - evalAndEqualityCheck(makeMultiplyExpression( - makeLiteralSparqlExpr(DoubleId(11)), makeLiteralSparqlExpr(DoubleId(3)))); - evalAndEqualityCheck( - makeStrEndsExpression(makeLiteralSparqlExpr(L("\"Freiburg\"")), - makeLiteralSparqlExpr(L("\"burg\"")))); - evalAndEqualityCheck( - makeIsIriExpression(makeLiteralSparqlExpr(I("")))); - evalAndEqualityCheck(makeLogExpression(makeLiteralSparqlExpr(DoubleId(8)))); - evalAndEqualityCheck( - makeStrIriDtExpression(makeLiteralSparqlExpr(L("\"test\"")), - makeLiteralSparqlExpr(I("")))); -} - -//______________________________________________________________________________ -// Check that the (Sparql) RelationalExpression returns the expected -// PrefilterExpression. -TEST(SparqlExpression, getPrefilterExpressionFromSparqlRelational) { - const TestDates dt{}; - const Variable var = Variable{"?x"}; - // ?x == BooldId(true) (RelationalExpression Sparql) - // expected: <(== BoolId(true)), ?x> (PrefilterExpression, Variable) - evalAndEqualityCheck(eqSprql(var, BoolId(true)), pr(eq(BoolId(true)), var)); - // For BoolId(true) == ?x we expect the same PrefilterExpression pair. - evalAndEqualityCheck(eqSprql(BoolId(true), var), pr(eq(BoolId(true)), var)); - // ?x != BooldId(true) (RelationalExpression Sparql) - // expected: <(!= BoolId(true)), ?x> (PrefilterExpression, Variable) - evalAndEqualityCheck(neqSprql(var, BoolId(false)), - pr(neq(BoolId(false)), var)); - // Same expected value for BoolId(true) != ?x. - evalAndEqualityCheck(neqSprql(BoolId(false), var), - pr(neq(BoolId(false)), var)); - // ?x >= IntId(1) - // expected: <(>= IntId(1)), ?x> - evalAndEqualityCheck(geSprql(var, IntId(1)), pr(ge(IntId(1)), var)); - // IntId(1) <= ?x - // expected: <(>= IntId(1)), ?x> - evalAndEqualityCheck(leSprql(IntId(1), var), pr(ge(IntId(1)), var)); - // ?x > IntId(1) - // expected: <(> IntId(1)), ?x> - evalAndEqualityCheck(gtSprql(var, IntId(1)), pr(gt(IntId(1)), var)); - // VocabId(10) != ?x - // expected: <(!= VocabId(10)), ?x> - evalAndEqualityCheck(neqSprql(VocabId(10), var), pr(neq(VocabId(10)), var)); - // BlankNodeId(1) > ?x - // expected: <(< BlankNodeId(1)), ?x> - evalAndEqualityCheck(geSprql(BlankNodeId(1), var), - pr(le(BlankNodeId(1)), var)); - // ?x < BlankNodeId(1) - // expected: <(< BlankNodeId(1)), ?x> - evalAndEqualityCheck(ltSprql(var, BlankNodeId(1)), - pr(lt(BlankNodeId(1)), var)); - // ?x <= referenceDate1 - // expected: <(<= referenceDate1), ?x> - evalAndEqualityCheck(leSprql(var, dt.referenceDate1), - pr(le(dt.referenceDate1), var)); - // referenceDate1 >= ?x - // expected: <(<= referenceDate1), ?x> - evalAndEqualityCheck(geSprql(dt.referenceDate1, var), - pr(le(dt.referenceDate1), var)); - // DoubleId(10.2) < ?x - // expected: <(> DoubleId(10.2)), ?x> - evalAndEqualityCheck(ltSprql(DoubleId(10.2), var), - pr(gt(DoubleId(10.2)), var)); - // ?x > DoubleId(10.2) - // expected: <(> DoubleId(10.2)), ?x> - evalAndEqualityCheck(gtSprql(var, DoubleId(10.2)), - pr(gt(DoubleId(10.2)), var)); -} - -//______________________________________________________________________________ -// More complex relational SparqlExpressions for which -// getPrefilterExpressionForMetadata should yield a vector containing the actual -// corresponding PrefilterExpression values. -TEST(SparqlExpression, getPrefilterExpressionsToComplexSparqlExpressions) { - const Variable varX = Variable{"?x"}; - const Variable varY = Variable{"?y"}; - const Variable varZ = Variable{"?z"}; - // ?x >= 10 AND ?x != 20 - // expected prefilter pairs: - // {<((>= 10) AND (!= 20)), ?x>} - evalAndEqualityCheck( - andSprqlExpr(geSprql(varX, IntId(10)), neqSprql(varX, IntId(20))), - pr(andExpr(ge(IntId(10)), neq(IntId(20))), varX)); - // ?z > VocabId(0) AND ?y > 0 AND ?x < 30.00 - // expected prefilter pairs - // {<(< 30.00), ?x>, <(> 0), ?y>, <(> VocabId(0)), ?z>} - evalAndEqualityCheck(andSprqlExpr(andSprqlExpr(gtSprql(varZ, VocabId(0)), - gtSprql(varY, IntId(0))), - ltSprql(varX, DoubleId(30.00))), - pr(lt(DoubleId(30.00)), varX), pr(gt(IntId(0)), varY), - pr(gt(VocabId(0)), varZ)); - - // ?x == VocabId(10) AND ?y >= VocabId(10) - // expected prefilter pairs: - // {<(== VocabId(10)), ?x>, <(>= VocabId(10)), ?y>} - evalAndEqualityCheck( - andSprqlExpr(eqSprql(varX, VocabId(10)), geSprql(varY, VocabId(10))), - pr(eq(VocabId(10)), varX), pr(ge(VocabId(10)), varY)); - // !(?x >= 10 OR ?x <= 0) - // expected prefilter pairs: - // {= 10 OR ?x <= 0), ?x>} - evalAndEqualityCheck(notSprqlExpr(orSprqlExpr(geSprql(varX, IntId(10)), - leSprql(varX, IntId(0)))), - pr(notExpr(orExpr(ge(IntId(10)), le(IntId(0)))), varX)); - // !(?z == VocabId(10) AND ?z >= VocabId(20)) - // expected prefilter pairs: - // {= VocabId(20)) , ?z>} - evalAndEqualityCheck( - notSprqlExpr( - andSprqlExpr(eqSprql(varZ, VocabId(10)), geSprql(varZ, VocabId(20)))), - pr(notExpr(andExpr(eq(VocabId(10)), ge(VocabId(20)))), varZ)); - // (?x == VocabId(10) AND ?z == VocabId(0)) AND ?y != DoubleId(22.1) - // expected prefilter pairs: - // {<(==VocabId(10)) , ?x>, <(!=DoubleId(22.1)), ?y>, <(==VocabId(0)), ?z>} - evalAndEqualityCheck(andSprqlExpr(andSprqlExpr(eqSprql(VocabId(10), varX), - eqSprql(varZ, VocabId(0))), - neqSprql(DoubleId(22.1), varY)), - pr(eq(VocabId(10)), varX), pr(neq(DoubleId(22.1)), varY), - pr(eq(VocabId(0)), varZ)); - // (?z >= 1000 AND ?x == VocabId(10)) OR ?z >= 10000 - // expected prefilter pairs: - // {<((>=1000) OR (>= 10000)), ?z>} - evalAndEqualityCheck(orSprqlExpr(andSprqlExpr(geSprql(varZ, IntId(1000)), - eqSprql(varX, VocabId(10))), - geSprql(varZ, IntId(10000))), - pr(orExpr(ge(IntId(1000)), ge(IntId(10000))), varZ)); - // !((?z <= VocabId(10) OR ?y <= VocabId(10)) OR ?x <= VocabId(10)) - // expected prefilter pairs: - // {, , } - evalAndEqualityCheck( - notSprqlExpr(orSprqlExpr( - orSprqlExpr(leSprql(varZ, VocabId(10)), leSprql(varY, VocabId(10))), - leSprql(varX, VocabId(10)))), - pr(notExpr(le(VocabId(10))), varX), pr(notExpr(le(VocabId(10))), varY), - pr(notExpr(le(VocabId(10))), varZ)); - // ?x >= 10 AND ?y >= 10 - // expected prefilter pairs: - // {<(>= 10), ?x>, <(>= 10), ?y>} - evalAndEqualityCheck( - andSprqlExpr(geSprql(varX, IntId(10)), geSprql(varY, IntId(10))), - pr(ge(IntId(10)), varX), pr(ge(IntId(10)), varY)); - // ?x <= 0 AND ?y <= 0 - // expected prefilter pairs: - // {<(<= 0), ?x>, <(<= 0), ?y>} - evalAndEqualityCheck( - andSprqlExpr(leSprql(varX, IntId(0)), leSprql(varY, IntId(0))), - pr(le(IntId(0)), varX), pr(le(IntId(0)), varY)); - // (?x >= 10 AND ?y >= 10) OR (?x <= 0 AND ?y <= 0) - // expected prefilter pairs: - // {<((>= 10) OR (<= 0)), ?x> <(>= 10) OR (<= 0)), ?y>} - evalAndEqualityCheck( - orSprqlExpr( - andSprqlExpr(geSprql(varX, IntId(10)), geSprql(varY, IntId(10))), - andSprqlExpr(leSprql(varX, IntId(0)), leSprql(varY, IntId(0)))), - pr(orExpr(ge(IntId(10)), le(IntId(0))), varX), - pr(orExpr(ge(IntId(10)), le(IntId(0))), varY)); - // !(?x >= 10 OR ?y >= 10) OR !(?x <= 0 OR ?y <= 0) - // expected prefilter pairs: - // {<((!(>= 10) OR !(<= 0))), ?x> <(!(>= 10) OR !(<= 0))), ?y>} - evalAndEqualityCheck( - orSprqlExpr(notSprqlExpr(orSprqlExpr(geSprql(varX, IntId(10)), - geSprql(varY, IntId(10)))), - notSprqlExpr(orSprqlExpr(leSprql(varX, IntId(0)), - leSprql(varY, IntId(0))))), - pr(orExpr(notExpr(ge(IntId(10))), notExpr(le(IntId(0)))), varX), - pr(orExpr(notExpr(ge(IntId(10))), notExpr(le(IntId(0)))), varY)); - // !(?x == VocabId(10) OR ?x == VocabId(20)) AND !(?z >= 10.00 OR ?y == false) - // expected prefilter pairs: - // {, , - // = 10), ?z>} - evalAndEqualityCheck( - andSprqlExpr(notSprqlExpr(orSprqlExpr(eqSprql(varX, VocabId(10)), - eqSprql(varX, VocabId(20)))), - notSprqlExpr(orSprqlExpr(geSprql(varZ, DoubleId(10)), - eqSprql(varY, BoolId(false))))), - pr(notExpr(orExpr(eq(VocabId(10)), eq(VocabId(20)))), varX), - pr(notExpr(eq(BoolId(false))), varY), - pr(notExpr(ge(DoubleId(10))), varZ)); - // !(!(?x >= 10 AND ?y >= 10)) OR !(!(?x <= 0 AND ?y <= 0)) - // expected prefilter pairs: - // {<(!!(>= 10) OR !!(<= 0)), ?x>, <(!!(>= 10) OR !!(<= 0)) ,?y>} - evalAndEqualityCheck( - orSprqlExpr(notSprqlExpr(notSprqlExpr(andSprqlExpr( - geSprql(varX, IntId(10)), geSprql(varY, IntId(10))))), - notSprqlExpr(notSprqlExpr(andSprqlExpr( - leSprql(varX, IntId(0)), leSprql(varY, IntId(0)))))), - pr(orExpr(notExpr(notExpr(ge(IntId(10)))), - notExpr(notExpr(le(IntId(0))))), - varX), - pr(orExpr(notExpr(notExpr(ge(IntId(10)))), - notExpr(notExpr(le(IntId(0))))), - varY)); - // !((?x >= VocabId(0) AND ?x <= VocabId(10)) OR !(?x != VocabId(99))) - // expected prefilter pairs: - // {= VocabId(0)) AND (<= VocabId(10))) OR !(!= VocabId(99))) , ?x>} - evalAndEqualityCheck( - notSprqlExpr(orSprqlExpr( - andSprqlExpr(geSprql(varX, VocabId(0)), leSprql(varX, VocabId(10))), - notSprqlExpr(neqSprql(varX, VocabId(99))))), - pr(notExpr(orExpr(andExpr(ge(VocabId(0)), le(VocabId(10))), - notExpr(neq(VocabId(99))))), - varX)); - // !((?y >= 10 AND ?y <= 100) OR !(?x >= VocabId(99))) - // expected prefilter pairs: - // {= VocabId(0)) AND (<= VocabId(10)), ?y>, = VocabId(99))), ?x>} - evalAndEqualityCheck( - notSprqlExpr(orSprqlExpr( - andSprqlExpr(geSprql(varY, VocabId(0)), leSprql(varY, VocabId(10))), - notSprqlExpr(geSprql(varX, VocabId(99))))), - pr(notExpr(notExpr(ge(VocabId(99)))), varX), - pr(notExpr(andExpr(ge(VocabId(0)), le(VocabId(10)))), varY)); - // ?z >= 10 AND ?z <= 100 AND ?x >= 10 AND ?x != 50 AND !(?y <= 10) AND - // !(?city <= VocabId(1000) OR ?city == VocabId(1005)) - // expected prefilter pairs: - // {, <((>= 10) AND (!= - // 50)), ?x>, , <((>= 10) AND (<= 100)), ?z>} - evalAndEqualityCheck( - andSprqlExpr( - andSprqlExpr( - andSprqlExpr(geSprql(varZ, IntId(10)), leSprql(varZ, IntId(100))), - andSprqlExpr(andSprqlExpr(geSprql(varX, IntId(10)), - neqSprql(varX, IntId(50))), - notSprqlExpr(leSprql(varY, IntId(10))))), - notSprqlExpr(orSprqlExpr(leSprql(Variable{"?city"}, VocabId(1000)), - eqSprql(Variable{"?city"}, VocabId(1005))))), - pr(notExpr(orExpr(le(VocabId(1000)), eq(VocabId(1005)))), - Variable{"?city"}), - pr(andExpr(ge(IntId(10)), neq(IntId(50))), varX), - pr(notExpr(le(IntId(10))), varY), - pr(andExpr(ge(IntId(10)), le(IntId(100))), varZ)); - // ?x >= 10 OR (?x >= -10 AND ?x < 0.00) - // expected prefilter pairs: - // {<((>= 10) OR ((>= -10) AND (< 0.00))), ?x>} - evalAndEqualityCheck( - orSprqlExpr(geSprql(varX, IntId(10)), - andSprqlExpr(geSprql(varX, IntId(-10)), - ltSprql(varX, DoubleId(0.00)))), - pr(orExpr(ge(IntId(10)), andExpr(ge(IntId(-10)), lt(DoubleId(0.00)))), - varX)); - // !(!(?x >= 10) OR !!(?x >= -10 AND ?x < 0.00)) - // expected prefilter pairs: - // {= 10) OR !!((>= -10) AND (< 0.00))), ?x>} - evalAndEqualityCheck( - notSprqlExpr(orSprqlExpr( - notSprqlExpr(geSprql(varX, IntId(10))), - notSprqlExpr(notSprqlExpr(andSprqlExpr( - geSprql(varX, IntId(-10)), ltSprql(varX, DoubleId(0.00))))))), - pr(notExpr(orExpr( - notExpr(ge(IntId(10))), - notExpr(notExpr(andExpr(ge(IntId(-10)), lt(DoubleId(0.00))))))), - varX)); - // ?y != ?x AND ?x >= 10 - // expected prefilter pairs: - // {<(>= 10), ?x>} - evalAndEqualityCheck( - andSprqlExpr(neqSprql(varY, varX), geSprql(varX, IntId(10))), - pr(ge(IntId(10)), varX)); - evalAndEqualityCheck( - andSprqlExpr(geSprql(varX, IntId(10)), neqSprql(varY, varX)), - pr(ge(IntId(10)), varX)); -} - -//______________________________________________________________________________ -// For this test we expect that no PrefilterExpression is available. -TEST(SparqlExpression, getEmptyPrefilterFromSparqlRelational) { - const Variable var = Variable{"?x"}; - const Iri iri = I(""); - const Literal lit = L("\"lit\""); - evalAndEqualityCheck(leSprql(var, var)); - evalAndEqualityCheck(neqSprql(iri, var)); - evalAndEqualityCheck(eqSprql(var, iri)); - evalAndEqualityCheck(neqSprql(IntId(10), DoubleId(23.3))); - evalAndEqualityCheck(gtSprql(DoubleId(10), lit)); - evalAndEqualityCheck(ltSprql(VocabId(10), BoolId(10))); - evalAndEqualityCheck(geSprql(lit, lit)); - evalAndEqualityCheck(eqSprql(iri, iri)); - evalAndEqualityCheck(orSprqlExpr(eqSprql(var, var), gtSprql(var, IntId(0)))); - evalAndEqualityCheck(orSprqlExpr(eqSprql(var, var), gtSprql(var, var))); - evalAndEqualityCheck(andSprqlExpr(eqSprql(var, var), gtSprql(var, var))); -} - -//______________________________________________________________________________ -// For the following more complex SparqlExpression trees, we also expect an -// empty PrefilterExpression vector. -TEST(SparqlExpression, getEmptyPrefilterForMoreComplexSparqlExpressions) { - const Variable varX = Variable{"?x"}; - const Variable varY = Variable{"?y"}; - const Variable varZ = Variable{"?z"}; - // ?x <= 10.00 OR ?y > 10 - evalAndEqualityCheck( - orSprqlExpr(leSprql(DoubleId(10), varX), gtSprql(IntId(10), varY))); - // ?x >= VocabId(23) OR ?z == VocabId(1) - evalAndEqualityCheck( - orSprqlExpr(geSprql(varX, VocabId(23)), eqSprql(varZ, VocabId(1)))); - // (?x < VocabId(10) OR ?z <= VocabId(4)) OR ?z != 5.00 - evalAndEqualityCheck(orSprqlExpr( - orSprqlExpr(ltSprql(varX, VocabId(10)), leSprql(VocabId(4), varZ)), - neqSprql(varZ, DoubleId(5)))); - // !(?z > 10.20 AND ?x < 0.001) - // is equal to - // ?z <= 10.20 OR ?x >= 0.001 - evalAndEqualityCheck(notSprqlExpr(andSprqlExpr( - gtSprql(DoubleId(10.2), varZ), ltSprql(DoubleId(0.001), varX)))); - // !(?x > 10.20 AND ?z != VocabId(22)) - // is equal to - // ?x <= 10.20 OR ?z == VocabId(22) - evalAndEqualityCheck(notSprqlExpr(andSprqlExpr(gtSprql(DoubleId(10.2), varX), - neqSprql(VocabId(22), varZ)))); - // !(!((?x < VocabId(10) OR ?x <= VocabId(4)) OR ?z != 5.00)) - // is equal to - // (?x < VocabId(10) OR ?x <= VocabId(4)) OR ?z != 5.00 - evalAndEqualityCheck(notSprqlExpr(notSprqlExpr(orSprqlExpr( - orSprqlExpr(ltSprql(varX, VocabId(10)), leSprql(VocabId(4), varX)), - neqSprql(varZ, DoubleId(5)))))); - // !(?x != 10 AND !(?y >= 10.00 OR ?z <= 10)) - // is equal to - // ?x == 10 OR ?y >= 10.00 OR ?z <= 10 - evalAndEqualityCheck(notSprqlExpr( - andSprqlExpr(neqSprql(varX, IntId(10)), - notSprqlExpr(orSprqlExpr(geSprql(varY, DoubleId(10.00)), - leSprql(varZ, IntId(10))))))); - // !((?x != 10 AND ?z != 10) AND (?y == 10 AND ?x >= 20)) - // is equal to - //?x == 10 OR ?z == 10 OR ?y != 10 OR ?x < 20 - evalAndEqualityCheck(notSprqlExpr(andSprqlExpr( - andSprqlExpr(neqSprql(varX, IntId(10)), neqSprql(varZ, IntId(10))), - andSprqlExpr(eqSprql(varY, IntId(10)), geSprql(varX, IntId(20)))))); - // !(?z >= 40 AND (?z != 10.00 AND ?y != VocabId(1))) - // is equal to - // ?z <= 40 OR ?z == 10.00 OR ?y == VocabId(1) - evalAndEqualityCheck(notSprqlExpr(andSprqlExpr( - geSprql(varZ, IntId(40)), andSprqlExpr(neqSprql(varZ, DoubleId(10.00)), - neqSprql(varY, VocabId(1)))))); - // ?z <= true OR !(?x == 10 AND ?y == 10) - // is equal to - // ?z <= true OR ?x != 10 OR ?y != 10 - evalAndEqualityCheck( - orSprqlExpr(leSprql(varZ, BoolId(true)), - notSprqlExpr(andSprqlExpr(eqSprql(varX, IntId(10)), - eqSprql(IntId(10), varY))))); - // !(!(?z <= true OR !(?x == 10 AND ?y == 10))) - // is equal to - // ?z <= true OR ?x != 10 OR ?y != 10 - evalAndEqualityCheck(notSprqlExpr(notSprqlExpr( - orSprqlExpr(leSprql(varZ, BoolId(true)), - notSprqlExpr(andSprqlExpr(eqSprql(varX, IntId(10)), - eqSprql(IntId(10), varY))))))); - // !(!(?x != 10 OR !(?y >= 10.00 AND ?z <= 10))) - // is equal to - // ?x != 10 OR ?y < 10.00 OR ?z > 10 - evalAndEqualityCheck(notSprqlExpr(notSprqlExpr( - orSprqlExpr(neqSprql(varX, IntId(10)), - notSprqlExpr(andSprqlExpr(geSprql(varY, DoubleId(10.00)), - leSprql(varZ, IntId(10)))))))); - // !(!(?x == VocabId(10) OR ?y >= 25) AND !(!(?z == true AND ?country == - // VocabId(20)))) - // is equal to - // ?x == VocabId(10) OR ?y >= 25 OR ?z == true AND ?country == VocabId(20) - evalAndEqualityCheck(notSprqlExpr(andSprqlExpr( - notSprqlExpr( - orSprqlExpr(eqSprql(varX, VocabId(10)), geSprql(varY, IntId(25)))), - notSprqlExpr(notSprqlExpr( - andSprqlExpr(eqSprql(varZ, BoolId(true)), - eqSprql(Variable{"?country"}, VocabId(20)))))))); -} - -// Test PrefilterExpression creation for SparqlExpression STRSTARTS -//______________________________________________________________________________ -TEST(SparqlExpression, getPrefilterExprForStrStartsExpr) { - const auto varX = Variable{"?x"}; - const auto varY = Variable{"?y"}; - evalAndEqualityCheck(strStartsSprql(varX, VocabId(0)), - pr(ge(VocabId(0)), varX)); - evalAndEqualityCheck(strStartsSprql(VocabId(0), varX), - pr(le(VocabId(0)), varX)); - evalAndEqualityCheck(strStartsSprql(varX, varY)); - evalAndEqualityCheck(strStartsSprql(VocabId(0), VocabId(10))); -} - -// Test that the conditions required for a correct merge of child -// PrefilterExpressions are properly checked during the PrefilterExpression -// construction procedure. This check is applied in the SparqlExpression (for -// NOT, AND and OR) counter-expressions, while constructing their corresponding -// PrefilterExpression. -//______________________________________________________________________________ -TEST(SparqlExpression, checkPropertiesForPrefilterConstruction) { - namespace pd = prefilterExpressions::detail; - const Variable varX = Variable{"?x"}; - const Variable varY = Variable{"?y"}; - const Variable varZ = Variable{"?z"}; - const Variable varW = Variable{"?w"}; - std::vector vec{}; - vec.push_back(pr(andExpr(lt(IntId(5)), gt(DoubleId(-0.01))), varX)); - vec.push_back(pr(gt(VocabId(0)), varY)); - EXPECT_NO_THROW(pd::checkPropertiesForPrefilterConstruction(vec)); - vec.push_back(pr(eq(VocabId(33)), varZ)); - EXPECT_NO_THROW(pd::checkPropertiesForPrefilterConstruction(vec)); - // Add a pair with duplicate Variable. - vec.push_back(pr(gt(VocabId(0)), varZ)); - AD_EXPECT_THROW_WITH_MESSAGE( - pd::checkPropertiesForPrefilterConstruction(vec), - ::testing::HasSubstr("For each relevant Variable must exist exactly " - "one pair.")); - // Remove the last two pairs and add a pair - // which violates the order on Variable(s). - vec.pop_back(); - vec.pop_back(); - vec.push_back(pr(eq(VocabId(0)), varW)); - AD_EXPECT_THROW_WITH_MESSAGE( - pd::checkPropertiesForPrefilterConstruction(vec), - ::testing::HasSubstr( - "The vector must contain the " - "pairs in sorted order w.r.t. Variable value.")); -} diff --git a/test/GetPrefilterExpressionFromSparqlExpressionTest.cpp b/test/GetPrefilterExpressionFromSparqlExpressionTest.cpp new file mode 100644 index 0000000000..8a476db000 --- /dev/null +++ b/test/GetPrefilterExpressionFromSparqlExpressionTest.cpp @@ -0,0 +1,592 @@ +// Copyright 2024, University of Freiburg, +// Chair of Algorithms and Data Structures +// Author: Hannes Baumann + +#include "./PrefilterExpressionTestHelpers.h" +#include "./SparqlExpressionTestHelpers.h" +#include "util/GTestHelpers.h" + +using ad_utility::testing::BlankNodeId; +using ad_utility::testing::BoolId; +using ad_utility::testing::DoubleId; +using ad_utility::testing::IntId; +using ad_utility::testing::UndefId; +using ad_utility::testing::VocabId; + +using Literal = ad_utility::triple_component::Literal; +using Iri = ad_utility::triple_component::Iri; +using RelValues = std::variant; + +namespace { + +using namespace sparqlExpression; + +namespace makeSparqlExpression { + +//______________________________________________________________________________ +const auto makeLiteralSparqlExpr = + [](const auto& child) -> std::unique_ptr { + using T = std::decay_t; + if constexpr (std::is_same_v) { + return std::make_unique(child); + } else if constexpr (std::is_same_v) { + return std::make_unique(child); + } else if constexpr (std::is_same_v) { + return std::make_unique(child); + } else if constexpr (std::is_same_v) { + return std::make_unique(child); + } else { + throw std::runtime_error( + "Can't create a LiteralExpression from provided (input) type."); + } +}; + +//______________________________________________________________________________ +auto getExpr = [](const auto& variantVal) -> std::unique_ptr { + return makeLiteralSparqlExpr(variantVal); +}; + +//______________________________________________________________________________ +template +std::unique_ptr makeRelationalSparqlExprImpl( + const RelValues& child0, const RelValues& child1) { + return std::make_unique(std::array{ + std::visit(getExpr, child0), std::visit(getExpr, child1)}); +}; + +//______________________________________________________________________________ +std::unique_ptr makeStringStartsWithSparqlExpression( + const RelValues& child0, const RelValues& child1) { + return makeStrStartsExpression(std::visit(getExpr, child0), + std::visit(getExpr, child1)); +}; + +//______________________________________________________________________________ +// LESS THAN (`<`, `SparqlExpression`) +constexpr auto ltSprql = &makeRelationalSparqlExprImpl; +// LESS EQUAL (`<=`, `SparqlExpression`) +constexpr auto leSprql = &makeRelationalSparqlExprImpl; +// EQUAL (`==`, `SparqlExpression`) +constexpr auto eqSprql = &makeRelationalSparqlExprImpl; +// NOT EQUAL (`!=`, `SparqlExpression`) +constexpr auto neqSprql = &makeRelationalSparqlExprImpl; +// GREATER EQUAL (`>=`, `SparqlExpression`) +constexpr auto geSprql = &makeRelationalSparqlExprImpl; +// GREATER THAN (`>`, `SparqlExpression`) +constexpr auto gtSprql = &makeRelationalSparqlExprImpl; +// AND (`&&`, `SparqlExpression`) +constexpr auto andSprqlExpr = &makeAndExpression; +// OR (`||`, `SparqlExpression`) +constexpr auto orSprqlExpr = &makeOrExpression; +// NOT (`!`, `SparqlExpression`) +constexpr auto notSprqlExpr = &makeUnaryNegateExpression; + +//______________________________________________________________________________ +// Create SparqlExpression `STRSTARTS`. +constexpr auto strStartsSprql = &makeStringStartsWithSparqlExpression; + +} // namespace makeSparqlExpression + +//______________________________________________________________________________ +// make `Literal` +const auto L = [](const std::string& content) -> Literal { + return Literal::fromStringRepresentation(content); +}; + +//______________________________________________________________________________ +// make `Iri` +const auto I = [](const std::string& content) -> Iri { + return Iri::fromIriref(content); +}; + +//______________________________________________________________________________ +struct TestDates { + const Id referenceDate1 = DateId(DateParser, "1999-11-11"); + const Id referenceDate2 = DateId(DateParser, "2005-02-27"); +}; + +//______________________________________________________________________________ +// ASSERT EQUALITY +//______________________________________________________________________________ +const auto equalityCheckPrefilterVectors = + [](const std::vector& result, + const std::vector& expected) -> void { + ASSERT_EQ(result.size(), expected.size()); + const auto isEqualImpl = [](const PrefilterExprVariablePair& resPair, + const PrefilterExprVariablePair& expPair) { + if (*resPair.first != *expPair.first || resPair.second != expPair.second) { + std::stringstream stream; + stream << "The following value pairs don't match:" + << "\nRESULT: " << *resPair.first << "EXPECTED: " << *expPair.first + << "RESULT: VARIABLE" << resPair.second.name() + << "\nEXPECTED: VARIABLE" << expPair.second.name() << std::endl; + ADD_FAILURE() << stream.str(); + return false; + } + return true; + }; + ASSERT_TRUE( + std::equal(result.begin(), result.end(), expected.begin(), isEqualImpl)); +}; + +//______________________________________________________________________________ +// `evalAndEqualityCheck` evaluates the provided `SparqlExpression` and checks +// in the following if the resulting vector contains the same +// `` pairs in the correct order. If no +// `` pair is provided, the expected value for +// the `SparqlExpression` is an empty vector. +const auto evalAndEqualityCheck = + [](std::unique_ptr sparqlExpr, + std::convertible_to auto&&... prefilterArgs) { + std::vector prefilterVarPair = {}; + if constexpr (sizeof...(prefilterArgs) > 0) { + (prefilterVarPair.emplace_back( + std::forward(prefilterArgs)), + ...); + } + equalityCheckPrefilterVectors( + sparqlExpr->getPrefilterExpressionForMetadata(), + std::move(prefilterVarPair)); + }; + +//______________________________________________________________________________ +// Construct a `PAIR` with the given `PrefilterExpression` and `Variable` value. +auto pr = + [](std::unique_ptr expr, + const Variable& var) -> sparqlExpression::PrefilterExprVariablePair { + return {std::move(expr), var}; +}; + +} // namespace + +using namespace makeSparqlExpression; +using namespace makeFilterExpression; +using namespace makeSparqlExpression; + +//______________________________________________________________________________ +// Test coverage for the default implementation of +// getPrefilterExpressionForMetadata. +TEST(GetPrefilterExpressionFromSparqlExpression, + testGetPrefilterExpressionDefault) { + evalAndEqualityCheck( + makeUnaryMinusExpression(makeLiteralSparqlExpr(IntId(0)))); + evalAndEqualityCheck(makeMultiplyExpression( + makeLiteralSparqlExpr(DoubleId(11)), makeLiteralSparqlExpr(DoubleId(3)))); + evalAndEqualityCheck( + makeStrEndsExpression(makeLiteralSparqlExpr(L("\"Freiburg\"")), + makeLiteralSparqlExpr(L("\"burg\"")))); + evalAndEqualityCheck( + makeIsIriExpression(makeLiteralSparqlExpr(I("")))); + evalAndEqualityCheck(makeLogExpression(makeLiteralSparqlExpr(DoubleId(8)))); + evalAndEqualityCheck( + makeStrIriDtExpression(makeLiteralSparqlExpr(L("\"test\"")), + makeLiteralSparqlExpr(I("")))); +} + +//______________________________________________________________________________ +// Check that the (Sparql) RelationalExpression returns the expected +// PrefilterExpression. +TEST(GetPrefilterExpressionFromSparqlExpression, + getPrefilterExpressionFromSparqlRelational) { + const TestDates dt{}; + const Variable var = Variable{"?x"}; + // ?x == BooldId(true) (RelationalExpression Sparql) + // expected: <(== BoolId(true)), ?x> (PrefilterExpression, Variable) + evalAndEqualityCheck(eqSprql(var, BoolId(true)), pr(eq(BoolId(true)), var)); + // For BoolId(true) == ?x we expect the same PrefilterExpression pair. + evalAndEqualityCheck(eqSprql(BoolId(true), var), pr(eq(BoolId(true)), var)); + // ?x != BooldId(true) (RelationalExpression Sparql) + // expected: <(!= BoolId(true)), ?x> (PrefilterExpression, Variable) + evalAndEqualityCheck(neqSprql(var, BoolId(false)), + pr(neq(BoolId(false)), var)); + // Same expected value for BoolId(true) != ?x. + evalAndEqualityCheck(neqSprql(BoolId(false), var), + pr(neq(BoolId(false)), var)); + // ?x >= IntId(1) + // expected: <(>= IntId(1)), ?x> + evalAndEqualityCheck(geSprql(var, IntId(1)), pr(ge(IntId(1)), var)); + // IntId(1) <= ?x + // expected: <(>= IntId(1)), ?x> + evalAndEqualityCheck(leSprql(IntId(1), var), pr(ge(IntId(1)), var)); + // ?x > IntId(1) + // expected: <(> IntId(1)), ?x> + evalAndEqualityCheck(gtSprql(var, IntId(1)), pr(gt(IntId(1)), var)); + // VocabId(10) != ?x + // expected: <(!= VocabId(10)), ?x> + evalAndEqualityCheck(neqSprql(VocabId(10), var), pr(neq(VocabId(10)), var)); + // BlankNodeId(1) > ?x + // expected: <(< BlankNodeId(1)), ?x> + evalAndEqualityCheck(geSprql(BlankNodeId(1), var), + pr(le(BlankNodeId(1)), var)); + // ?x < BlankNodeId(1) + // expected: <(< BlankNodeId(1)), ?x> + evalAndEqualityCheck(ltSprql(var, BlankNodeId(1)), + pr(lt(BlankNodeId(1)), var)); + // ?x <= referenceDate1 + // expected: <(<= referenceDate1), ?x> + evalAndEqualityCheck(leSprql(var, dt.referenceDate1), + pr(le(dt.referenceDate1), var)); + // referenceDate1 >= ?x + // expected: <(<= referenceDate1), ?x> + evalAndEqualityCheck(geSprql(dt.referenceDate1, var), + pr(le(dt.referenceDate1), var)); + // DoubleId(10.2) < ?x + // expected: <(> DoubleId(10.2)), ?x> + evalAndEqualityCheck(ltSprql(DoubleId(10.2), var), + pr(gt(DoubleId(10.2)), var)); + // ?x > DoubleId(10.2) + // expected: <(> DoubleId(10.2)), ?x> + evalAndEqualityCheck(gtSprql(var, DoubleId(10.2)), + pr(gt(DoubleId(10.2)), var)); +} + +//______________________________________________________________________________ +// More complex relational SparqlExpressions for which +// getPrefilterExpressionForMetadata should yield a vector containing the actual +// corresponding PrefilterExpression values. +TEST(GetPrefilterExpressionFromSparqlExpression, + getPrefilterExpressionsToComplexSparqlExpressions) { + const Variable varX = Variable{"?x"}; + const Variable varY = Variable{"?y"}; + const Variable varZ = Variable{"?z"}; + // ?x >= 10 AND ?x != 20 + // expected prefilter pairs: + // {<((>= 10) AND (!= 20)), ?x>} + evalAndEqualityCheck( + andSprqlExpr(geSprql(varX, IntId(10)), neqSprql(varX, IntId(20))), + pr(andExpr(ge(IntId(10)), neq(IntId(20))), varX)); + // ?z > VocabId(0) AND ?y > 0 AND ?x < 30.00 + // expected prefilter pairs + // {<(< 30.00), ?x>, <(> 0), ?y>, <(> VocabId(0)), ?z>} + evalAndEqualityCheck(andSprqlExpr(andSprqlExpr(gtSprql(varZ, VocabId(0)), + gtSprql(varY, IntId(0))), + ltSprql(varX, DoubleId(30.00))), + pr(lt(DoubleId(30.00)), varX), pr(gt(IntId(0)), varY), + pr(gt(VocabId(0)), varZ)); + + // ?x == VocabId(10) AND ?y >= VocabId(10) + // expected prefilter pairs: + // {<(== VocabId(10)), ?x>, <(>= VocabId(10)), ?y>} + evalAndEqualityCheck( + andSprqlExpr(eqSprql(varX, VocabId(10)), geSprql(varY, VocabId(10))), + pr(eq(VocabId(10)), varX), pr(ge(VocabId(10)), varY)); + // !(?x >= 10 OR ?x <= 0) + // expected prefilter pairs: + // {= 10 OR ?x <= 0), ?x>} + evalAndEqualityCheck(notSprqlExpr(orSprqlExpr(geSprql(varX, IntId(10)), + leSprql(varX, IntId(0)))), + pr(notExpr(orExpr(ge(IntId(10)), le(IntId(0)))), varX)); + // !(?z == VocabId(10) AND ?z >= VocabId(20)) + // expected prefilter pairs: + // {= VocabId(20)) , ?z>} + evalAndEqualityCheck( + notSprqlExpr( + andSprqlExpr(eqSprql(varZ, VocabId(10)), geSprql(varZ, VocabId(20)))), + pr(notExpr(andExpr(eq(VocabId(10)), ge(VocabId(20)))), varZ)); + // (?x == VocabId(10) AND ?z == VocabId(0)) AND ?y != DoubleId(22.1) + // expected prefilter pairs: + // {<(==VocabId(10)) , ?x>, <(!=DoubleId(22.1)), ?y>, <(==VocabId(0)), ?z>} + evalAndEqualityCheck(andSprqlExpr(andSprqlExpr(eqSprql(VocabId(10), varX), + eqSprql(varZ, VocabId(0))), + neqSprql(DoubleId(22.1), varY)), + pr(eq(VocabId(10)), varX), pr(neq(DoubleId(22.1)), varY), + pr(eq(VocabId(0)), varZ)); + // (?z >= 1000 AND ?x == VocabId(10)) OR ?z >= 10000 + // expected prefilter pairs: + // {<((>=1000) OR (>= 10000)), ?z>} + evalAndEqualityCheck(orSprqlExpr(andSprqlExpr(geSprql(varZ, IntId(1000)), + eqSprql(varX, VocabId(10))), + geSprql(varZ, IntId(10000))), + pr(orExpr(ge(IntId(1000)), ge(IntId(10000))), varZ)); + // !((?z <= VocabId(10) OR ?y <= VocabId(10)) OR ?x <= VocabId(10)) + // expected prefilter pairs: + // {, , } + evalAndEqualityCheck( + notSprqlExpr(orSprqlExpr( + orSprqlExpr(leSprql(varZ, VocabId(10)), leSprql(varY, VocabId(10))), + leSprql(varX, VocabId(10)))), + pr(notExpr(le(VocabId(10))), varX), pr(notExpr(le(VocabId(10))), varY), + pr(notExpr(le(VocabId(10))), varZ)); + // ?x >= 10 AND ?y >= 10 + // expected prefilter pairs: + // {<(>= 10), ?x>, <(>= 10), ?y>} + evalAndEqualityCheck( + andSprqlExpr(geSprql(varX, IntId(10)), geSprql(varY, IntId(10))), + pr(ge(IntId(10)), varX), pr(ge(IntId(10)), varY)); + // ?x <= 0 AND ?y <= 0 + // expected prefilter pairs: + // {<(<= 0), ?x>, <(<= 0), ?y>} + evalAndEqualityCheck( + andSprqlExpr(leSprql(varX, IntId(0)), leSprql(varY, IntId(0))), + pr(le(IntId(0)), varX), pr(le(IntId(0)), varY)); + // (?x >= 10 AND ?y >= 10) OR (?x <= 0 AND ?y <= 0) + // expected prefilter pairs: + // {<((>= 10) OR (<= 0)), ?x> <(>= 10) OR (<= 0)), ?y>} + evalAndEqualityCheck( + orSprqlExpr( + andSprqlExpr(geSprql(varX, IntId(10)), geSprql(varY, IntId(10))), + andSprqlExpr(leSprql(varX, IntId(0)), leSprql(varY, IntId(0)))), + pr(orExpr(ge(IntId(10)), le(IntId(0))), varX), + pr(orExpr(ge(IntId(10)), le(IntId(0))), varY)); + // !(?x >= 10 OR ?y >= 10) OR !(?x <= 0 OR ?y <= 0) + // expected prefilter pairs: + // {<((!(>= 10) OR !(<= 0))), ?x> <(!(>= 10) OR !(<= 0))), ?y>} + evalAndEqualityCheck( + orSprqlExpr(notSprqlExpr(orSprqlExpr(geSprql(varX, IntId(10)), + geSprql(varY, IntId(10)))), + notSprqlExpr(orSprqlExpr(leSprql(varX, IntId(0)), + leSprql(varY, IntId(0))))), + pr(orExpr(notExpr(ge(IntId(10))), notExpr(le(IntId(0)))), varX), + pr(orExpr(notExpr(ge(IntId(10))), notExpr(le(IntId(0)))), varY)); + // !(?x == VocabId(10) OR ?x == VocabId(20)) AND !(?z >= 10.00 OR ?y == false) + // expected prefilter pairs: + // {, , + // = 10), ?z>} + evalAndEqualityCheck( + andSprqlExpr(notSprqlExpr(orSprqlExpr(eqSprql(varX, VocabId(10)), + eqSprql(varX, VocabId(20)))), + notSprqlExpr(orSprqlExpr(geSprql(varZ, DoubleId(10)), + eqSprql(varY, BoolId(false))))), + pr(notExpr(orExpr(eq(VocabId(10)), eq(VocabId(20)))), varX), + pr(notExpr(eq(BoolId(false))), varY), + pr(notExpr(ge(DoubleId(10))), varZ)); + // !(!(?x >= 10 AND ?y >= 10)) OR !(!(?x <= 0 AND ?y <= 0)) + // expected prefilter pairs: + // {<(!!(>= 10) OR !!(<= 0)), ?x>, <(!!(>= 10) OR !!(<= 0)) ,?y>} + evalAndEqualityCheck( + orSprqlExpr(notSprqlExpr(notSprqlExpr(andSprqlExpr( + geSprql(varX, IntId(10)), geSprql(varY, IntId(10))))), + notSprqlExpr(notSprqlExpr(andSprqlExpr( + leSprql(varX, IntId(0)), leSprql(varY, IntId(0)))))), + pr(orExpr(notExpr(notExpr(ge(IntId(10)))), + notExpr(notExpr(le(IntId(0))))), + varX), + pr(orExpr(notExpr(notExpr(ge(IntId(10)))), + notExpr(notExpr(le(IntId(0))))), + varY)); + // !((?x >= VocabId(0) AND ?x <= VocabId(10)) OR !(?x != VocabId(99))) + // expected prefilter pairs: + // {= VocabId(0)) AND (<= VocabId(10))) OR !(!= VocabId(99))) , ?x>} + evalAndEqualityCheck( + notSprqlExpr(orSprqlExpr( + andSprqlExpr(geSprql(varX, VocabId(0)), leSprql(varX, VocabId(10))), + notSprqlExpr(neqSprql(varX, VocabId(99))))), + pr(notExpr(orExpr(andExpr(ge(VocabId(0)), le(VocabId(10))), + notExpr(neq(VocabId(99))))), + varX)); + // !((?y >= 10 AND ?y <= 100) OR !(?x >= VocabId(99))) + // expected prefilter pairs: + // {= VocabId(0)) AND (<= VocabId(10)), ?y>, = VocabId(99))), ?x>} + evalAndEqualityCheck( + notSprqlExpr(orSprqlExpr( + andSprqlExpr(geSprql(varY, VocabId(0)), leSprql(varY, VocabId(10))), + notSprqlExpr(geSprql(varX, VocabId(99))))), + pr(notExpr(notExpr(ge(VocabId(99)))), varX), + pr(notExpr(andExpr(ge(VocabId(0)), le(VocabId(10)))), varY)); + // ?z >= 10 AND ?z <= 100 AND ?x >= 10 AND ?x != 50 AND !(?y <= 10) AND + // !(?city <= VocabId(1000) OR ?city == VocabId(1005)) + // expected prefilter pairs: + // {, <((>= 10) AND (!= + // 50)), ?x>, , <((>= 10) AND (<= 100)), ?z>} + evalAndEqualityCheck( + andSprqlExpr( + andSprqlExpr( + andSprqlExpr(geSprql(varZ, IntId(10)), leSprql(varZ, IntId(100))), + andSprqlExpr(andSprqlExpr(geSprql(varX, IntId(10)), + neqSprql(varX, IntId(50))), + notSprqlExpr(leSprql(varY, IntId(10))))), + notSprqlExpr(orSprqlExpr(leSprql(Variable{"?city"}, VocabId(1000)), + eqSprql(Variable{"?city"}, VocabId(1005))))), + pr(notExpr(orExpr(le(VocabId(1000)), eq(VocabId(1005)))), + Variable{"?city"}), + pr(andExpr(ge(IntId(10)), neq(IntId(50))), varX), + pr(notExpr(le(IntId(10))), varY), + pr(andExpr(ge(IntId(10)), le(IntId(100))), varZ)); + // ?x >= 10 OR (?x >= -10 AND ?x < 0.00) + // expected prefilter pairs: + // {<((>= 10) OR ((>= -10) AND (< 0.00))), ?x>} + evalAndEqualityCheck( + orSprqlExpr(geSprql(varX, IntId(10)), + andSprqlExpr(geSprql(varX, IntId(-10)), + ltSprql(varX, DoubleId(0.00)))), + pr(orExpr(ge(IntId(10)), andExpr(ge(IntId(-10)), lt(DoubleId(0.00)))), + varX)); + // !(!(?x >= 10) OR !!(?x >= -10 AND ?x < 0.00)) + // expected prefilter pairs: + // {= 10) OR !!((>= -10) AND (< 0.00))), ?x>} + evalAndEqualityCheck( + notSprqlExpr(orSprqlExpr( + notSprqlExpr(geSprql(varX, IntId(10))), + notSprqlExpr(notSprqlExpr(andSprqlExpr( + geSprql(varX, IntId(-10)), ltSprql(varX, DoubleId(0.00))))))), + pr(notExpr(orExpr( + notExpr(ge(IntId(10))), + notExpr(notExpr(andExpr(ge(IntId(-10)), lt(DoubleId(0.00))))))), + varX)); + // ?y != ?x AND ?x >= 10 + // expected prefilter pairs: + // {<(>= 10), ?x>} + evalAndEqualityCheck( + andSprqlExpr(neqSprql(varY, varX), geSprql(varX, IntId(10))), + pr(ge(IntId(10)), varX)); + evalAndEqualityCheck( + andSprqlExpr(geSprql(varX, IntId(10)), neqSprql(varY, varX)), + pr(ge(IntId(10)), varX)); +} + +//______________________________________________________________________________ +// For this test we expect that no PrefilterExpression is available. +TEST(GetPrefilterExpressionFromSparqlExpression, + getEmptyPrefilterFromSparqlRelational) { + const Variable var = Variable{"?x"}; + const Iri iri = I(""); + const Literal lit = L("\"lit\""); + evalAndEqualityCheck(leSprql(var, var)); + evalAndEqualityCheck(neqSprql(iri, var)); + evalAndEqualityCheck(eqSprql(var, iri)); + evalAndEqualityCheck(neqSprql(IntId(10), DoubleId(23.3))); + evalAndEqualityCheck(gtSprql(DoubleId(10), lit)); + evalAndEqualityCheck(ltSprql(VocabId(10), BoolId(10))); + evalAndEqualityCheck(geSprql(lit, lit)); + evalAndEqualityCheck(eqSprql(iri, iri)); + evalAndEqualityCheck(orSprqlExpr(eqSprql(var, var), gtSprql(var, IntId(0)))); + evalAndEqualityCheck(orSprqlExpr(eqSprql(var, var), gtSprql(var, var))); + evalAndEqualityCheck(andSprqlExpr(eqSprql(var, var), gtSprql(var, var))); +} + +//______________________________________________________________________________ +// For the following more complex SparqlExpression trees, we also expect an +// empty PrefilterExpression vector. +TEST(GetPrefilterExpressionFromSparqlExpression, + getEmptyPrefilterForMoreComplexSparqlExpressions) { + const Variable varX = Variable{"?x"}; + const Variable varY = Variable{"?y"}; + const Variable varZ = Variable{"?z"}; + // ?x <= 10.00 OR ?y > 10 + evalAndEqualityCheck( + orSprqlExpr(leSprql(DoubleId(10), varX), gtSprql(IntId(10), varY))); + // ?x >= VocabId(23) OR ?z == VocabId(1) + evalAndEqualityCheck( + orSprqlExpr(geSprql(varX, VocabId(23)), eqSprql(varZ, VocabId(1)))); + // (?x < VocabId(10) OR ?z <= VocabId(4)) OR ?z != 5.00 + evalAndEqualityCheck(orSprqlExpr( + orSprqlExpr(ltSprql(varX, VocabId(10)), leSprql(VocabId(4), varZ)), + neqSprql(varZ, DoubleId(5)))); + // !(?z > 10.20 AND ?x < 0.001) + // is equal to + // ?z <= 10.20 OR ?x >= 0.001 + evalAndEqualityCheck(notSprqlExpr(andSprqlExpr( + gtSprql(DoubleId(10.2), varZ), ltSprql(DoubleId(0.001), varX)))); + // !(?x > 10.20 AND ?z != VocabId(22)) + // is equal to + // ?x <= 10.20 OR ?z == VocabId(22) + evalAndEqualityCheck(notSprqlExpr(andSprqlExpr(gtSprql(DoubleId(10.2), varX), + neqSprql(VocabId(22), varZ)))); + // !(!((?x < VocabId(10) OR ?x <= VocabId(4)) OR ?z != 5.00)) + // is equal to + // (?x < VocabId(10) OR ?x <= VocabId(4)) OR ?z != 5.00 + evalAndEqualityCheck(notSprqlExpr(notSprqlExpr(orSprqlExpr( + orSprqlExpr(ltSprql(varX, VocabId(10)), leSprql(VocabId(4), varX)), + neqSprql(varZ, DoubleId(5)))))); + // !(?x != 10 AND !(?y >= 10.00 OR ?z <= 10)) + // is equal to + // ?x == 10 OR ?y >= 10.00 OR ?z <= 10 + evalAndEqualityCheck(notSprqlExpr( + andSprqlExpr(neqSprql(varX, IntId(10)), + notSprqlExpr(orSprqlExpr(geSprql(varY, DoubleId(10.00)), + leSprql(varZ, IntId(10))))))); + // !((?x != 10 AND ?z != 10) AND (?y == 10 AND ?x >= 20)) + // is equal to + //?x == 10 OR ?z == 10 OR ?y != 10 OR ?x < 20 + evalAndEqualityCheck(notSprqlExpr(andSprqlExpr( + andSprqlExpr(neqSprql(varX, IntId(10)), neqSprql(varZ, IntId(10))), + andSprqlExpr(eqSprql(varY, IntId(10)), geSprql(varX, DoubleId(20)))))); + // !(?z >= 40 AND (?z != 10.00 AND ?y != VocabId(1))) + // is equal to + // ?z <= 40 OR ?z == 10.00 OR ?y == VocabId(1) + evalAndEqualityCheck(notSprqlExpr(andSprqlExpr( + geSprql(varZ, IntId(40)), andSprqlExpr(neqSprql(varZ, DoubleId(10.00)), + neqSprql(varY, VocabId(1)))))); + // ?z <= true OR !(?x == 10 AND ?y == 10) + // is equal to + // ?z <= true OR ?x != 10 OR ?y != 10 + evalAndEqualityCheck( + orSprqlExpr(leSprql(varZ, BoolId(true)), + notSprqlExpr(andSprqlExpr(eqSprql(varX, IntId(10)), + eqSprql(IntId(10), varY))))); + // !(!(?z <= true OR !(?x == 10 AND ?y == 10))) + // is equal to + // ?z <= true OR ?x != 10 OR ?y != 10 + evalAndEqualityCheck(notSprqlExpr(notSprqlExpr( + orSprqlExpr(leSprql(varZ, BoolId(true)), + notSprqlExpr(andSprqlExpr(eqSprql(varX, IntId(10)), + eqSprql(IntId(10), varY))))))); + // !(!(?x != 10 OR !(?y >= 10.00 AND ?z <= 10))) + // is equal to + // ?x != 10 OR ?y < 10.00 OR ?z > 10 + evalAndEqualityCheck(notSprqlExpr(notSprqlExpr( + orSprqlExpr(neqSprql(varX, IntId(10)), + notSprqlExpr(andSprqlExpr(geSprql(varY, DoubleId(10.00)), + leSprql(varZ, IntId(10)))))))); + // !(!(?x == VocabId(10) OR ?y >= 25) AND !(!(?z == true AND ?country == + // VocabId(20)))) + // is equal to + // ?x == VocabId(10) OR ?y >= 25 OR ?z == true AND ?country == VocabId(20) + evalAndEqualityCheck(notSprqlExpr(andSprqlExpr( + notSprqlExpr( + orSprqlExpr(eqSprql(varX, VocabId(10)), geSprql(varY, IntId(25)))), + notSprqlExpr(notSprqlExpr( + andSprqlExpr(eqSprql(varZ, BoolId(true)), + eqSprql(Variable{"?country"}, VocabId(20)))))))); +} + +// Test PrefilterExpression creation for SparqlExpression STRSTARTS +//______________________________________________________________________________ +TEST(GetPrefilterExpressionFromSparqlExpression, + getPrefilterExprForStrStartsExpr) { + const auto varX = Variable{"?x"}; + const auto varY = Variable{"?y"}; + evalAndEqualityCheck(strStartsSprql(varX, VocabId(0)), + pr(ge(VocabId(0)), varX)); + evalAndEqualityCheck(strStartsSprql(VocabId(0), varX), + pr(le(VocabId(0)), varX)); + evalAndEqualityCheck(strStartsSprql(varX, varY)); + evalAndEqualityCheck(strStartsSprql(VocabId(0), VocabId(10))); +} + +// Test that the conditions required for a correct merge of child +// PrefilterExpressions are properly checked during the PrefilterExpression +// construction procedure. This check is applied in the SparqlExpression (for +// NOT, AND and OR) counter-expressions, while constructing their corresponding +// PrefilterExpression. +//______________________________________________________________________________ +TEST(GetPrefilterExpressionFromSparqlExpression, + checkPropertiesForPrefilterConstruction) { + namespace pd = prefilterExpressions::detail; + const Variable varX = Variable{"?x"}; + const Variable varY = Variable{"?y"}; + const Variable varZ = Variable{"?z"}; + const Variable varW = Variable{"?w"}; + std::vector vec{}; + vec.push_back(pr(andExpr(lt(IntId(5)), gt(DoubleId(-0.01))), varX)); + vec.push_back(pr(gt(VocabId(0)), varY)); + EXPECT_NO_THROW(pd::checkPropertiesForPrefilterConstruction(vec)); + vec.push_back(pr(eq(VocabId(33)), varZ)); + EXPECT_NO_THROW(pd::checkPropertiesForPrefilterConstruction(vec)); + // Add a pair with duplicate Variable. + vec.push_back(pr(gt(VocabId(0)), varZ)); + AD_EXPECT_THROW_WITH_MESSAGE( + pd::checkPropertiesForPrefilterConstruction(vec), + ::testing::HasSubstr("For each relevant Variable must exist exactly " + "one pair.")); + // Remove the last two pairs and add a pair + // which violates the order on Variable(s). + vec.pop_back(); + vec.pop_back(); + vec.push_back(pr(eq(VocabId(0)), varW)); + AD_EXPECT_THROW_WITH_MESSAGE( + pd::checkPropertiesForPrefilterConstruction(vec), + ::testing::HasSubstr( + "The vector must contain the " + "pairs in sorted order w.r.t. Variable value.")); +} diff --git a/test/PrefilterExpressionIndexTest.cpp b/test/PrefilterExpressionIndexTest.cpp new file mode 100644 index 0000000000..a941afab6a --- /dev/null +++ b/test/PrefilterExpressionIndexTest.cpp @@ -0,0 +1,600 @@ +// Copyright 2024, University of Freiburg, +// Chair of Algorithms and Data Structures +// Author: Hannes Baumann + +#include + +#include + +#include "./PrefilterExpressionTestHelpers.h" +#include "./SparqlExpressionTestHelpers.h" +#include "util/GTestHelpers.h" + +using ad_utility::testing::BlankNodeId; +using ad_utility::testing::BoolId; +using ad_utility::testing::DoubleId; +using ad_utility::testing::IntId; +using ad_utility::testing::UndefId; +using ad_utility::testing::VocabId; + +namespace { + +using namespace prefilterExpressions; +using namespace makeFilterExpression; + +//______________________________________________________________________________ +/* +Our pre-filtering procedure expects blocks that are in correct (ascending) +order w.r.t. their contained ValueIds given the first and last triple. + +The correct order of the ValueIds is dependent on their type and underlying +representation. + +Short overview on the ascending order logic for the underlying values: +Order ValueIds for (signed) integer values - [0... max, -max... -1] +Order ValueIds for (signed) doubles values - [0.0... max, -0.0... -max] +Order ValueIds for Vocab and LocalVocab values given the vocabulary with +indices (up to N) - [VocabId(0), .... VocabId(N)] + +COLUMN 1 and COLUMN 2 contain fixed values, this is a necessary condition +that is also checked during the pre-filtering procedure. The actual evaluation +column (we filter w.r.t. values of COLUMN 0) contains mixed types. +*/ +//______________________________________________________________________________ +class PrefilterExpressionOnMetadataTest : public ::testing::Test { + public: + const Id referenceDate1 = DateId(DateParser, "1999-11-11"); + const Id referenceDate2 = DateId(DateParser, "2005-02-27"); + const Id undef = Id::makeUndefined(); + const Id falseId = BoolId(false); + const Id trueId = BoolId(true); + const Id referenceDateEqual = DateId(DateParser, "2000-01-01"); + + // Fixed column ValueIds + const Id VocabId10 = VocabId(10); + const Id DoubleId33 = DoubleId(33); + const Id GraphId = VocabId(0); + + // Define BlockMetadata + const BlockMetadata b1 = makeBlock(undef, undef); + const BlockMetadata b2 = makeBlock(undef, falseId); + const BlockMetadata b3 = makeBlock(falseId, falseId); + const BlockMetadata b4 = makeBlock(trueId, IntId(0)); + const BlockMetadata b5 = makeBlock(IntId(0), IntId(0)); + const BlockMetadata b6 = makeBlock(IntId(0), IntId(5)); + const BlockMetadata b7 = makeBlock(IntId(5), IntId(6)); + const BlockMetadata b8 = makeBlock(IntId(8), IntId(9)); + const BlockMetadata b9 = makeBlock(IntId(-10), IntId(-8)); + const BlockMetadata b10 = makeBlock(IntId(-4), IntId(-4)); + const BlockMetadata b11 = makeBlock(IntId(-4), DoubleId(2)); + const BlockMetadata b12 = makeBlock(DoubleId(2), DoubleId(2)); + const BlockMetadata b13 = makeBlock(DoubleId(4), DoubleId(4)); + const BlockMetadata b14 = makeBlock(DoubleId(4), DoubleId(10)); + const BlockMetadata b15 = makeBlock(DoubleId(-1.23), DoubleId(-6.25)); + const BlockMetadata b16 = makeBlock(DoubleId(-6.25), DoubleId(-6.25)); + const BlockMetadata b17 = makeBlock(DoubleId(-10.42), DoubleId(-12.00)); + const BlockMetadata b18 = makeBlock(DoubleId(-14.01), VocabId(0)); + const BlockMetadata b19 = makeBlock(VocabId(10), VocabId(14)); + const BlockMetadata b20 = makeBlock(VocabId(14), VocabId(14)); + const BlockMetadata b21 = makeBlock(VocabId(14), VocabId(17)); + const BlockMetadata b22 = + makeBlock(VocabId(20), DateId(DateParser, "1999-12-12")); + const BlockMetadata b23 = makeBlock(DateId(DateParser, "2000-01-01"), + DateId(DateParser, "2000-01-01")); + const BlockMetadata b24 = + makeBlock(DateId(DateParser, "2024-10-08"), BlankNodeId(10)); + + // All blocks that contain mixed (ValueId) types over column 0 + const std::vector mixedBlocks = {b2, b4, b11, b18, b22, b24}; + + // Ordered and unique vector with BlockMetadata + const std::vector blocks = { + b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, + b13, b14, b15, b16, b17, b18, b19, b20, b21, b22, b23, b24}; + + const std::vector blocksInvalidOrder1 = { + b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, + b13, b14, b15, b16, b17, b18, b19, b20, b21, b22, b24, b23}; + + const std::vector blocksInvalidOrder2 = { + b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, + b14, b10, b15, b16, b17, b18, b19, b20, b21, b22, b23, b24}; + + const std::vector blocksWithDuplicate1 = { + b1, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, + b13, b14, b15, b16, b17, b18, b19, b20, b21, b22, b23, b24}; + + const std::vector blocksWithDuplicate2 = { + b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, + b14, b15, b16, b17, b18, b19, b20, b21, b22, b23, b24, b24}; + + // Function to create BlockMetadata + const BlockMetadata makeBlock(const ValueId& firstId, const ValueId& lastId) { + assert(firstId <= lastId); + static size_t blockIdx = 0; + ++blockIdx; + return {{{}, + 0, + // COLUMN 0 | COLUMN 1 | COLUMN 2 + {firstId, VocabId10, DoubleId33, GraphId}, // firstTriple + {lastId, VocabId10, DoubleId33, GraphId}, // lastTriple + {}, + false}, + blockIdx}; + } + + // Check if expected error is thrown. + auto makeTestErrorCheck(std::unique_ptr expr, + const std::vector& input, + const std::string& expected, + size_t evaluationColumn = 0) { + AD_EXPECT_THROW_WITH_MESSAGE(expr->evaluate(input, evaluationColumn), + ::testing::HasSubstr(expected)); + } + + // Assert that the PrefilterExpression tree is properly copied when calling + // method clone. + auto makeTestClone(std::unique_ptr expr) { + ASSERT_EQ(*expr, *expr->clone()); + } + + // Check that the provided expression prefilters the correct blocks. + auto makeTest(std::unique_ptr expr, + std::vector&& expected) { + std::vector expectedAdjusted; + // This is for convenience, we automatically insert all mixed blocks + // which must be always returned. + std::ranges::set_union( + expected, mixedBlocks, std::back_inserter(expectedAdjusted), + [](const BlockMetadata& b1, const BlockMetadata& b2) { + return b1.blockIndex_ < b2.blockIndex_; + }); + ASSERT_EQ(expr->evaluate(blocks, 0), expectedAdjusted); + } +}; + +} // namespace + +//______________________________________________________________________________ +TEST_F(PrefilterExpressionOnMetadataTest, testBlockFormatForDebugging) { + EXPECT_EQ( + "#BlockMetadata\n(first) Triple: I:0 V:10 D:33.000000 V:0\n(last) " + "Triple: I:0 V:10 D:33.000000 V:0\nnum. rows: 0.\n", + (std::stringstream() << b5).str()); + EXPECT_EQ( + "#BlockMetadata\n(first) Triple: I:-4 V:10 D:33.000000 V:0\n(last) " + "Triple: D:2.000000 V:10 D:33.000000 V:0\nnum. rows: 0.\n", + (std::stringstream() << b11).str()); + EXPECT_EQ( + "#BlockMetadata\n(first) Triple: V:14 V:10 D:33.000000 V:0\n(last) " + "Triple: V:17 V:10 D:33.000000 V:0\nnum. rows: 0.\n", + (std::stringstream() << b21).str()); +} + +// Test Relational Expressions +//______________________________________________________________________________ +// Test LessThanExpression +// Note: the `makeTest` function automatically adds the blocks with mixed +// datatypes to the expected result. +TEST_F(PrefilterExpressionOnMetadataTest, testLessThanExpressions) { + makeTest(lt(IntId(5)), + {b5, b6, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18}); + makeTest(lt(IntId(-12)), {b18}); + makeTest(lt(IntId(0)), {b9, b10, b15, b16, b17, b18}); + makeTest(lt(DoubleId(-14.01)), {b18}); + makeTest(lt(DoubleId(-11.22)), {b17, b18}); + makeTest(lt(DoubleId(-4.121)), {b9, b15, b16, b17, b18}); + makeTest(lt(VocabId(0)), {b18}); + makeTest(lt(VocabId(12)), {b18, b19}); + makeTest(lt(VocabId(14)), {b18, b19}); + makeTest(lt(VocabId(16)), {b18, b19, b20, b21}); + makeTest(lt(IntId(100)), + {b5, b6, b7, b8, b9, b10, b12, b13, b14, b15, b16, b17, b18}); + makeTest(lt(undef), {}); + makeTest(lt(falseId), {}); + makeTest(lt(trueId), {b2, b3}); + makeTest(lt(referenceDate1), {}); + makeTest(lt(referenceDateEqual), {b22}); + makeTest(lt(referenceDate2), {b22, b23, b24}); + makeTest(lt(BlankNodeId(11)), {b24}); +} + +//______________________________________________________________________________ +// Test LessEqualExpression +// Note: the `makeTest` function automatically adds the blocks with mixed +// datatypes to the expected result. +TEST_F(PrefilterExpressionOnMetadataTest, testLessEqualExpressions) { + makeTest(le(IntId(0)), {b5, b6, b9, b10, b11, b15, b16, b17, b18}); + makeTest(le(IntId(-6)), {b9, b11, b15, b16, b17, b18}); + makeTest(le(IntId(7)), + {b5, b6, b7, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18}); + makeTest(le(IntId(-9)), {b9, b11, b17, b18}); + makeTest(le(DoubleId(-9.131)), {b9, b11, b17, b18}); + makeTest(le(DoubleId(1.1415)), {b5, b6, b9, b10, b11, b15, b16, b17, b18}); + makeTest(le(DoubleId(3.1415)), + {b5, b6, b9, b10, b11, b12, b15, b16, b17, b18}); + makeTest(le(DoubleId(-11.99999999999999)), {b17, b18}); + makeTest(le(DoubleId(-14.03)), {b18}); + makeTest(le(VocabId(0)), {b18}); + makeTest(le(VocabId(11)), {b18, b19}); + makeTest(le(VocabId(14)), {b18, b19, b20, b21}); + makeTest(le(undef), {}); + makeTest(le(falseId), {b2, b3}); + makeTest(le(trueId), {b2, b3, b4}); + makeTest(le(referenceDateEqual), {b22, b23}); + makeTest(le(BlankNodeId(11)), {b24}); +} + +//______________________________________________________________________________ +// Test GreaterThanExpression +// Note: the `makeTest` function automatically adds the blocks with mixed +// datatypes to the expected result. +TEST_F(PrefilterExpressionOnMetadataTest, testGreaterThanExpression) { + makeTest(gt(DoubleId(5.5375)), {b7, b8, b11, b14, b18}); + makeTest(gt(DoubleId(9.9994)), {b14}); + makeTest(gt(IntId(-5)), {b5, b6, b7, b8, b10, b11, b12, b13, b14, b15}); + makeTest(gt(DoubleId(-5.5375)), + {b5, b6, b7, b8, b10, b11, b12, b13, b14, b15}); + makeTest(gt(DoubleId(-6.2499999)), + {b5, b6, b7, b8, b10, b11, b12, b13, b14, b15}); + makeTest(gt(IntId(1)), {b6, b7, b8, b11, b12, b13, b14}); + makeTest(gt(IntId(3)), {b6, b7, b8, b11, b13, b14}); + makeTest(gt(IntId(4)), {b6, b7, b8, b11, b14}); + makeTest(gt(IntId(-4)), {b5, b6, b7, b8, b11, b12, b13, b14, b15}); + makeTest(gt(IntId(33)), {}); + makeTest(gt(VocabId(22)), {b22}); + makeTest(gt(VocabId(14)), {b21, b22}); + makeTest(gt(VocabId(12)), {b19, b20, b21, b22}); + makeTest(gt(undef), {}); + makeTest(gt(falseId), {b4}); + makeTest(gt(trueId), {}); + makeTest(gt(referenceDateEqual), {b24}); + makeTest(gt(referenceDate1), {b22, b23, b24}); + makeTest(gt(referenceDate2), {b24}); +} + +//______________________________________________________________________________ +// Test GreaterEqualExpression +// Note: the `makeTest` function automatically adds the blocks with mixed +// datatypes to the expected result. +TEST_F(PrefilterExpressionOnMetadataTest, testGreaterEqualExpression) { + makeTest(ge(IntId(0)), {b5, b6, b7, b8, b11, b12, b13, b14}); + makeTest(ge(IntId(8)), {b8, b11, b14}); + makeTest(ge(DoubleId(9.98)), {b11, b14}); + makeTest(ge(IntId(-3)), {b5, b6, b7, b8, b11, b12, b13, b14, b15}); + makeTest(ge(IntId(-10)), + {b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16}); + makeTest(ge(DoubleId(-3.1415)), {b5, b6, b7, b8, b11, b12, b13, b14, b15}); + makeTest(ge(DoubleId(-4.000001)), + {b5, b6, b7, b8, b10, b11, b12, b13, b14, b15}); + makeTest(ge(DoubleId(10.000)), {b11, b14}); + makeTest(ge(DoubleId(-15.22)), + {b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18}); + makeTest(ge(DoubleId(7.999999)), {b8, b11, b14}); + makeTest(ge(DoubleId(10.0001)), {}); + makeTest(ge(VocabId(14)), {b18, b19, b20, b21, b22}); + makeTest(ge(VocabId(10)), {b18, b19, b20, b21, b22}); + makeTest(ge(VocabId(17)), {b18, b21, b22}); + makeTest(ge(undef), {}); + makeTest(ge(falseId), {b2, b3, b4}); + makeTest(ge(trueId), {b4}); + makeTest(ge(referenceDateEqual), {b23, b24}); +} + +//______________________________________________________________________________ +// Test EqualExpression +// Note: the `makeTest` function automatically adds the blocks with mixed +// datatypes to the expected result. +TEST_F(PrefilterExpressionOnMetadataTest, testEqualExpression) { + makeTest(eq(IntId(0)), {b4, b5, b6, b11}); + makeTest(eq(IntId(5)), {b6, b7, b11, b14}); + makeTest(eq(IntId(22)), {}); + makeTest(eq(IntId(-10)), {b9, b11, b18}); + makeTest(eq(DoubleId(-6.25)), {b15, b16}); + makeTest(eq(IntId(-11)), {b17}); + makeTest(eq(DoubleId(-14.02)), {b18}); + makeTest(eq(DoubleId(-0.001)), {b11}); + makeTest(eq(DoubleId(0)), {b4, b5, b6, b11}); + makeTest(eq(IntId(2)), {b6, b11, b12}); + makeTest(eq(DoubleId(5.5)), {b7, b11, b14}); + makeTest(eq(DoubleId(1.5)), {b6, b11}); + makeTest(eq(VocabId(1)), {b18}); + makeTest(eq(VocabId(14)), {b18, b19, b20, b21}); + makeTest(eq(VocabId(11)), {b18, b19}); + makeTest(eq(VocabId(17)), {b18, b21}); + makeTest(eq(IntId(-4)), {b10, b11, b15}); + makeTest(eq(trueId), {b4}); + makeTest(eq(referenceDate1), {b22}); + makeTest(eq(referenceDateEqual), {b23}); + makeTest(eq(referenceDate2), {}); +} + +//______________________________________________________________________________ +// Test NotEqualExpression +// Note: the `makeTest` function automatically adds the blocks with mixed +// datatypes to the expected result. +TEST_F(PrefilterExpressionOnMetadataTest, testNotEqualExpression) { + makeTest(neq(DoubleId(0.00)), + {b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18}); + makeTest(neq(IntId(-4)), + {b5, b6, b7, b8, b9, b11, b12, b13, b14, b15, b16, b17, b18}); + makeTest(neq(DoubleId(0.001)), + {b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18}); + makeTest(neq(IntId(2)), + {b5, b6, b7, b8, b9, b10, b11, b13, b14, b15, b16, b17, b18}); + makeTest(neq(DoubleId(-6.2500)), + {b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b17, b18}); + makeTest(neq(IntId(5)), + {b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18}); + makeTest(neq(DoubleId(-101.23)), + {b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18}); + makeTest(neq(VocabId(0)), {b19, b20, b21, b22}); + makeTest(neq(VocabId(7)), {b18, b19, b20, b21, b22}); + makeTest(neq(VocabId(14)), {b18, b19, b21, b22}); + makeTest(neq(VocabId(17)), {b18, b19, b20, b21, b22}); + makeTest(neq(undef), {}); + makeTest(neq(falseId), {b4}); + makeTest(neq(referenceDateEqual), {b22, b24}); + makeTest(neq(referenceDate1), {b22, b23, b24}); +} + +// Test Logical Expressions +//______________________________________________________________________________ +// Test AndExpression +// Note: the `makeTest` function automatically adds the blocks with mixed +// datatypes to the expected result. +TEST_F(PrefilterExpressionOnMetadataTest, testAndExpression) { + makeTest(andExpr(ge(VocabId(10)), gt(VocabId(10))), {b19, b20, b21, b22}); + makeTest(andExpr(ge(VocabId(10)), ge(VocabId(10))), {b19, b20, b21, b22}); + makeTest(andExpr(ge(VocabId(12)), gt(VocabId(17))), {b22}); + makeTest(andExpr(ge(VocabId(12)), gt(VocabId(17))), {b22}); + makeTest(andExpr(ge(VocabId(10)), lt(VocabId(14))), {b19}); + makeTest(andExpr(le(VocabId(0)), lt(VocabId(10))), {b18}); + makeTest(andExpr(le(VocabId(17)), lt(VocabId(17))), {b18, b19, b20, b21}); + makeTest(andExpr(ge(DoubleId(-6.25)), lt(IntId(-7))), {}); + makeTest(andExpr(gt(DoubleId(-6.25)), lt(DoubleId(-6.25))), {}); + makeTest(andExpr(gt(IntId(0)), lt(IntId(0))), {}); + makeTest(andExpr(gt(IntId(-10)), lt(DoubleId(0))), {b9, b10, b11, b15, b16}); + makeTest(andExpr(gt(IntId(0)), eq(DoubleId(0))), {b6, b11}); + makeTest(andExpr(ge(IntId(0)), eq(IntId(0))), {b5, b6, b11}); + makeTest(andExpr(gt(DoubleId(-34.23)), ge(DoubleId(15.1))), {}); + makeTest(andExpr(lt(IntId(0)), le(DoubleId(-4))), + {b9, b10, b11, b15, b16, b17, b18}); + makeTest(andExpr(neq(IntId(0)), neq(IntId(-4))), + {b6, b7, b8, b9, b11, b12, b13, b14, b15, b16, b17, b18}); + makeTest(andExpr(neq(DoubleId(-3.141)), eq(DoubleId(4.5))), + {b6, b11, b14, b18}); + makeTest(andExpr(neq(DoubleId(-6.25)), lt(IntId(0))), + {b9, b10, b11, b15, b17, b18}); + makeTest(andExpr(le(DoubleId(-4)), ge(DoubleId(1))), {}); + makeTest(andExpr(le(DoubleId(-2)), eq(IntId(-3))), {b11, b15}); + makeTest(andExpr(andExpr(le(IntId(10)), gt(DoubleId(0))), eq(undef)), {}); + makeTest(andExpr(gt(referenceDate1), le(IntId(10))), {}); + makeTest(andExpr(gt(IntId(4)), andExpr(gt(DoubleId(8)), lt(IntId(10)))), + {b8, b14}); + makeTest(andExpr(eq(IntId(0)), andExpr(lt(IntId(-20)), gt(IntId(30)))), {}); + makeTest(andExpr(eq(IntId(0)), andExpr(le(IntId(0)), ge(IntId(0)))), + {b4, b5, b6, b11}); +} + +//______________________________________________________________________________ +// Test OrExpression +// Note: the `makeTest` function automatically adds the blocks with mixed +// datatypes to the expected result. +TEST_F(PrefilterExpressionOnMetadataTest, testOrExpression) { + makeTest(orExpr(lt(VocabId(22)), le(VocabId(0))), {b18, b19, b20, b21}); + makeTest(orExpr(le(VocabId(0)), ge(VocabId(16))), {b18, b21, b22}); + makeTest(orExpr(gt(VocabId(17)), ge(VocabId(17))), {b21, b22}); + makeTest(orExpr(lt(DoubleId(-5.95)), eq(VocabId(14))), + {b9, b15, b16, b17, b18, b19, b20, b21}); + makeTest(orExpr(eq(DoubleId(0)), neq(VocabId(14))), + {b5, b6, b11, b18, b19, b21}); + makeTest(orExpr(eq(DoubleId(0)), eq(DoubleId(-6.25))), + {b5, b6, b11, b15, b16, b18}); + makeTest(orExpr(gt(undef), le(IntId(-6))), {b9, b15, b16, b17, b18}); + makeTest(orExpr(le(trueId), gt(referenceDate1)), {b2, b3, b4, b22, b23, b24}); + makeTest(orExpr(eq(IntId(0)), orExpr(lt(IntId(-10)), gt(IntId(8)))), + {b5, b6, b8, b11, b14, b17, b18}); + makeTest(orExpr(gt(referenceDate2), eq(trueId)), {b4}); + makeTest(orExpr(eq(VocabId(17)), orExpr(lt(VocabId(0)), gt(VocabId(20)))), + {b21, b22}); + makeTest(orExpr(eq(undef), gt(referenceDateEqual)), {b24}); + makeTest(orExpr(gt(IntId(8)), gt(DoubleId(22.1))), {b8, b14}); + makeTest(orExpr(lt(DoubleId(-8.25)), le(IntId(-10))), {b9, b17, b18}); + makeTest(orExpr(eq(IntId(0)), neq(DoubleId(0.25))), + {b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18}); + makeTest(orExpr(gt(referenceDate1), orExpr(gt(trueId), eq(IntId(0)))), + {b4, b5, b6, b11, b22, b23, b24}); + makeTest(orExpr(gt(DoubleId(-6.25)), lt(DoubleId(-6.25))), + {b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b17, b18}); + makeTest(orExpr(orExpr(eq(IntId(0)), eq(IntId(5))), + orExpr(eq(DoubleId(-6.25)), lt(DoubleId(-12)))), + {b4, b5, b6, b7, b11, b14, b15, b16, b18}); + makeTest(orExpr(le(trueId), gt(falseId)), {b2, b3, b4}); + makeTest(orExpr(eq(VocabId(0)), eq(DoubleId(0.25))), {b6, b11, b18}); +} + +//______________________________________________________________________________ +// Test NotExpression +// Note: the `makeTest` function automatically adds the blocks with mixed +// datatypes to the expected result. +TEST_F(PrefilterExpressionOnMetadataTest, testNotExpression) { + makeTest(notExpr(eq(VocabId(2))), {b18, b19, b20, b21, b22}); + makeTest(notExpr(eq(VocabId(14))), {b18, b19, b21, b22}); + makeTest(notExpr(neq(VocabId(14))), {b19, b20, b21}); + makeTest(notExpr(gt(VocabId(2))), {b18}); + makeTest(notExpr(lt(DoubleId(-14.01))), + {b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18}); + makeTest(notExpr(ge(DoubleId(-14.01))), {b18}); + makeTest(notExpr(gt(DoubleId(-4.00))), {b9, b10, b11, b15, b16, b17, b18}); + makeTest(notExpr(ge(DoubleId(-24.4))), {b18}); + makeTest(notExpr(gt(referenceDate2)), {b22, b23}); + makeTest(notExpr(le(trueId)), {}); + makeTest(notExpr(le(IntId(0))), {b6, b7, b8, b11, b12, b13, b14}); + makeTest(notExpr(gt(undef)), {}); + makeTest(notExpr(eq(DoubleId(-6.25))), + {b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b17, b18}); + makeTest(notExpr(neq(DoubleId(4))), {b6, b11, b13, b14, b18}); + makeTest(notExpr(gt(DoubleId(0))), + {b4, b5, b6, b9, b10, b11, b15, b16, b17, b18}); + makeTest(notExpr(notExpr(eq(IntId(0)))), {b4, b5, b6, b11}); + makeTest(notExpr(notExpr(neq(DoubleId(-6.25)))), + {b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b17, b18}); + makeTest(notExpr(notExpr(lt(VocabId(10)))), {b18}); + makeTest(notExpr(notExpr(ge(DoubleId(3.99)))), {b6, b7, b8, b11, b13, b14}); + makeTest(notExpr(andExpr(le(IntId(0)), ge(IntId(0)))), + {b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18}); + makeTest(notExpr(andExpr(neq(IntId(-10)), neq(DoubleId(-14.02)))), {b9, b18}); + makeTest( + notExpr(andExpr(gt(IntId(10)), ge(DoubleId(-6.25)))), + {b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18}); + makeTest( + notExpr(andExpr(lt(DoubleId(-7)), ge(IntId(6)))), + {b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18}); + makeTest(notExpr(orExpr(le(IntId(0)), ge(DoubleId(6)))), + {b6, b7, b11, b12, b13, b14}); + makeTest(notExpr(orExpr(ge(DoubleId(0)), gt(IntId(-10)))), + {b9, b11, b17, b18}); + makeTest(notExpr(orExpr(lt(VocabId(10)), gt(VocabId(10)))), {b19}); + makeTest(notExpr(orExpr(lt(DoubleId(-4)), gt(IntId(-4)))), {b10, b11, b15}); + makeTest(notExpr(orExpr(gt(IntId(-42)), ge(VocabId(0)))), {b11}); + makeTest(notExpr(orExpr(ge(VocabId(14)), gt(VocabId(15)))), {b18, b19}); +} + +//______________________________________________________________________________ +// Test PrefilterExpressions mixed +// Note: the `makeTest` function automatically adds the blocks with mixed +// datatypes to the expected result. +TEST_F(PrefilterExpressionOnMetadataTest, + testGeneralPrefilterExprCombinations) { + makeTest(andExpr(notExpr(gt(DoubleId(-14.01))), lt(IntId(0))), {b18}); + makeTest( + orExpr(andExpr(gt(DoubleId(8.25)), le(IntId(10))), eq(DoubleId(-6.25))), + {b8, b14, b15, b16}); + makeTest( + orExpr(andExpr(gt(DoubleId(8.25)), le(IntId(10))), lt(DoubleId(-6.25))), + {b8, b9, b14, b17, b18}); + makeTest(andExpr(orExpr(ge(trueId), le(falseId)), eq(referenceDate1)), {}); + makeTest(andExpr(eq(IntId(0)), orExpr(lt(IntId(-11)), le(IntId(-12)))), {}); + makeTest( + andExpr(eq(DoubleId(-4)), orExpr(gt(IntId(-4)), lt(DoubleId(-1.25)))), + {b10, b11, b15}); + makeTest(orExpr(notExpr(andExpr(lt(IntId(10)), gt(IntId(5)))), eq(IntId(0))), + {b4, b5, b6, b7, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18}); + makeTest(andExpr(orExpr(gt(VocabId(16)), le(VocabId(5))), gt(DoubleId(7.25))), + {}); + makeTest(andExpr(lt(falseId), orExpr(lt(IntId(10)), gt(DoubleId(17.25)))), + {}); + makeTest( + orExpr(andExpr(gt(VocabId(16)), ge(VocabId(17))), gt(DoubleId(7.25))), + {b8, b14, b18, b21, b22}); + makeTest(orExpr(eq(trueId), andExpr(gt(referenceDate1), lt(referenceDate2))), + {b4, b22, b23}); +} + +//______________________________________________________________________________ +// Test that correct errors are thrown for invalid input (condition) +TEST_F(PrefilterExpressionOnMetadataTest, testInputConditionCheck) { + makeTestErrorCheck(le(IntId(5)), blocksWithDuplicate1, + "The provided data blocks must be unique."); + makeTestErrorCheck(andExpr(gt(VocabId(10)), le(VocabId(20))), + blocksWithDuplicate2, + "The provided data blocks must be unique."); + makeTestErrorCheck(gt(DoubleId(2)), blocksInvalidOrder1, + "The blocks must be provided in sorted order."); + makeTestErrorCheck(andExpr(gt(VocabId(10)), le(VocabId(20))), + blocksInvalidOrder2, + "The blocks must be provided in sorted order."); + makeTestErrorCheck( + gt(DoubleId(2)), blocks, + "The values in the columns up to the evaluation column must be " + "consistent.", + 1); + makeTestErrorCheck( + gt(DoubleId(2)), blocks, + "The values in the columns up to the evaluation column must be " + "consistent.", + 2); +} + +//______________________________________________________________________________ +// Check for correctness given only one BlockMetadata value is provided. +TEST_F(PrefilterExpressionOnMetadataTest, testWithOneBlockMetadataValue) { + auto expr = orExpr(eq(DoubleId(-6.25)), eq(IntId(0))); + std::vector input = {b16}; + EXPECT_EQ(expr->evaluate(input, 0), input); + EXPECT_EQ(expr->evaluate(input, 1), std::vector{}); + EXPECT_EQ(expr->evaluate(input, 2), std::vector{}); +} + +//______________________________________________________________________________ +// Test method clone. clone() creates a copy of the complete PrefilterExpression +// tree. +TEST_F(PrefilterExpressionOnMetadataTest, testMethodClonePrefilterExpression) { + makeTestClone(lt(VocabId(10))); + makeTestClone(gt(referenceDate2)); + makeTestClone(andExpr(lt(VocabId(20)), gt(VocabId(10)))); + makeTestClone(neq(IntId(10))); + makeTestClone(orExpr(eq(IntId(10)), neq(DoubleId(10)))); + makeTestClone(notExpr(ge(referenceDate1))); + makeTestClone(notExpr(notExpr(neq(VocabId(0))))); + makeTestClone(notExpr(andExpr(eq(IntId(10)), neq(DoubleId(10))))); + makeTestClone(orExpr(orExpr(eq(VocabId(101)), lt(IntId(100))), + andExpr(gt(referenceDate1), lt(referenceDate2)))); + makeTestClone(andExpr(andExpr(neq(IntId(10)), neq(DoubleId(100.23))), + orExpr(gt(DoubleId(0.001)), lt(IntId(250))))); + makeTestClone(orExpr(orExpr(eq(VocabId(101)), lt(IntId(100))), + notExpr(andExpr(lt(VocabId(0)), neq(IntId(100)))))); +} + +//______________________________________________________________________________ +// Test PrefilterExpression equality operator. +TEST_F(PrefilterExpressionOnMetadataTest, testEqualityOperator) { + // Relational PrefilterExpressions + ASSERT_FALSE(*ge(referenceDate1) == *ge(referenceDate2)); + ASSERT_FALSE(*neq(BoolId(true)) == *eq(BoolId(true))); + ASSERT_TRUE(*eq(IntId(1)) == *eq(IntId(1))); + ASSERT_TRUE(*ge(referenceDate1) == *ge(referenceDate1)); + // NotExpression + ASSERT_TRUE(*notExpr(eq(IntId(0))) == *notExpr(eq(IntId(0)))); + ASSERT_TRUE(*notExpr(notExpr(ge(VocabId(0)))) == + *notExpr(notExpr(ge(VocabId(0))))); + ASSERT_FALSE(*notExpr(gt(IntId(0))) == *eq(IntId(0))); + ASSERT_FALSE(*notExpr(andExpr(eq(IntId(1)), eq(IntId(0)))) == + *notExpr(ge(VocabId(0)))); + // Binary PrefilterExpressions (AND and OR) + ASSERT_TRUE(*orExpr(eq(IntId(0)), le(IntId(0))) == + *orExpr(eq(IntId(0)), le(IntId(0)))); + ASSERT_TRUE(*andExpr(le(VocabId(1)), le(IntId(0))) == + *andExpr(le(VocabId(1)), le(IntId(0)))); + ASSERT_FALSE(*orExpr(eq(IntId(0)), le(IntId(0))) == + *andExpr(le(VocabId(1)), le(IntId(0)))); + ASSERT_FALSE(*notExpr(orExpr(eq(IntId(0)), le(IntId(0)))) == + *orExpr(eq(IntId(0)), le(IntId(0)))); +} + +//______________________________________________________________________________ +// Test PrefilterExpression content formatting for debugging. +TEST(PrefilterExpressionExpressionOnMetadataTest, + checkPrintFormattedPrefilterExpression) { + auto expr = lt(IntId(10)); + EXPECT_EQ((std::stringstream() << *expr).str(), + "Prefilter RelationalExpression\nValueId: I:10\n.\n"); + expr = neq(DoubleId(8.21)); + EXPECT_EQ((std::stringstream() << *expr).str(), + "Prefilter RelationalExpression\nValueId: D:8.210000\n.\n"); + expr = notExpr(eq(VocabId(0))); + EXPECT_EQ((std::stringstream() << *expr).str(), + "Prefilter NotExpression:\nchild {Prefilter " + "RelationalExpression\nValueId: V:0\n}\n.\n"); + expr = orExpr(le(IntId(0)), ge(IntId(5))); + EXPECT_EQ((std::stringstream() << *expr).str(), + "Prefilter LogicalExpression\nchild1 {Prefilter " + "RelationalExpression\nValueId: I:0\n}child2 {Prefilter " + "RelationalExpression=)>\nValueId: I:5\n}\n.\n"); + expr = andExpr(lt(IntId(20)), gt(IntId(10))); + EXPECT_EQ((std::stringstream() << *expr).str(), + "Prefilter LogicalExpression\nchild1 {Prefilter " + "RelationalExpression\nValueId: I:20\n}child2 {Prefilter " + "RelationalExpression)>\nValueId: I:10\n}\n.\n"); +} diff --git a/test/PrefilterExpressionTestHelpers.h b/test/PrefilterExpressionTestHelpers.h new file mode 100644 index 0000000000..d148e8dc57 --- /dev/null +++ b/test/PrefilterExpressionTestHelpers.h @@ -0,0 +1,70 @@ +// Copyright 2024, University of Freiburg, +// Chair of Algorithms and Data Structures +// Author: Hannes Baumann + +#pragma once + +#include + +#include "./engine/sparqlExpressions/LiteralExpression.h" +#include "./engine/sparqlExpressions/NaryExpression.h" +#include "./engine/sparqlExpressions/PrefilterExpressionIndex.h" +#include "./engine/sparqlExpressions/RelationalExpressions.h" +#include "./engine/sparqlExpressions/SparqlExpression.h" +#include "util/DateYearDuration.h" +#include "util/IdTestHelpers.h" + +using ad_utility::testing::DateId; + +constexpr auto DateParser = &DateYearOrDuration::parseXsdDate; + +namespace makeFilterExpression { +using namespace prefilterExpressions; + +namespace { +//______________________________________________________________________________ +// Make RelationalExpression +template +auto relExpr = + [](const ValueId& referenceId) -> std::unique_ptr { + return std::make_unique(referenceId); +}; + +// Make AndExpression or OrExpression +template +auto logExpr = [](std::unique_ptr child1, + std::unique_ptr child2) + -> std::unique_ptr { + return std::make_unique(std::move(child1), std::move(child2)); +}; + +// Make NotExpression +auto notPrefilterExpression = [](std::unique_ptr child) + -> std::unique_ptr { + return std::make_unique(std::move(child)); +}; +} // namespace + +// Make PrefilterExpression +//______________________________________________________________________________ +// instantiation relational +// LESS THAN (`<`) +constexpr auto lt = relExpr; +// LESS EQUAL (`<=`) +constexpr auto le = relExpr; +// GREATER EQUAL (`>=`) +constexpr auto ge = relExpr; +// GREATER THAN (`>`) +constexpr auto gt = relExpr; +// EQUAL (`==`) +constexpr auto eq = relExpr; +// NOT EQUAL (`!=`) +constexpr auto neq = relExpr; +// AND (`&&`) +constexpr auto andExpr = logExpr; +// OR (`||`) +constexpr auto orExpr = logExpr; +// NOT (`!`) +constexpr auto notExpr = notPrefilterExpression; + +} // namespace makeFilterExpression From 5144b47d355b571c6796c0b74bfdbc4ac71d1d97 Mon Sep 17 00:00:00 2001 From: "c.u." Date: Thu, 14 Nov 2024 10:39:09 +0100 Subject: [PATCH 53/81] Add STDEV() aggregate function (#1614) Add a new aggregate function `STDEV(X)` which computes the (sample) standard deviation, such that a user will not have to repetitively type `math:sqrt(sum(math:pow((X - avg(X)), 2)) / (count(*) - 1))`. This is not part of the SPARQL standard, but also doesn't cause any conflicts. --- src/engine/GroupBy.cpp | 3 + .../sparqlExpressions/AggregateExpression.cpp | 6 + src/engine/sparqlExpressions/CMakeLists.txt | 1 + .../sparqlExpressions/StdevExpression.cpp | 80 + .../sparqlExpressions/StdevExpression.h | 100 + .../sparqlParser/SparqlQleverVisitor.cpp | 3 + src/parser/sparqlParser/SparqlQleverVisitor.h | 1 + .../sparqlParser/generated/SparqlAutomatic.g4 | 2 + .../generated/SparqlAutomatic.interp | 4 +- .../generated/SparqlAutomatic.tokens | 75 +- .../generated/SparqlAutomaticLexer.cpp | 2514 +++++++++-------- .../generated/SparqlAutomaticLexer.h | 75 +- .../generated/SparqlAutomaticLexer.interp | 5 +- .../generated/SparqlAutomaticLexer.tokens | 75 +- .../generated/SparqlAutomaticParser.cpp | 2074 +++++++------- .../generated/SparqlAutomaticParser.h | 76 +- test/AggregateExpressionTest.cpp | 33 + test/GroupByTest.cpp | 1 + test/SparqlAntlrParserTest.cpp | 29 + test/SparqlExpressionTest.cpp | 4 + test/SparqlParserTest.cpp | 14 + 21 files changed, 2755 insertions(+), 2420 deletions(-) create mode 100644 src/engine/sparqlExpressions/StdevExpression.cpp create mode 100644 src/engine/sparqlExpressions/StdevExpression.h diff --git a/src/engine/GroupBy.cpp b/src/engine/GroupBy.cpp index e6ff853c48..d478b7d88e 100644 --- a/src/engine/GroupBy.cpp +++ b/src/engine/GroupBy.cpp @@ -20,6 +20,7 @@ #include "engine/sparqlExpressions/SampleExpression.h" #include "engine/sparqlExpressions/SparqlExpression.h" #include "engine/sparqlExpressions/SparqlExpressionGenerators.h" +#include "engine/sparqlExpressions/StdevExpression.h" #include "global/RuntimeParameters.h" #include "index/Index.h" #include "index/IndexImpl.h" @@ -1026,6 +1027,8 @@ GroupBy::isSupportedAggregate(sparqlExpression::SparqlExpression* expr) { if (auto val = dynamic_cast(expr)) { return H{GROUP_CONCAT, val->getSeparator()}; } + // NOTE: The STDEV function is not suitable for lazy and hash map + // optimizations. if (dynamic_cast(expr)) return H{SAMPLE}; // `expr` is an unsupported aggregate diff --git a/src/engine/sparqlExpressions/AggregateExpression.cpp b/src/engine/sparqlExpressions/AggregateExpression.cpp index e3ed846ec7..87ca749209 100644 --- a/src/engine/sparqlExpressions/AggregateExpression.cpp +++ b/src/engine/sparqlExpressions/AggregateExpression.cpp @@ -6,6 +6,7 @@ #include "engine/sparqlExpressions/AggregateExpression.h" #include "engine/sparqlExpressions/GroupConcatExpression.h" +#include "engine/sparqlExpressions/StdevExpression.h" namespace sparqlExpression::detail { @@ -180,6 +181,11 @@ AggregateExpression::getVariableForCount() // Explicit instantiation for the AVG expression. template class AggregateExpression; +// Explicit instantiation for the STDEV expression. +template class AggregateExpression; +template class DeviationAggExpression; + // Explicit instantiations for the other aggregate expressions. #define INSTANTIATE_AGG_EXP(Function, ValueGetter) \ template class AggregateExpression< \ diff --git a/src/engine/sparqlExpressions/CMakeLists.txt b/src/engine/sparqlExpressions/CMakeLists.txt index 17a96d689e..27e7ba018e 100644 --- a/src/engine/sparqlExpressions/CMakeLists.txt +++ b/src/engine/sparqlExpressions/CMakeLists.txt @@ -6,6 +6,7 @@ add_library(sparqlExpressions SampleExpression.cpp RelationalExpressions.cpp AggregateExpression.cpp + StdevExpression.cpp RegexExpression.cpp NumericUnaryExpressions.cpp NumericBinaryExpressions.cpp diff --git a/src/engine/sparqlExpressions/StdevExpression.cpp b/src/engine/sparqlExpressions/StdevExpression.cpp new file mode 100644 index 0000000000..c05e1cfe4d --- /dev/null +++ b/src/engine/sparqlExpressions/StdevExpression.cpp @@ -0,0 +1,80 @@ +// Copyright 2024, University of Freiburg, +// Chair of Algorithms and Data Structures. +// Author: Christoph Ullinger + +#include "engine/sparqlExpressions/StdevExpression.h" + +#include "engine/sparqlExpressions/SparqlExpressionTypes.h" + +namespace sparqlExpression::detail { + +// _____________________________________________________________________________ +ExpressionResult DeviationExpression::evaluate( + EvaluationContext* context) const { + // Helper: Extracts a double or int (as double) from a variant + auto numValVisitor = [](const T& value) -> std::optional { + if constexpr (ad_utility::isSimilar || + ad_utility::isSimilar) { + return static_cast(value); + } else { + return std::nullopt; + } + }; + + // Helper to replace child expression results with their squared deviation + auto devImpl = [context, numValVisitor]( + bool& undef, + VectorWithMemoryLimit& exprResult, + auto generator) { + double sum = 0.0; + // Intermediate storage of the results returned from the child + // expression + VectorWithMemoryLimit childResults{context->_allocator}; + + // Collect values as doubles + for (auto& inp : generator) { + const auto& n = detail::NumericValueGetter{}(std::move(inp), context); + auto v = std::visit(numValVisitor, n); + if (v.has_value()) { + childResults.push_back(v.value()); + sum += v.value(); + } else { + // There is a non-numeric value in the input. Therefore the entire + // result will be undef. + undef = true; + return; + } + context->cancellationHandle_->throwIfCancelled(); + } + + // Calculate squared deviation and save for result + double avg = sum / static_cast(context->size()); + for (size_t i = 0; i < childResults.size(); i++) { + exprResult.at(i) = IdOrLiteralOrIri{ + ValueId::makeFromDouble(std::pow(childResults.at(i) - avg, 2))}; + } + }; + + // Visitor for child expression result + auto impl = [context, + devImpl](SingleExpressionResult auto&& el) -> ExpressionResult { + // Prepare space for result + VectorWithMemoryLimit exprResult{context->_allocator}; + exprResult.resize(context->size()); + bool undef = false; + + auto generator = + detail::makeGenerator(AD_FWD(el), context->size(), context); + devImpl(undef, exprResult, std::move(generator)); + + if (undef) { + return IdOrLiteralOrIri{Id::makeUndefined()}; + } + return exprResult; + }; + + auto childRes = child_->evaluate(context); + return std::visit(impl, std::move(childRes)); +}; + +} // namespace sparqlExpression::detail diff --git a/src/engine/sparqlExpressions/StdevExpression.h b/src/engine/sparqlExpressions/StdevExpression.h new file mode 100644 index 0000000000..348ea3f5d4 --- /dev/null +++ b/src/engine/sparqlExpressions/StdevExpression.h @@ -0,0 +1,100 @@ +// Copyright 2024, University of Freiburg, +// Chair of Algorithms and Data Structures. +// Author: Christoph Ullinger + +#pragma once + +#include +#include +#include +#include + +#include "engine/sparqlExpressions/AggregateExpression.h" +#include "engine/sparqlExpressions/LiteralExpression.h" +#include "engine/sparqlExpressions/NaryExpression.h" +#include "engine/sparqlExpressions/SparqlExpression.h" +#include "engine/sparqlExpressions/SparqlExpressionTypes.h" +#include "engine/sparqlExpressions/SparqlExpressionValueGetters.h" +#include "global/ValueId.h" + +namespace sparqlExpression { + +namespace detail { + +/// The STDEV Expression + +// Helper expression: The individual deviation squares. A DeviationExpression +// over X corresponds to the value (X - AVG(X))^2. +class DeviationExpression : public SparqlExpression { + private: + Ptr child_; + + public: + explicit DeviationExpression(Ptr&& child) : child_{std::move(child)} {} + + // __________________________________________________________________________ + ExpressionResult evaluate(EvaluationContext* context) const override; + + // __________________________________________________________________________ + AggregateStatus isAggregate() const override { + return SparqlExpression::AggregateStatus::NoAggregate; + } + + // __________________________________________________________________________ + [[nodiscard]] string getCacheKey( + const VariableToColumnMap& varColMap) const override { + return absl::StrCat("[ SQ.DEVIATION ]", child_->getCacheKey(varColMap)); + } + + private: + // _________________________________________________________________________ + std::span childrenImpl() override { + return {&child_, 1}; + } +}; + +// Separate subclass of AggregateOperation, that replaces its child with a +// DeviationExpression of this child. Everything else is left untouched. +template +class DeviationAggExpression + : public AggregateExpression { + public: + // __________________________________________________________________________ + DeviationAggExpression(bool distinct, SparqlExpression::Ptr&& child, + AggregateOperation aggregateOp = AggregateOperation{}) + : AggregateExpression( + distinct, std::make_unique(std::move(child)), + aggregateOp){}; +}; + +// The final operation for dividing by degrees of freedom and calculation square +// root after summing up the squared deviation +inline auto stdevFinalOperation = [](const NumericValue& aggregation, + size_t numElements) { + auto divAndRoot = [](double value, double degreesOfFreedom) { + if (degreesOfFreedom <= 0) { + return 0.0; + } else { + return std::sqrt(value / degreesOfFreedom); + } + }; + return makeNumericExpressionForAggregate()( + aggregation, NumericValue{static_cast(numElements) - 1}); +}; + +// The actual Standard Deviation Expression +// Mind the explicit instantiation of StdevExpressionBase in +// AggregateExpression.cpp +using StdevExpressionBase = + DeviationAggExpression; +class StdevExpression : public StdevExpressionBase { + using StdevExpressionBase::StdevExpressionBase; + ValueId resultForEmptyGroup() const override { return Id::makeFromDouble(0); } +}; + +} // namespace detail + +using detail::StdevExpression; + +} // namespace sparqlExpression diff --git a/src/parser/sparqlParser/SparqlQleverVisitor.cpp b/src/parser/sparqlParser/SparqlQleverVisitor.cpp index c51d1cb1c9..f0438fc7f6 100644 --- a/src/parser/sparqlParser/SparqlQleverVisitor.cpp +++ b/src/parser/sparqlParser/SparqlQleverVisitor.cpp @@ -23,6 +23,7 @@ #include "engine/sparqlExpressions/RegexExpression.h" #include "engine/sparqlExpressions/RelationalExpressions.h" #include "engine/sparqlExpressions/SampleExpression.h" +#include "engine/sparqlExpressions/StdevExpression.h" #include "engine/sparqlExpressions/UuidExpressions.h" #include "parser/GraphPatternOperation.h" #include "parser/RdfParser.h" @@ -2372,6 +2373,8 @@ ExpressionPtr Visitor::visit(Parser::AggregateContext* ctx) { } return makePtr.operator()(std::move(separator)); + } else if (functionName == "stdev") { + return makePtr.operator()(); } else { AD_CORRECTNESS_CHECK(functionName == "sample"); return makePtr.operator()(); diff --git a/src/parser/sparqlParser/SparqlQleverVisitor.h b/src/parser/sparqlParser/SparqlQleverVisitor.h index 180f916c3b..724fa531e3 100644 --- a/src/parser/sparqlParser/SparqlQleverVisitor.h +++ b/src/parser/sparqlParser/SparqlQleverVisitor.h @@ -10,6 +10,7 @@ #include "engine/sparqlExpressions/AggregateExpression.h" #include "engine/sparqlExpressions/NaryExpression.h" +#include "engine/sparqlExpressions/StdevExpression.h" #include "parser/data/GraphRef.h" #undef EOF #include "parser/sparqlParser/generated/SparqlAutomaticVisitor.h" diff --git a/src/parser/sparqlParser/generated/SparqlAutomatic.g4 b/src/parser/sparqlParser/generated/SparqlAutomatic.g4 index 7199f36d22..f8b8c9206d 100644 --- a/src/parser/sparqlParser/generated/SparqlAutomatic.g4 +++ b/src/parser/sparqlParser/generated/SparqlAutomatic.g4 @@ -582,6 +582,7 @@ aggregate : COUNT '(' DISTINCT? ( '*' | expression ) ')' | MIN '(' DISTINCT? expression ')' | MAX '(' DISTINCT? expression ')' | AVG '(' DISTINCT? expression ')' + | STDEV '(' DISTINCT? expression ')' | SAMPLE '(' DISTINCT? expression ')' | GROUP_CONCAT '(' DISTINCT? expression ( ';' SEPARATOR '=' string )? ')' ; @@ -763,6 +764,7 @@ SUM : S U M; MIN : M I N; MAX : M A X; AVG : A V G; +STDEV : S T D E V ; SAMPLE : S A M P L E; SEPARATOR : S E P A R A T O R; diff --git a/src/parser/sparqlParser/generated/SparqlAutomatic.interp b/src/parser/sparqlParser/generated/SparqlAutomatic.interp index 28fbd0d13a..d9e3d98064 100644 --- a/src/parser/sparqlParser/generated/SparqlAutomatic.interp +++ b/src/parser/sparqlParser/generated/SparqlAutomatic.interp @@ -175,6 +175,7 @@ null null null null +null token symbolic names: null @@ -316,6 +317,7 @@ SUM MIN MAX AVG +STDEV SAMPLE SEPARATOR IRI_REF @@ -515,4 +517,4 @@ pnameNs atn: -[4, 1, 175, 1647, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7, 20, 2, 21, 7, 21, 2, 22, 7, 22, 2, 23, 7, 23, 2, 24, 7, 24, 2, 25, 7, 25, 2, 26, 7, 26, 2, 27, 7, 27, 2, 28, 7, 28, 2, 29, 7, 29, 2, 30, 7, 30, 2, 31, 7, 31, 2, 32, 7, 32, 2, 33, 7, 33, 2, 34, 7, 34, 2, 35, 7, 35, 2, 36, 7, 36, 2, 37, 7, 37, 2, 38, 7, 38, 2, 39, 7, 39, 2, 40, 7, 40, 2, 41, 7, 41, 2, 42, 7, 42, 2, 43, 7, 43, 2, 44, 7, 44, 2, 45, 7, 45, 2, 46, 7, 46, 2, 47, 7, 47, 2, 48, 7, 48, 2, 49, 7, 49, 2, 50, 7, 50, 2, 51, 7, 51, 2, 52, 7, 52, 2, 53, 7, 53, 2, 54, 7, 54, 2, 55, 7, 55, 2, 56, 7, 56, 2, 57, 7, 57, 2, 58, 7, 58, 2, 59, 7, 59, 2, 60, 7, 60, 2, 61, 7, 61, 2, 62, 7, 62, 2, 63, 7, 63, 2, 64, 7, 64, 2, 65, 7, 65, 2, 66, 7, 66, 2, 67, 7, 67, 2, 68, 7, 68, 2, 69, 7, 69, 2, 70, 7, 70, 2, 71, 7, 71, 2, 72, 7, 72, 2, 73, 7, 73, 2, 74, 7, 74, 2, 75, 7, 75, 2, 76, 7, 76, 2, 77, 7, 77, 2, 78, 7, 78, 2, 79, 7, 79, 2, 80, 7, 80, 2, 81, 7, 81, 2, 82, 7, 82, 2, 83, 7, 83, 2, 84, 7, 84, 2, 85, 7, 85, 2, 86, 7, 86, 2, 87, 7, 87, 2, 88, 7, 88, 2, 89, 7, 89, 2, 90, 7, 90, 2, 91, 7, 91, 2, 92, 7, 92, 2, 93, 7, 93, 2, 94, 7, 94, 2, 95, 7, 95, 2, 96, 7, 96, 2, 97, 7, 97, 2, 98, 7, 98, 2, 99, 7, 99, 2, 100, 7, 100, 2, 101, 7, 101, 2, 102, 7, 102, 2, 103, 7, 103, 2, 104, 7, 104, 2, 105, 7, 105, 2, 106, 7, 106, 2, 107, 7, 107, 2, 108, 7, 108, 2, 109, 7, 109, 2, 110, 7, 110, 2, 111, 7, 111, 2, 112, 7, 112, 2, 113, 7, 113, 2, 114, 7, 114, 2, 115, 7, 115, 2, 116, 7, 116, 2, 117, 7, 117, 2, 118, 7, 118, 2, 119, 7, 119, 2, 120, 7, 120, 2, 121, 7, 121, 2, 122, 7, 122, 2, 123, 7, 123, 2, 124, 7, 124, 2, 125, 7, 125, 2, 126, 7, 126, 2, 127, 7, 127, 2, 128, 7, 128, 2, 129, 7, 129, 2, 130, 7, 130, 2, 131, 7, 131, 2, 132, 7, 132, 2, 133, 7, 133, 2, 134, 7, 134, 2, 135, 7, 135, 2, 136, 7, 136, 2, 137, 7, 137, 2, 138, 7, 138, 2, 139, 7, 139, 2, 140, 7, 140, 2, 141, 7, 141, 2, 142, 7, 142, 2, 143, 7, 143, 2, 144, 7, 144, 2, 145, 7, 145, 2, 146, 7, 146, 2, 147, 7, 147, 2, 148, 7, 148, 2, 149, 7, 149, 2, 150, 7, 150, 2, 151, 7, 151, 2, 152, 7, 152, 2, 153, 7, 153, 2, 154, 7, 154, 2, 155, 7, 155, 2, 156, 7, 156, 1, 0, 1, 0, 3, 0, 317, 8, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 326, 8, 1, 1, 1, 1, 1, 1, 2, 1, 2, 5, 2, 332, 8, 2, 10, 2, 12, 2, 335, 9, 2, 1, 3, 1, 3, 1, 3, 1, 4, 1, 4, 1, 4, 1, 4, 1, 5, 1, 5, 5, 5, 346, 8, 5, 10, 5, 12, 5, 349, 9, 5, 1, 5, 1, 5, 1, 5, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 7, 1, 7, 3, 7, 361, 8, 7, 1, 7, 4, 7, 364, 8, 7, 11, 7, 12, 7, 365, 1, 7, 3, 7, 369, 8, 7, 1, 8, 1, 8, 3, 8, 373, 8, 8, 1, 9, 1, 9, 1, 9, 1, 9, 1, 10, 1, 10, 1, 10, 1, 10, 1, 11, 1, 11, 1, 11, 5, 11, 386, 8, 11, 10, 11, 12, 11, 389, 9, 11, 1, 11, 1, 11, 1, 11, 1, 11, 5, 11, 395, 8, 11, 10, 11, 12, 11, 398, 9, 11, 1, 11, 1, 11, 1, 11, 3, 11, 403, 8, 11, 1, 11, 1, 11, 3, 11, 407, 8, 11, 1, 12, 1, 12, 4, 12, 411, 8, 12, 11, 12, 12, 12, 412, 1, 12, 3, 12, 416, 8, 12, 1, 12, 5, 12, 419, 8, 12, 10, 12, 12, 12, 422, 9, 12, 1, 12, 3, 12, 425, 8, 12, 1, 12, 1, 12, 1, 13, 1, 13, 5, 13, 431, 8, 13, 10, 13, 12, 13, 434, 9, 13, 1, 13, 1, 13, 1, 13, 1, 14, 1, 14, 1, 14, 3, 14, 442, 8, 14, 1, 15, 1, 15, 1, 16, 1, 16, 1, 16, 1, 17, 1, 17, 1, 18, 3, 18, 452, 8, 18, 1, 18, 1, 18, 1, 19, 3, 19, 457, 8, 19, 1, 19, 3, 19, 460, 8, 19, 1, 19, 3, 19, 463, 8, 19, 1, 19, 3, 19, 466, 8, 19, 1, 20, 1, 20, 4, 20, 470, 8, 20, 11, 20, 12, 20, 471, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 3, 21, 480, 8, 21, 1, 21, 1, 21, 1, 21, 3, 21, 485, 8, 21, 1, 22, 1, 22, 4, 22, 489, 8, 22, 11, 22, 12, 22, 490, 1, 23, 1, 23, 1, 24, 1, 24, 3, 24, 497, 8, 24, 1, 24, 4, 24, 500, 8, 24, 11, 24, 12, 24, 501, 1, 25, 1, 25, 1, 25, 1, 25, 3, 25, 508, 8, 25, 3, 25, 510, 8, 25, 1, 26, 1, 26, 3, 26, 514, 8, 26, 1, 26, 3, 26, 517, 8, 26, 1, 26, 1, 26, 3, 26, 521, 8, 26, 1, 26, 3, 26, 524, 8, 26, 1, 26, 1, 26, 3, 26, 528, 8, 26, 1, 26, 3, 26, 531, 8, 26, 1, 26, 1, 26, 3, 26, 535, 8, 26, 1, 26, 3, 26, 538, 8, 26, 1, 26, 1, 26, 3, 26, 542, 8, 26, 1, 26, 3, 26, 545, 8, 26, 1, 26, 1, 26, 3, 26, 549, 8, 26, 1, 26, 3, 26, 552, 8, 26, 3, 26, 554, 8, 26, 1, 27, 1, 27, 1, 27, 1, 28, 1, 28, 1, 28, 1, 29, 1, 29, 1, 29, 1, 30, 1, 30, 3, 30, 567, 8, 30, 1, 31, 1, 31, 1, 31, 1, 31, 3, 31, 573, 8, 31, 3, 31, 575, 8, 31, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 3, 32, 588, 8, 32, 1, 33, 1, 33, 3, 33, 592, 8, 33, 1, 33, 1, 33, 1, 33, 3, 33, 597, 8, 33, 1, 34, 1, 34, 3, 34, 601, 8, 34, 1, 34, 1, 34, 1, 35, 1, 35, 3, 35, 607, 8, 35, 1, 35, 1, 35, 1, 36, 1, 36, 3, 36, 613, 8, 36, 1, 36, 1, 36, 1, 37, 1, 37, 3, 37, 619, 8, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 38, 1, 38, 3, 38, 627, 8, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 39, 1, 39, 3, 39, 635, 8, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 40, 1, 40, 1, 40, 1, 40, 1, 41, 1, 41, 1, 41, 1, 41, 1, 42, 1, 42, 1, 42, 1, 42, 1, 43, 1, 43, 3, 43, 655, 8, 43, 1, 43, 1, 43, 3, 43, 659, 8, 43, 1, 43, 3, 43, 662, 8, 43, 1, 43, 5, 43, 665, 8, 43, 10, 43, 12, 43, 668, 9, 43, 1, 43, 1, 43, 1, 43, 1, 44, 1, 44, 1, 44, 1, 45, 1, 45, 1, 45, 1, 46, 1, 46, 1, 46, 1, 46, 3, 46, 683, 8, 46, 1, 47, 1, 47, 1, 47, 3, 47, 688, 8, 47, 1, 48, 1, 48, 1, 48, 1, 49, 1, 49, 1, 49, 1, 49, 3, 49, 697, 8, 49, 1, 50, 1, 50, 1, 50, 1, 50, 1, 51, 1, 51, 1, 51, 1, 51, 1, 52, 3, 52, 708, 8, 52, 1, 52, 1, 52, 3, 52, 712, 8, 52, 1, 52, 3, 52, 715, 8, 52, 5, 52, 717, 8, 52, 10, 52, 12, 52, 720, 9, 52, 1, 53, 1, 53, 1, 53, 1, 53, 3, 53, 726, 8, 53, 1, 53, 1, 53, 1, 54, 1, 54, 1, 54, 3, 54, 733, 8, 54, 3, 54, 735, 8, 54, 1, 55, 1, 55, 1, 55, 3, 55, 740, 8, 55, 1, 55, 1, 55, 1, 56, 3, 56, 745, 8, 56, 1, 56, 5, 56, 748, 8, 56, 10, 56, 12, 56, 751, 9, 56, 1, 57, 1, 57, 3, 57, 755, 8, 57, 1, 57, 3, 57, 758, 8, 57, 1, 58, 1, 58, 1, 58, 3, 58, 763, 8, 58, 3, 58, 765, 8, 58, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 1, 59, 3, 59, 775, 8, 59, 1, 60, 1, 60, 1, 60, 1, 61, 1, 61, 1, 61, 1, 61, 1, 62, 1, 62, 3, 62, 786, 8, 62, 1, 62, 1, 62, 1, 62, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 63, 1, 64, 1, 64, 1, 64, 1, 65, 1, 65, 3, 65, 803, 8, 65, 1, 66, 1, 66, 1, 66, 5, 66, 808, 8, 66, 10, 66, 12, 66, 811, 9, 66, 1, 66, 1, 66, 1, 67, 1, 67, 1, 67, 5, 67, 818, 8, 67, 10, 67, 12, 67, 821, 9, 67, 1, 67, 3, 67, 824, 8, 67, 1, 67, 1, 67, 5, 67, 828, 8, 67, 10, 67, 12, 67, 831, 9, 67, 1, 67, 1, 67, 1, 68, 1, 68, 5, 68, 837, 8, 68, 10, 68, 12, 68, 840, 9, 68, 1, 68, 1, 68, 3, 68, 844, 8, 68, 1, 69, 1, 69, 1, 69, 1, 69, 1, 69, 3, 69, 851, 8, 69, 1, 70, 1, 70, 1, 70, 1, 71, 1, 71, 1, 71, 5, 71, 859, 8, 71, 10, 71, 12, 71, 862, 9, 71, 1, 72, 1, 72, 1, 72, 1, 73, 1, 73, 1, 73, 3, 73, 870, 8, 73, 1, 74, 1, 74, 1, 74, 1, 75, 1, 75, 1, 75, 3, 75, 878, 8, 75, 1, 75, 1, 75, 1, 75, 5, 75, 883, 8, 75, 10, 75, 12, 75, 886, 9, 75, 1, 75, 1, 75, 3, 75, 890, 8, 75, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 5, 76, 897, 8, 76, 10, 76, 12, 76, 900, 9, 76, 1, 76, 1, 76, 3, 76, 904, 8, 76, 1, 77, 1, 77, 3, 77, 908, 8, 77, 1, 77, 1, 77, 1, 78, 1, 78, 1, 78, 3, 78, 915, 8, 78, 3, 78, 917, 8, 78, 1, 79, 1, 79, 1, 79, 1, 79, 1, 79, 1, 79, 3, 79, 925, 8, 79, 1, 80, 3, 80, 928, 8, 80, 1, 81, 1, 81, 1, 81, 1, 81, 1, 81, 1, 81, 3, 81, 936, 8, 81, 5, 81, 938, 8, 81, 10, 81, 12, 81, 941, 9, 81, 1, 82, 1, 82, 3, 82, 945, 8, 82, 1, 83, 1, 83, 1, 83, 5, 83, 950, 8, 83, 10, 83, 12, 83, 953, 9, 83, 1, 84, 1, 84, 1, 85, 1, 85, 1, 85, 1, 85, 1, 85, 1, 85, 3, 85, 963, 8, 85, 1, 86, 3, 86, 966, 8, 86, 1, 87, 1, 87, 1, 87, 3, 87, 971, 8, 87, 5, 87, 973, 8, 87, 10, 87, 12, 87, 976, 9, 87, 1, 88, 1, 88, 1, 89, 1, 89, 1, 90, 1, 90, 1, 90, 1, 91, 1, 91, 1, 91, 1, 92, 1, 92, 3, 92, 990, 8, 92, 1, 93, 1, 93, 1, 93, 5, 93, 995, 8, 93, 10, 93, 12, 93, 998, 9, 93, 1, 94, 1, 94, 1, 95, 1, 95, 1, 96, 1, 96, 1, 96, 5, 96, 1007, 8, 96, 10, 96, 12, 96, 1010, 9, 96, 1, 97, 1, 97, 1, 97, 5, 97, 1015, 8, 97, 10, 97, 12, 97, 1018, 9, 97, 1, 98, 1, 98, 3, 98, 1022, 8, 98, 1, 99, 1, 99, 1, 99, 3, 99, 1027, 8, 99, 1, 100, 1, 100, 1, 101, 1, 101, 1, 101, 1, 101, 1, 101, 1, 101, 1, 101, 1, 101, 3, 101, 1039, 8, 101, 1, 102, 1, 102, 1, 102, 1, 102, 1, 102, 5, 102, 1046, 8, 102, 10, 102, 12, 102, 1049, 9, 102, 3, 102, 1051, 8, 102, 1, 102, 3, 102, 1054, 8, 102, 1, 103, 1, 103, 1, 103, 1, 103, 1, 103, 3, 103, 1061, 8, 103, 3, 103, 1063, 8, 103, 1, 104, 1, 104, 1, 105, 1, 105, 3, 105, 1069, 8, 105, 1, 106, 1, 106, 1, 106, 1, 106, 1, 107, 1, 107, 3, 107, 1077, 8, 107, 1, 108, 1, 108, 1, 108, 1, 108, 1, 109, 1, 109, 4, 109, 1085, 8, 109, 11, 109, 12, 109, 1086, 1, 109, 1, 109, 1, 110, 1, 110, 4, 110, 1093, 8, 110, 11, 110, 12, 110, 1094, 1, 110, 1, 110, 1, 111, 1, 111, 3, 111, 1101, 8, 111, 1, 112, 1, 112, 3, 112, 1105, 8, 112, 1, 113, 1, 113, 3, 113, 1109, 8, 113, 1, 114, 1, 114, 3, 114, 1113, 8, 114, 1, 115, 1, 115, 1, 116, 1, 116, 1, 116, 1, 116, 1, 116, 1, 116, 3, 116, 1123, 8, 116, 1, 117, 1, 117, 1, 118, 1, 118, 1, 118, 5, 118, 1130, 8, 118, 10, 118, 12, 118, 1133, 9, 118, 1, 119, 1, 119, 1, 119, 5, 119, 1138, 8, 119, 10, 119, 12, 119, 1141, 9, 119, 1, 120, 1, 120, 1, 121, 1, 121, 1, 121, 1, 121, 1, 121, 1, 121, 1, 121, 1, 121, 1, 121, 1, 121, 1, 121, 1, 121, 1, 121, 1, 121, 1, 121, 1, 121, 1, 121, 1, 121, 3, 121, 1163, 8, 121, 1, 122, 1, 122, 1, 123, 1, 123, 5, 123, 1169, 8, 123, 10, 123, 12, 123, 1172, 9, 123, 1, 124, 1, 124, 1, 124, 1, 124, 1, 124, 3, 124, 1179, 8, 124, 1, 125, 1, 125, 1, 126, 1, 126, 1, 127, 1, 127, 3, 127, 1187, 8, 127, 1, 127, 5, 127, 1190, 8, 127, 10, 127, 12, 127, 1193, 9, 127, 1, 128, 1, 128, 5, 128, 1197, 8, 128, 10, 128, 12, 128, 1200, 9, 128, 1, 129, 1, 129, 3, 129, 1204, 8, 129, 1, 130, 1, 130, 1, 130, 1, 131, 1, 131, 1, 131, 1, 132, 1, 132, 1, 132, 1, 132, 1, 132, 1, 132, 1, 132, 3, 132, 1219, 8, 132, 1, 133, 1, 133, 1, 133, 1, 133, 1, 133, 1, 133, 1, 133, 3, 133, 1228, 8, 133, 1, 134, 1, 134, 1, 134, 1, 134, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 3, 135, 1274, 8, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 1, 135, 3, 135, 1488, 8, 135, 1, 136, 1, 136, 1, 136, 1, 136, 1, 136, 1, 136, 1, 136, 3, 136, 1497, 8, 136, 1, 136, 1, 136, 1, 137, 1, 137, 1, 137, 1, 137, 1, 137, 1, 138, 1, 138, 1, 138, 1, 138, 1, 138, 1, 138, 1, 138, 3, 138, 1513, 8, 138, 1, 138, 1, 138, 1, 139, 1, 139, 1, 139, 1, 139, 1, 139, 1, 139, 1, 139, 1, 139, 1, 139, 3, 139, 1526, 8, 139, 1, 139, 1, 139, 1, 140, 1, 140, 1, 140, 1, 141, 1, 141, 1, 141, 1, 141, 1, 142, 1, 142, 1, 142, 3, 142, 1540, 8, 142, 1, 142, 1, 142, 3, 142, 1544, 8, 142, 1, 142, 1, 142, 1, 142, 1, 142, 3, 142, 1550, 8, 142, 1, 142, 1, 142, 1, 142, 1, 142, 1, 142, 1, 142, 3, 142, 1558, 8, 142, 1, 142, 1, 142, 1, 142, 1, 142, 1, 142, 1, 142, 3, 142, 1566, 8, 142, 1, 142, 1, 142, 1, 142, 1, 142, 1, 142, 1, 142, 3, 142, 1574, 8, 142, 1, 142, 1, 142, 1, 142, 1, 142, 1, 142, 1, 142, 3, 142, 1582, 8, 142, 1, 142, 1, 142, 1, 142, 1, 142, 1, 142, 1, 142, 3, 142, 1590, 8, 142, 1, 142, 1, 142, 1, 142, 1, 142, 1, 142, 3, 142, 1597, 8, 142, 1, 142, 1, 142, 3, 142, 1601, 8, 142, 1, 143, 1, 143, 3, 143, 1605, 8, 143, 1, 144, 1, 144, 1, 144, 1, 144, 3, 144, 1611, 8, 144, 1, 145, 1, 145, 1, 145, 3, 145, 1616, 8, 145, 1, 146, 1, 146, 1, 147, 1, 147, 1, 148, 1, 148, 1, 149, 1, 149, 1, 150, 1, 150, 1, 151, 3, 151, 1629, 8, 151, 1, 151, 1, 151, 3, 151, 1633, 8, 151, 1, 152, 1, 152, 3, 152, 1637, 8, 152, 1, 153, 1, 153, 1, 154, 1, 154, 1, 155, 1, 155, 1, 156, 1, 156, 1, 156, 0, 0, 157, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94, 96, 98, 100, 102, 104, 106, 108, 110, 112, 114, 116, 118, 120, 122, 124, 126, 128, 130, 132, 134, 136, 138, 140, 142, 144, 146, 148, 150, 152, 154, 156, 158, 160, 162, 164, 166, 168, 170, 172, 174, 176, 178, 180, 182, 184, 186, 188, 190, 192, 194, 196, 198, 200, 202, 204, 206, 208, 210, 212, 214, 216, 218, 220, 222, 224, 226, 228, 230, 232, 234, 236, 238, 240, 242, 244, 246, 248, 250, 252, 254, 256, 258, 260, 262, 264, 266, 268, 270, 272, 274, 276, 278, 280, 282, 284, 286, 288, 290, 292, 294, 296, 298, 300, 302, 304, 306, 308, 310, 312, 0, 10, 1, 0, 33, 34, 1, 0, 47, 48, 2, 0, 1, 1, 13, 14, 1, 0, 145, 146, 1, 0, 149, 151, 1, 0, 152, 154, 1, 0, 155, 157, 1, 0, 28, 29, 1, 0, 159, 162, 2, 0, 144, 144, 165, 165, 1761, 0, 316, 1, 0, 0, 0, 2, 320, 1, 0, 0, 0, 4, 333, 1, 0, 0, 0, 6, 336, 1, 0, 0, 0, 8, 339, 1, 0, 0, 0, 10, 343, 1, 0, 0, 0, 12, 353, 1, 0, 0, 0, 14, 358, 1, 0, 0, 0, 16, 372, 1, 0, 0, 0, 18, 374, 1, 0, 0, 0, 20, 378, 1, 0, 0, 0, 22, 382, 1, 0, 0, 0, 24, 408, 1, 0, 0, 0, 26, 428, 1, 0, 0, 0, 28, 438, 1, 0, 0, 0, 30, 443, 1, 0, 0, 0, 32, 445, 1, 0, 0, 0, 34, 448, 1, 0, 0, 0, 36, 451, 1, 0, 0, 0, 38, 456, 1, 0, 0, 0, 40, 467, 1, 0, 0, 0, 42, 484, 1, 0, 0, 0, 44, 486, 1, 0, 0, 0, 46, 492, 1, 0, 0, 0, 48, 496, 1, 0, 0, 0, 50, 509, 1, 0, 0, 0, 52, 553, 1, 0, 0, 0, 54, 555, 1, 0, 0, 0, 56, 558, 1, 0, 0, 0, 58, 561, 1, 0, 0, 0, 60, 566, 1, 0, 0, 0, 62, 568, 1, 0, 0, 0, 64, 587, 1, 0, 0, 0, 66, 589, 1, 0, 0, 0, 68, 598, 1, 0, 0, 0, 70, 604, 1, 0, 0, 0, 72, 610, 1, 0, 0, 0, 74, 616, 1, 0, 0, 0, 76, 624, 1, 0, 0, 0, 78, 632, 1, 0, 0, 0, 80, 640, 1, 0, 0, 0, 82, 644, 1, 0, 0, 0, 84, 648, 1, 0, 0, 0, 86, 654, 1, 0, 0, 0, 88, 672, 1, 0, 0, 0, 90, 675, 1, 0, 0, 0, 92, 678, 1, 0, 0, 0, 94, 687, 1, 0, 0, 0, 96, 689, 1, 0, 0, 0, 98, 696, 1, 0, 0, 0, 100, 698, 1, 0, 0, 0, 102, 702, 1, 0, 0, 0, 104, 707, 1, 0, 0, 0, 106, 721, 1, 0, 0, 0, 108, 729, 1, 0, 0, 0, 110, 736, 1, 0, 0, 0, 112, 744, 1, 0, 0, 0, 114, 752, 1, 0, 0, 0, 116, 759, 1, 0, 0, 0, 118, 774, 1, 0, 0, 0, 120, 776, 1, 0, 0, 0, 122, 779, 1, 0, 0, 0, 124, 783, 1, 0, 0, 0, 126, 790, 1, 0, 0, 0, 128, 797, 1, 0, 0, 0, 130, 802, 1, 0, 0, 0, 132, 804, 1, 0, 0, 0, 134, 823, 1, 0, 0, 0, 136, 843, 1, 0, 0, 0, 138, 850, 1, 0, 0, 0, 140, 852, 1, 0, 0, 0, 142, 855, 1, 0, 0, 0, 144, 863, 1, 0, 0, 0, 146, 869, 1, 0, 0, 0, 148, 871, 1, 0, 0, 0, 150, 889, 1, 0, 0, 0, 152, 903, 1, 0, 0, 0, 154, 905, 1, 0, 0, 0, 156, 911, 1, 0, 0, 0, 158, 924, 1, 0, 0, 0, 160, 927, 1, 0, 0, 0, 162, 929, 1, 0, 0, 0, 164, 944, 1, 0, 0, 0, 166, 946, 1, 0, 0, 0, 168, 954, 1, 0, 0, 0, 170, 962, 1, 0, 0, 0, 172, 965, 1, 0, 0, 0, 174, 967, 1, 0, 0, 0, 176, 977, 1, 0, 0, 0, 178, 979, 1, 0, 0, 0, 180, 981, 1, 0, 0, 0, 182, 984, 1, 0, 0, 0, 184, 989, 1, 0, 0, 0, 186, 991, 1, 0, 0, 0, 188, 999, 1, 0, 0, 0, 190, 1001, 1, 0, 0, 0, 192, 1003, 1, 0, 0, 0, 194, 1011, 1, 0, 0, 0, 196, 1019, 1, 0, 0, 0, 198, 1026, 1, 0, 0, 0, 200, 1028, 1, 0, 0, 0, 202, 1038, 1, 0, 0, 0, 204, 1053, 1, 0, 0, 0, 206, 1062, 1, 0, 0, 0, 208, 1064, 1, 0, 0, 0, 210, 1068, 1, 0, 0, 0, 212, 1070, 1, 0, 0, 0, 214, 1076, 1, 0, 0, 0, 216, 1078, 1, 0, 0, 0, 218, 1082, 1, 0, 0, 0, 220, 1090, 1, 0, 0, 0, 222, 1100, 1, 0, 0, 0, 224, 1104, 1, 0, 0, 0, 226, 1108, 1, 0, 0, 0, 228, 1112, 1, 0, 0, 0, 230, 1114, 1, 0, 0, 0, 232, 1122, 1, 0, 0, 0, 234, 1124, 1, 0, 0, 0, 236, 1126, 1, 0, 0, 0, 238, 1134, 1, 0, 0, 0, 240, 1142, 1, 0, 0, 0, 242, 1144, 1, 0, 0, 0, 244, 1164, 1, 0, 0, 0, 246, 1166, 1, 0, 0, 0, 248, 1178, 1, 0, 0, 0, 250, 1180, 1, 0, 0, 0, 252, 1182, 1, 0, 0, 0, 254, 1186, 1, 0, 0, 0, 256, 1194, 1, 0, 0, 0, 258, 1203, 1, 0, 0, 0, 260, 1205, 1, 0, 0, 0, 262, 1208, 1, 0, 0, 0, 264, 1218, 1, 0, 0, 0, 266, 1227, 1, 0, 0, 0, 268, 1229, 1, 0, 0, 0, 270, 1487, 1, 0, 0, 0, 272, 1489, 1, 0, 0, 0, 274, 1500, 1, 0, 0, 0, 276, 1505, 1, 0, 0, 0, 278, 1516, 1, 0, 0, 0, 280, 1529, 1, 0, 0, 0, 282, 1532, 1, 0, 0, 0, 284, 1600, 1, 0, 0, 0, 286, 1602, 1, 0, 0, 0, 288, 1606, 1, 0, 0, 0, 290, 1615, 1, 0, 0, 0, 292, 1617, 1, 0, 0, 0, 294, 1619, 1, 0, 0, 0, 296, 1621, 1, 0, 0, 0, 298, 1623, 1, 0, 0, 0, 300, 1625, 1, 0, 0, 0, 302, 1628, 1, 0, 0, 0, 304, 1636, 1, 0, 0, 0, 306, 1638, 1, 0, 0, 0, 308, 1640, 1, 0, 0, 0, 310, 1642, 1, 0, 0, 0, 312, 1644, 1, 0, 0, 0, 314, 317, 3, 2, 1, 0, 315, 317, 3, 62, 31, 0, 316, 314, 1, 0, 0, 0, 316, 315, 1, 0, 0, 0, 317, 318, 1, 0, 0, 0, 318, 319, 5, 0, 0, 1, 319, 1, 1, 0, 0, 0, 320, 325, 3, 4, 2, 0, 321, 326, 3, 10, 5, 0, 322, 326, 3, 22, 11, 0, 323, 326, 3, 24, 12, 0, 324, 326, 3, 26, 13, 0, 325, 321, 1, 0, 0, 0, 325, 322, 1, 0, 0, 0, 325, 323, 1, 0, 0, 0, 325, 324, 1, 0, 0, 0, 326, 327, 1, 0, 0, 0, 327, 328, 3, 60, 30, 0, 328, 3, 1, 0, 0, 0, 329, 332, 3, 6, 3, 0, 330, 332, 3, 8, 4, 0, 331, 329, 1, 0, 0, 0, 331, 330, 1, 0, 0, 0, 332, 335, 1, 0, 0, 0, 333, 331, 1, 0, 0, 0, 333, 334, 1, 0, 0, 0, 334, 5, 1, 0, 0, 0, 335, 333, 1, 0, 0, 0, 336, 337, 5, 30, 0, 0, 337, 338, 3, 308, 154, 0, 338, 7, 1, 0, 0, 0, 339, 340, 5, 31, 0, 0, 340, 341, 5, 142, 0, 0, 341, 342, 3, 308, 154, 0, 342, 9, 1, 0, 0, 0, 343, 347, 3, 14, 7, 0, 344, 346, 3, 28, 14, 0, 345, 344, 1, 0, 0, 0, 346, 349, 1, 0, 0, 0, 347, 345, 1, 0, 0, 0, 347, 348, 1, 0, 0, 0, 348, 350, 1, 0, 0, 0, 349, 347, 1, 0, 0, 0, 350, 351, 3, 36, 18, 0, 351, 352, 3, 38, 19, 0, 352, 11, 1, 0, 0, 0, 353, 354, 3, 14, 7, 0, 354, 355, 3, 36, 18, 0, 355, 356, 3, 38, 19, 0, 356, 357, 3, 60, 30, 0, 357, 13, 1, 0, 0, 0, 358, 360, 5, 32, 0, 0, 359, 361, 7, 0, 0, 0, 360, 359, 1, 0, 0, 0, 360, 361, 1, 0, 0, 0, 361, 368, 1, 0, 0, 0, 362, 364, 3, 16, 8, 0, 363, 362, 1, 0, 0, 0, 364, 365, 1, 0, 0, 0, 365, 363, 1, 0, 0, 0, 365, 366, 1, 0, 0, 0, 366, 369, 1, 0, 0, 0, 367, 369, 5, 1, 0, 0, 368, 363, 1, 0, 0, 0, 368, 367, 1, 0, 0, 0, 369, 15, 1, 0, 0, 0, 370, 373, 3, 230, 115, 0, 371, 373, 3, 18, 9, 0, 372, 370, 1, 0, 0, 0, 372, 371, 1, 0, 0, 0, 373, 17, 1, 0, 0, 0, 374, 375, 5, 2, 0, 0, 375, 376, 3, 20, 10, 0, 376, 377, 5, 3, 0, 0, 377, 19, 1, 0, 0, 0, 378, 379, 3, 234, 117, 0, 379, 380, 5, 35, 0, 0, 380, 381, 3, 230, 115, 0, 381, 21, 1, 0, 0, 0, 382, 406, 5, 36, 0, 0, 383, 387, 3, 154, 77, 0, 384, 386, 3, 28, 14, 0, 385, 384, 1, 0, 0, 0, 386, 389, 1, 0, 0, 0, 387, 385, 1, 0, 0, 0, 387, 388, 1, 0, 0, 0, 388, 390, 1, 0, 0, 0, 389, 387, 1, 0, 0, 0, 390, 391, 3, 36, 18, 0, 391, 392, 3, 38, 19, 0, 392, 407, 1, 0, 0, 0, 393, 395, 3, 28, 14, 0, 394, 393, 1, 0, 0, 0, 395, 398, 1, 0, 0, 0, 396, 394, 1, 0, 0, 0, 396, 397, 1, 0, 0, 0, 397, 399, 1, 0, 0, 0, 398, 396, 1, 0, 0, 0, 399, 400, 5, 37, 0, 0, 400, 402, 5, 4, 0, 0, 401, 403, 3, 108, 54, 0, 402, 401, 1, 0, 0, 0, 402, 403, 1, 0, 0, 0, 403, 404, 1, 0, 0, 0, 404, 405, 5, 5, 0, 0, 405, 407, 3, 38, 19, 0, 406, 383, 1, 0, 0, 0, 406, 396, 1, 0, 0, 0, 407, 23, 1, 0, 0, 0, 408, 415, 5, 38, 0, 0, 409, 411, 3, 228, 114, 0, 410, 409, 1, 0, 0, 0, 411, 412, 1, 0, 0, 0, 412, 410, 1, 0, 0, 0, 412, 413, 1, 0, 0, 0, 413, 416, 1, 0, 0, 0, 414, 416, 5, 1, 0, 0, 415, 410, 1, 0, 0, 0, 415, 414, 1, 0, 0, 0, 416, 420, 1, 0, 0, 0, 417, 419, 3, 28, 14, 0, 418, 417, 1, 0, 0, 0, 419, 422, 1, 0, 0, 0, 420, 418, 1, 0, 0, 0, 420, 421, 1, 0, 0, 0, 421, 424, 1, 0, 0, 0, 422, 420, 1, 0, 0, 0, 423, 425, 3, 36, 18, 0, 424, 423, 1, 0, 0, 0, 424, 425, 1, 0, 0, 0, 425, 426, 1, 0, 0, 0, 426, 427, 3, 38, 19, 0, 427, 25, 1, 0, 0, 0, 428, 432, 5, 39, 0, 0, 429, 431, 3, 28, 14, 0, 430, 429, 1, 0, 0, 0, 431, 434, 1, 0, 0, 0, 432, 430, 1, 0, 0, 0, 432, 433, 1, 0, 0, 0, 433, 435, 1, 0, 0, 0, 434, 432, 1, 0, 0, 0, 435, 436, 3, 36, 18, 0, 436, 437, 3, 38, 19, 0, 437, 27, 1, 0, 0, 0, 438, 441, 5, 40, 0, 0, 439, 442, 3, 30, 15, 0, 440, 442, 3, 32, 16, 0, 441, 439, 1, 0, 0, 0, 441, 440, 1, 0, 0, 0, 442, 29, 1, 0, 0, 0, 443, 444, 3, 34, 17, 0, 444, 31, 1, 0, 0, 0, 445, 446, 5, 41, 0, 0, 446, 447, 3, 34, 17, 0, 447, 33, 1, 0, 0, 0, 448, 449, 3, 302, 151, 0, 449, 35, 1, 0, 0, 0, 450, 452, 5, 37, 0, 0, 451, 450, 1, 0, 0, 0, 451, 452, 1, 0, 0, 0, 452, 453, 1, 0, 0, 0, 453, 454, 3, 110, 55, 0, 454, 37, 1, 0, 0, 0, 455, 457, 3, 40, 20, 0, 456, 455, 1, 0, 0, 0, 456, 457, 1, 0, 0, 0, 457, 459, 1, 0, 0, 0, 458, 460, 3, 44, 22, 0, 459, 458, 1, 0, 0, 0, 459, 460, 1, 0, 0, 0, 460, 462, 1, 0, 0, 0, 461, 463, 3, 48, 24, 0, 462, 461, 1, 0, 0, 0, 462, 463, 1, 0, 0, 0, 463, 465, 1, 0, 0, 0, 464, 466, 3, 52, 26, 0, 465, 464, 1, 0, 0, 0, 465, 466, 1, 0, 0, 0, 466, 39, 1, 0, 0, 0, 467, 469, 5, 42, 0, 0, 468, 470, 3, 42, 21, 0, 469, 468, 1, 0, 0, 0, 470, 471, 1, 0, 0, 0, 471, 469, 1, 0, 0, 0, 471, 472, 1, 0, 0, 0, 472, 41, 1, 0, 0, 0, 473, 485, 3, 270, 135, 0, 474, 485, 3, 148, 74, 0, 475, 476, 5, 2, 0, 0, 476, 479, 3, 234, 117, 0, 477, 478, 5, 35, 0, 0, 478, 480, 3, 230, 115, 0, 479, 477, 1, 0, 0, 0, 479, 480, 1, 0, 0, 0, 480, 481, 1, 0, 0, 0, 481, 482, 5, 3, 0, 0, 482, 485, 1, 0, 0, 0, 483, 485, 3, 230, 115, 0, 484, 473, 1, 0, 0, 0, 484, 474, 1, 0, 0, 0, 484, 475, 1, 0, 0, 0, 484, 483, 1, 0, 0, 0, 485, 43, 1, 0, 0, 0, 486, 488, 5, 44, 0, 0, 487, 489, 3, 46, 23, 0, 488, 487, 1, 0, 0, 0, 489, 490, 1, 0, 0, 0, 490, 488, 1, 0, 0, 0, 490, 491, 1, 0, 0, 0, 491, 45, 1, 0, 0, 0, 492, 493, 3, 146, 73, 0, 493, 47, 1, 0, 0, 0, 494, 497, 5, 45, 0, 0, 495, 497, 5, 46, 0, 0, 496, 494, 1, 0, 0, 0, 496, 495, 1, 0, 0, 0, 497, 499, 1, 0, 0, 0, 498, 500, 3, 50, 25, 0, 499, 498, 1, 0, 0, 0, 500, 501, 1, 0, 0, 0, 501, 499, 1, 0, 0, 0, 501, 502, 1, 0, 0, 0, 502, 49, 1, 0, 0, 0, 503, 504, 7, 1, 0, 0, 504, 510, 3, 268, 134, 0, 505, 508, 3, 146, 73, 0, 506, 508, 3, 230, 115, 0, 507, 505, 1, 0, 0, 0, 507, 506, 1, 0, 0, 0, 508, 510, 1, 0, 0, 0, 509, 503, 1, 0, 0, 0, 509, 507, 1, 0, 0, 0, 510, 51, 1, 0, 0, 0, 511, 513, 3, 54, 27, 0, 512, 514, 3, 56, 28, 0, 513, 512, 1, 0, 0, 0, 513, 514, 1, 0, 0, 0, 514, 516, 1, 0, 0, 0, 515, 517, 3, 58, 29, 0, 516, 515, 1, 0, 0, 0, 516, 517, 1, 0, 0, 0, 517, 554, 1, 0, 0, 0, 518, 520, 3, 54, 27, 0, 519, 521, 3, 58, 29, 0, 520, 519, 1, 0, 0, 0, 520, 521, 1, 0, 0, 0, 521, 523, 1, 0, 0, 0, 522, 524, 3, 56, 28, 0, 523, 522, 1, 0, 0, 0, 523, 524, 1, 0, 0, 0, 524, 554, 1, 0, 0, 0, 525, 527, 3, 56, 28, 0, 526, 528, 3, 54, 27, 0, 527, 526, 1, 0, 0, 0, 527, 528, 1, 0, 0, 0, 528, 530, 1, 0, 0, 0, 529, 531, 3, 58, 29, 0, 530, 529, 1, 0, 0, 0, 530, 531, 1, 0, 0, 0, 531, 554, 1, 0, 0, 0, 532, 534, 3, 56, 28, 0, 533, 535, 3, 58, 29, 0, 534, 533, 1, 0, 0, 0, 534, 535, 1, 0, 0, 0, 535, 537, 1, 0, 0, 0, 536, 538, 3, 54, 27, 0, 537, 536, 1, 0, 0, 0, 537, 538, 1, 0, 0, 0, 538, 554, 1, 0, 0, 0, 539, 541, 3, 58, 29, 0, 540, 542, 3, 56, 28, 0, 541, 540, 1, 0, 0, 0, 541, 542, 1, 0, 0, 0, 542, 544, 1, 0, 0, 0, 543, 545, 3, 54, 27, 0, 544, 543, 1, 0, 0, 0, 544, 545, 1, 0, 0, 0, 545, 554, 1, 0, 0, 0, 546, 548, 3, 58, 29, 0, 547, 549, 3, 54, 27, 0, 548, 547, 1, 0, 0, 0, 548, 549, 1, 0, 0, 0, 549, 551, 1, 0, 0, 0, 550, 552, 3, 56, 28, 0, 551, 550, 1, 0, 0, 0, 551, 552, 1, 0, 0, 0, 552, 554, 1, 0, 0, 0, 553, 511, 1, 0, 0, 0, 553, 518, 1, 0, 0, 0, 553, 525, 1, 0, 0, 0, 553, 532, 1, 0, 0, 0, 553, 539, 1, 0, 0, 0, 553, 546, 1, 0, 0, 0, 554, 53, 1, 0, 0, 0, 555, 556, 5, 49, 0, 0, 556, 557, 3, 208, 104, 0, 557, 55, 1, 0, 0, 0, 558, 559, 5, 50, 0, 0, 559, 560, 3, 208, 104, 0, 560, 57, 1, 0, 0, 0, 561, 562, 5, 51, 0, 0, 562, 563, 3, 208, 104, 0, 563, 59, 1, 0, 0, 0, 564, 565, 5, 52, 0, 0, 565, 567, 3, 130, 65, 0, 566, 564, 1, 0, 0, 0, 566, 567, 1, 0, 0, 0, 567, 61, 1, 0, 0, 0, 568, 574, 3, 4, 2, 0, 569, 572, 3, 64, 32, 0, 570, 571, 5, 6, 0, 0, 571, 573, 3, 62, 31, 0, 572, 570, 1, 0, 0, 0, 572, 573, 1, 0, 0, 0, 573, 575, 1, 0, 0, 0, 574, 569, 1, 0, 0, 0, 574, 575, 1, 0, 0, 0, 575, 63, 1, 0, 0, 0, 576, 588, 3, 66, 33, 0, 577, 588, 3, 68, 34, 0, 578, 588, 3, 70, 35, 0, 579, 588, 3, 74, 37, 0, 580, 588, 3, 76, 38, 0, 581, 588, 3, 78, 39, 0, 582, 588, 3, 72, 36, 0, 583, 588, 3, 80, 40, 0, 584, 588, 3, 82, 41, 0, 585, 588, 3, 84, 42, 0, 586, 588, 3, 86, 43, 0, 587, 576, 1, 0, 0, 0, 587, 577, 1, 0, 0, 0, 587, 578, 1, 0, 0, 0, 587, 579, 1, 0, 0, 0, 587, 580, 1, 0, 0, 0, 587, 581, 1, 0, 0, 0, 587, 582, 1, 0, 0, 0, 587, 583, 1, 0, 0, 0, 587, 584, 1, 0, 0, 0, 587, 585, 1, 0, 0, 0, 587, 586, 1, 0, 0, 0, 588, 65, 1, 0, 0, 0, 589, 591, 5, 53, 0, 0, 590, 592, 5, 54, 0, 0, 591, 590, 1, 0, 0, 0, 591, 592, 1, 0, 0, 0, 592, 593, 1, 0, 0, 0, 593, 596, 3, 302, 151, 0, 594, 595, 5, 55, 0, 0, 595, 597, 3, 96, 48, 0, 596, 594, 1, 0, 0, 0, 596, 597, 1, 0, 0, 0, 597, 67, 1, 0, 0, 0, 598, 600, 5, 56, 0, 0, 599, 601, 5, 54, 0, 0, 600, 599, 1, 0, 0, 0, 600, 601, 1, 0, 0, 0, 601, 602, 1, 0, 0, 0, 602, 603, 3, 98, 49, 0, 603, 69, 1, 0, 0, 0, 604, 606, 5, 57, 0, 0, 605, 607, 5, 54, 0, 0, 606, 605, 1, 0, 0, 0, 606, 607, 1, 0, 0, 0, 607, 608, 1, 0, 0, 0, 608, 609, 3, 98, 49, 0, 609, 71, 1, 0, 0, 0, 610, 612, 5, 58, 0, 0, 611, 613, 5, 54, 0, 0, 612, 611, 1, 0, 0, 0, 612, 613, 1, 0, 0, 0, 613, 614, 1, 0, 0, 0, 614, 615, 3, 96, 48, 0, 615, 73, 1, 0, 0, 0, 616, 618, 5, 59, 0, 0, 617, 619, 5, 54, 0, 0, 618, 617, 1, 0, 0, 0, 618, 619, 1, 0, 0, 0, 619, 620, 1, 0, 0, 0, 620, 621, 3, 94, 47, 0, 621, 622, 5, 60, 0, 0, 622, 623, 3, 94, 47, 0, 623, 75, 1, 0, 0, 0, 624, 626, 5, 62, 0, 0, 625, 627, 5, 54, 0, 0, 626, 625, 1, 0, 0, 0, 626, 627, 1, 0, 0, 0, 627, 628, 1, 0, 0, 0, 628, 629, 3, 94, 47, 0, 629, 630, 5, 60, 0, 0, 630, 631, 3, 94, 47, 0, 631, 77, 1, 0, 0, 0, 632, 634, 5, 63, 0, 0, 633, 635, 5, 54, 0, 0, 634, 633, 1, 0, 0, 0, 634, 635, 1, 0, 0, 0, 635, 636, 1, 0, 0, 0, 636, 637, 3, 94, 47, 0, 637, 638, 5, 60, 0, 0, 638, 639, 3, 94, 47, 0, 639, 79, 1, 0, 0, 0, 640, 641, 5, 64, 0, 0, 641, 642, 5, 61, 0, 0, 642, 643, 3, 102, 51, 0, 643, 81, 1, 0, 0, 0, 644, 645, 5, 65, 0, 0, 645, 646, 5, 61, 0, 0, 646, 647, 3, 102, 51, 0, 647, 83, 1, 0, 0, 0, 648, 649, 5, 65, 0, 0, 649, 650, 5, 37, 0, 0, 650, 651, 3, 100, 50, 0, 651, 85, 1, 0, 0, 0, 652, 653, 5, 66, 0, 0, 653, 655, 3, 302, 151, 0, 654, 652, 1, 0, 0, 0, 654, 655, 1, 0, 0, 0, 655, 661, 1, 0, 0, 0, 656, 658, 3, 88, 44, 0, 657, 659, 3, 90, 45, 0, 658, 657, 1, 0, 0, 0, 658, 659, 1, 0, 0, 0, 659, 662, 1, 0, 0, 0, 660, 662, 3, 90, 45, 0, 661, 656, 1, 0, 0, 0, 661, 660, 1, 0, 0, 0, 662, 666, 1, 0, 0, 0, 663, 665, 3, 92, 46, 0, 664, 663, 1, 0, 0, 0, 665, 668, 1, 0, 0, 0, 666, 664, 1, 0, 0, 0, 666, 667, 1, 0, 0, 0, 667, 669, 1, 0, 0, 0, 668, 666, 1, 0, 0, 0, 669, 670, 5, 37, 0, 0, 670, 671, 3, 110, 55, 0, 671, 87, 1, 0, 0, 0, 672, 673, 5, 65, 0, 0, 673, 674, 3, 100, 50, 0, 674, 89, 1, 0, 0, 0, 675, 676, 5, 64, 0, 0, 676, 677, 3, 100, 50, 0, 677, 91, 1, 0, 0, 0, 678, 682, 5, 67, 0, 0, 679, 683, 3, 302, 151, 0, 680, 681, 5, 41, 0, 0, 681, 683, 3, 302, 151, 0, 682, 679, 1, 0, 0, 0, 682, 680, 1, 0, 0, 0, 683, 93, 1, 0, 0, 0, 684, 688, 5, 68, 0, 0, 685, 686, 5, 69, 0, 0, 686, 688, 3, 302, 151, 0, 687, 684, 1, 0, 0, 0, 687, 685, 1, 0, 0, 0, 688, 95, 1, 0, 0, 0, 689, 690, 5, 69, 0, 0, 690, 691, 3, 302, 151, 0, 691, 97, 1, 0, 0, 0, 692, 697, 3, 96, 48, 0, 693, 697, 5, 68, 0, 0, 694, 697, 5, 41, 0, 0, 695, 697, 5, 70, 0, 0, 696, 692, 1, 0, 0, 0, 696, 693, 1, 0, 0, 0, 696, 694, 1, 0, 0, 0, 696, 695, 1, 0, 0, 0, 697, 99, 1, 0, 0, 0, 698, 699, 5, 4, 0, 0, 699, 700, 3, 104, 52, 0, 700, 701, 5, 5, 0, 0, 701, 101, 1, 0, 0, 0, 702, 703, 5, 4, 0, 0, 703, 704, 3, 104, 52, 0, 704, 705, 5, 5, 0, 0, 705, 103, 1, 0, 0, 0, 706, 708, 3, 108, 54, 0, 707, 706, 1, 0, 0, 0, 707, 708, 1, 0, 0, 0, 708, 718, 1, 0, 0, 0, 709, 711, 3, 106, 53, 0, 710, 712, 5, 7, 0, 0, 711, 710, 1, 0, 0, 0, 711, 712, 1, 0, 0, 0, 712, 714, 1, 0, 0, 0, 713, 715, 3, 108, 54, 0, 714, 713, 1, 0, 0, 0, 714, 715, 1, 0, 0, 0, 715, 717, 1, 0, 0, 0, 716, 709, 1, 0, 0, 0, 717, 720, 1, 0, 0, 0, 718, 716, 1, 0, 0, 0, 718, 719, 1, 0, 0, 0, 719, 105, 1, 0, 0, 0, 720, 718, 1, 0, 0, 0, 721, 722, 5, 69, 0, 0, 722, 723, 3, 228, 114, 0, 723, 725, 5, 4, 0, 0, 724, 726, 3, 108, 54, 0, 725, 724, 1, 0, 0, 0, 725, 726, 1, 0, 0, 0, 726, 727, 1, 0, 0, 0, 727, 728, 5, 5, 0, 0, 728, 107, 1, 0, 0, 0, 729, 734, 3, 158, 79, 0, 730, 732, 5, 7, 0, 0, 731, 733, 3, 108, 54, 0, 732, 731, 1, 0, 0, 0, 732, 733, 1, 0, 0, 0, 733, 735, 1, 0, 0, 0, 734, 730, 1, 0, 0, 0, 734, 735, 1, 0, 0, 0, 735, 109, 1, 0, 0, 0, 736, 739, 5, 4, 0, 0, 737, 740, 3, 12, 6, 0, 738, 740, 3, 112, 56, 0, 739, 737, 1, 0, 0, 0, 739, 738, 1, 0, 0, 0, 740, 741, 1, 0, 0, 0, 741, 742, 5, 5, 0, 0, 742, 111, 1, 0, 0, 0, 743, 745, 3, 116, 58, 0, 744, 743, 1, 0, 0, 0, 744, 745, 1, 0, 0, 0, 745, 749, 1, 0, 0, 0, 746, 748, 3, 114, 57, 0, 747, 746, 1, 0, 0, 0, 748, 751, 1, 0, 0, 0, 749, 747, 1, 0, 0, 0, 749, 750, 1, 0, 0, 0, 750, 113, 1, 0, 0, 0, 751, 749, 1, 0, 0, 0, 752, 754, 3, 118, 59, 0, 753, 755, 5, 7, 0, 0, 754, 753, 1, 0, 0, 0, 754, 755, 1, 0, 0, 0, 755, 757, 1, 0, 0, 0, 756, 758, 3, 116, 58, 0, 757, 756, 1, 0, 0, 0, 757, 758, 1, 0, 0, 0, 758, 115, 1, 0, 0, 0, 759, 764, 3, 170, 85, 0, 760, 762, 5, 7, 0, 0, 761, 763, 3, 116, 58, 0, 762, 761, 1, 0, 0, 0, 762, 763, 1, 0, 0, 0, 763, 765, 1, 0, 0, 0, 764, 760, 1, 0, 0, 0, 764, 765, 1, 0, 0, 0, 765, 117, 1, 0, 0, 0, 766, 775, 3, 142, 71, 0, 767, 775, 3, 120, 60, 0, 768, 775, 3, 140, 70, 0, 769, 775, 3, 122, 61, 0, 770, 775, 3, 124, 62, 0, 771, 775, 3, 144, 72, 0, 772, 775, 3, 126, 63, 0, 773, 775, 3, 128, 64, 0, 774, 766, 1, 0, 0, 0, 774, 767, 1, 0, 0, 0, 774, 768, 1, 0, 0, 0, 774, 769, 1, 0, 0, 0, 774, 770, 1, 0, 0, 0, 774, 771, 1, 0, 0, 0, 774, 772, 1, 0, 0, 0, 774, 773, 1, 0, 0, 0, 775, 119, 1, 0, 0, 0, 776, 777, 5, 71, 0, 0, 777, 778, 3, 110, 55, 0, 778, 121, 1, 0, 0, 0, 779, 780, 5, 69, 0, 0, 780, 781, 3, 228, 114, 0, 781, 782, 3, 110, 55, 0, 782, 123, 1, 0, 0, 0, 783, 785, 5, 72, 0, 0, 784, 786, 5, 54, 0, 0, 785, 784, 1, 0, 0, 0, 785, 786, 1, 0, 0, 0, 786, 787, 1, 0, 0, 0, 787, 788, 3, 228, 114, 0, 788, 789, 3, 110, 55, 0, 789, 125, 1, 0, 0, 0, 790, 791, 5, 73, 0, 0, 791, 792, 5, 2, 0, 0, 792, 793, 3, 234, 117, 0, 793, 794, 5, 35, 0, 0, 794, 795, 3, 230, 115, 0, 795, 796, 5, 3, 0, 0, 796, 127, 1, 0, 0, 0, 797, 798, 5, 52, 0, 0, 798, 799, 3, 130, 65, 0, 799, 129, 1, 0, 0, 0, 800, 803, 3, 132, 66, 0, 801, 803, 3, 134, 67, 0, 802, 800, 1, 0, 0, 0, 802, 801, 1, 0, 0, 0, 803, 131, 1, 0, 0, 0, 804, 805, 3, 230, 115, 0, 805, 809, 5, 4, 0, 0, 806, 808, 3, 138, 69, 0, 807, 806, 1, 0, 0, 0, 808, 811, 1, 0, 0, 0, 809, 807, 1, 0, 0, 0, 809, 810, 1, 0, 0, 0, 810, 812, 1, 0, 0, 0, 811, 809, 1, 0, 0, 0, 812, 813, 5, 5, 0, 0, 813, 133, 1, 0, 0, 0, 814, 824, 5, 164, 0, 0, 815, 819, 5, 2, 0, 0, 816, 818, 3, 230, 115, 0, 817, 816, 1, 0, 0, 0, 818, 821, 1, 0, 0, 0, 819, 817, 1, 0, 0, 0, 819, 820, 1, 0, 0, 0, 820, 822, 1, 0, 0, 0, 821, 819, 1, 0, 0, 0, 822, 824, 5, 3, 0, 0, 823, 814, 1, 0, 0, 0, 823, 815, 1, 0, 0, 0, 824, 825, 1, 0, 0, 0, 825, 829, 5, 4, 0, 0, 826, 828, 3, 136, 68, 0, 827, 826, 1, 0, 0, 0, 828, 831, 1, 0, 0, 0, 829, 827, 1, 0, 0, 0, 829, 830, 1, 0, 0, 0, 830, 832, 1, 0, 0, 0, 831, 829, 1, 0, 0, 0, 832, 833, 5, 5, 0, 0, 833, 135, 1, 0, 0, 0, 834, 838, 5, 2, 0, 0, 835, 837, 3, 138, 69, 0, 836, 835, 1, 0, 0, 0, 837, 840, 1, 0, 0, 0, 838, 836, 1, 0, 0, 0, 838, 839, 1, 0, 0, 0, 839, 841, 1, 0, 0, 0, 840, 838, 1, 0, 0, 0, 841, 844, 5, 3, 0, 0, 842, 844, 5, 164, 0, 0, 843, 834, 1, 0, 0, 0, 843, 842, 1, 0, 0, 0, 844, 137, 1, 0, 0, 0, 845, 851, 3, 302, 151, 0, 846, 851, 3, 288, 144, 0, 847, 851, 3, 290, 145, 0, 848, 851, 3, 298, 149, 0, 849, 851, 5, 74, 0, 0, 850, 845, 1, 0, 0, 0, 850, 846, 1, 0, 0, 0, 850, 847, 1, 0, 0, 0, 850, 848, 1, 0, 0, 0, 850, 849, 1, 0, 0, 0, 851, 139, 1, 0, 0, 0, 852, 853, 5, 75, 0, 0, 853, 854, 3, 110, 55, 0, 854, 141, 1, 0, 0, 0, 855, 860, 3, 110, 55, 0, 856, 857, 5, 76, 0, 0, 857, 859, 3, 110, 55, 0, 858, 856, 1, 0, 0, 0, 859, 862, 1, 0, 0, 0, 860, 858, 1, 0, 0, 0, 860, 861, 1, 0, 0, 0, 861, 143, 1, 0, 0, 0, 862, 860, 1, 0, 0, 0, 863, 864, 5, 77, 0, 0, 864, 865, 3, 146, 73, 0, 865, 145, 1, 0, 0, 0, 866, 870, 3, 268, 134, 0, 867, 870, 3, 270, 135, 0, 868, 870, 3, 148, 74, 0, 869, 866, 1, 0, 0, 0, 869, 867, 1, 0, 0, 0, 869, 868, 1, 0, 0, 0, 870, 147, 1, 0, 0, 0, 871, 872, 3, 302, 151, 0, 872, 873, 3, 150, 75, 0, 873, 149, 1, 0, 0, 0, 874, 890, 5, 164, 0, 0, 875, 877, 5, 2, 0, 0, 876, 878, 5, 33, 0, 0, 877, 876, 1, 0, 0, 0, 877, 878, 1, 0, 0, 0, 878, 879, 1, 0, 0, 0, 879, 884, 3, 234, 117, 0, 880, 881, 5, 8, 0, 0, 881, 883, 3, 234, 117, 0, 882, 880, 1, 0, 0, 0, 883, 886, 1, 0, 0, 0, 884, 882, 1, 0, 0, 0, 884, 885, 1, 0, 0, 0, 885, 887, 1, 0, 0, 0, 886, 884, 1, 0, 0, 0, 887, 888, 5, 3, 0, 0, 888, 890, 1, 0, 0, 0, 889, 874, 1, 0, 0, 0, 889, 875, 1, 0, 0, 0, 890, 151, 1, 0, 0, 0, 891, 904, 5, 164, 0, 0, 892, 893, 5, 2, 0, 0, 893, 898, 3, 234, 117, 0, 894, 895, 5, 8, 0, 0, 895, 897, 3, 234, 117, 0, 896, 894, 1, 0, 0, 0, 897, 900, 1, 0, 0, 0, 898, 896, 1, 0, 0, 0, 898, 899, 1, 0, 0, 0, 899, 901, 1, 0, 0, 0, 900, 898, 1, 0, 0, 0, 901, 902, 5, 3, 0, 0, 902, 904, 1, 0, 0, 0, 903, 891, 1, 0, 0, 0, 903, 892, 1, 0, 0, 0, 904, 153, 1, 0, 0, 0, 905, 907, 5, 4, 0, 0, 906, 908, 3, 156, 78, 0, 907, 906, 1, 0, 0, 0, 907, 908, 1, 0, 0, 0, 908, 909, 1, 0, 0, 0, 909, 910, 5, 5, 0, 0, 910, 155, 1, 0, 0, 0, 911, 916, 3, 158, 79, 0, 912, 914, 5, 7, 0, 0, 913, 915, 3, 156, 78, 0, 914, 913, 1, 0, 0, 0, 914, 915, 1, 0, 0, 0, 915, 917, 1, 0, 0, 0, 916, 912, 1, 0, 0, 0, 916, 917, 1, 0, 0, 0, 917, 157, 1, 0, 0, 0, 918, 919, 3, 226, 113, 0, 919, 920, 3, 162, 81, 0, 920, 925, 1, 0, 0, 0, 921, 922, 3, 210, 105, 0, 922, 923, 3, 160, 80, 0, 923, 925, 1, 0, 0, 0, 924, 918, 1, 0, 0, 0, 924, 921, 1, 0, 0, 0, 925, 159, 1, 0, 0, 0, 926, 928, 3, 162, 81, 0, 927, 926, 1, 0, 0, 0, 927, 928, 1, 0, 0, 0, 928, 161, 1, 0, 0, 0, 929, 930, 3, 164, 82, 0, 930, 939, 3, 166, 83, 0, 931, 935, 5, 6, 0, 0, 932, 933, 3, 164, 82, 0, 933, 934, 3, 166, 83, 0, 934, 936, 1, 0, 0, 0, 935, 932, 1, 0, 0, 0, 935, 936, 1, 0, 0, 0, 936, 938, 1, 0, 0, 0, 937, 931, 1, 0, 0, 0, 938, 941, 1, 0, 0, 0, 939, 937, 1, 0, 0, 0, 939, 940, 1, 0, 0, 0, 940, 163, 1, 0, 0, 0, 941, 939, 1, 0, 0, 0, 942, 945, 3, 228, 114, 0, 943, 945, 5, 9, 0, 0, 944, 942, 1, 0, 0, 0, 944, 943, 1, 0, 0, 0, 945, 165, 1, 0, 0, 0, 946, 951, 3, 168, 84, 0, 947, 948, 5, 8, 0, 0, 948, 950, 3, 168, 84, 0, 949, 947, 1, 0, 0, 0, 950, 953, 1, 0, 0, 0, 951, 949, 1, 0, 0, 0, 951, 952, 1, 0, 0, 0, 952, 167, 1, 0, 0, 0, 953, 951, 1, 0, 0, 0, 954, 955, 3, 222, 111, 0, 955, 169, 1, 0, 0, 0, 956, 957, 3, 226, 113, 0, 957, 958, 3, 174, 87, 0, 958, 963, 1, 0, 0, 0, 959, 960, 3, 214, 107, 0, 960, 961, 3, 172, 86, 0, 961, 963, 1, 0, 0, 0, 962, 956, 1, 0, 0, 0, 962, 959, 1, 0, 0, 0, 963, 171, 1, 0, 0, 0, 964, 966, 3, 174, 87, 0, 965, 964, 1, 0, 0, 0, 965, 966, 1, 0, 0, 0, 966, 173, 1, 0, 0, 0, 967, 974, 3, 182, 91, 0, 968, 970, 5, 6, 0, 0, 969, 971, 3, 180, 90, 0, 970, 969, 1, 0, 0, 0, 970, 971, 1, 0, 0, 0, 971, 973, 1, 0, 0, 0, 972, 968, 1, 0, 0, 0, 973, 976, 1, 0, 0, 0, 974, 972, 1, 0, 0, 0, 974, 975, 1, 0, 0, 0, 975, 175, 1, 0, 0, 0, 976, 974, 1, 0, 0, 0, 977, 978, 3, 190, 95, 0, 978, 177, 1, 0, 0, 0, 979, 980, 3, 230, 115, 0, 980, 179, 1, 0, 0, 0, 981, 982, 3, 184, 92, 0, 982, 983, 3, 166, 83, 0, 983, 181, 1, 0, 0, 0, 984, 985, 3, 184, 92, 0, 985, 986, 3, 186, 93, 0, 986, 183, 1, 0, 0, 0, 987, 990, 3, 176, 88, 0, 988, 990, 3, 178, 89, 0, 989, 987, 1, 0, 0, 0, 989, 988, 1, 0, 0, 0, 990, 185, 1, 0, 0, 0, 991, 996, 3, 188, 94, 0, 992, 993, 5, 8, 0, 0, 993, 995, 3, 188, 94, 0, 994, 992, 1, 0, 0, 0, 995, 998, 1, 0, 0, 0, 996, 994, 1, 0, 0, 0, 996, 997, 1, 0, 0, 0, 997, 187, 1, 0, 0, 0, 998, 996, 1, 0, 0, 0, 999, 1000, 3, 224, 112, 0, 1000, 189, 1, 0, 0, 0, 1001, 1002, 3, 192, 96, 0, 1002, 191, 1, 0, 0, 0, 1003, 1008, 3, 194, 97, 0, 1004, 1005, 5, 10, 0, 0, 1005, 1007, 3, 194, 97, 0, 1006, 1004, 1, 0, 0, 0, 1007, 1010, 1, 0, 0, 0, 1008, 1006, 1, 0, 0, 0, 1008, 1009, 1, 0, 0, 0, 1009, 193, 1, 0, 0, 0, 1010, 1008, 1, 0, 0, 0, 1011, 1016, 3, 198, 99, 0, 1012, 1013, 5, 11, 0, 0, 1013, 1015, 3, 198, 99, 0, 1014, 1012, 1, 0, 0, 0, 1015, 1018, 1, 0, 0, 0, 1016, 1014, 1, 0, 0, 0, 1016, 1017, 1, 0, 0, 0, 1017, 195, 1, 0, 0, 0, 1018, 1016, 1, 0, 0, 0, 1019, 1021, 3, 202, 101, 0, 1020, 1022, 3, 200, 100, 0, 1021, 1020, 1, 0, 0, 0, 1021, 1022, 1, 0, 0, 0, 1022, 197, 1, 0, 0, 0, 1023, 1027, 3, 196, 98, 0, 1024, 1025, 5, 12, 0, 0, 1025, 1027, 3, 196, 98, 0, 1026, 1023, 1, 0, 0, 0, 1026, 1024, 1, 0, 0, 0, 1027, 199, 1, 0, 0, 0, 1028, 1029, 7, 2, 0, 0, 1029, 201, 1, 0, 0, 0, 1030, 1039, 3, 302, 151, 0, 1031, 1039, 5, 9, 0, 0, 1032, 1033, 5, 15, 0, 0, 1033, 1039, 3, 204, 102, 0, 1034, 1035, 5, 2, 0, 0, 1035, 1036, 3, 190, 95, 0, 1036, 1037, 5, 3, 0, 0, 1037, 1039, 1, 0, 0, 0, 1038, 1030, 1, 0, 0, 0, 1038, 1031, 1, 0, 0, 0, 1038, 1032, 1, 0, 0, 0, 1038, 1034, 1, 0, 0, 0, 1039, 203, 1, 0, 0, 0, 1040, 1054, 3, 206, 103, 0, 1041, 1050, 5, 2, 0, 0, 1042, 1047, 3, 206, 103, 0, 1043, 1044, 5, 10, 0, 0, 1044, 1046, 3, 206, 103, 0, 1045, 1043, 1, 0, 0, 0, 1046, 1049, 1, 0, 0, 0, 1047, 1045, 1, 0, 0, 0, 1047, 1048, 1, 0, 0, 0, 1048, 1051, 1, 0, 0, 0, 1049, 1047, 1, 0, 0, 0, 1050, 1042, 1, 0, 0, 0, 1050, 1051, 1, 0, 0, 0, 1051, 1052, 1, 0, 0, 0, 1052, 1054, 5, 3, 0, 0, 1053, 1040, 1, 0, 0, 0, 1053, 1041, 1, 0, 0, 0, 1054, 205, 1, 0, 0, 0, 1055, 1063, 3, 302, 151, 0, 1056, 1063, 5, 9, 0, 0, 1057, 1060, 5, 12, 0, 0, 1058, 1061, 3, 302, 151, 0, 1059, 1061, 5, 9, 0, 0, 1060, 1058, 1, 0, 0, 0, 1060, 1059, 1, 0, 0, 0, 1061, 1063, 1, 0, 0, 0, 1062, 1055, 1, 0, 0, 0, 1062, 1056, 1, 0, 0, 0, 1062, 1057, 1, 0, 0, 0, 1063, 207, 1, 0, 0, 0, 1064, 1065, 5, 149, 0, 0, 1065, 209, 1, 0, 0, 0, 1066, 1069, 3, 218, 109, 0, 1067, 1069, 3, 212, 106, 0, 1068, 1066, 1, 0, 0, 0, 1068, 1067, 1, 0, 0, 0, 1069, 211, 1, 0, 0, 0, 1070, 1071, 5, 16, 0, 0, 1071, 1072, 3, 162, 81, 0, 1072, 1073, 5, 17, 0, 0, 1073, 213, 1, 0, 0, 0, 1074, 1077, 3, 220, 110, 0, 1075, 1077, 3, 216, 108, 0, 1076, 1074, 1, 0, 0, 0, 1076, 1075, 1, 0, 0, 0, 1077, 215, 1, 0, 0, 0, 1078, 1079, 5, 16, 0, 0, 1079, 1080, 3, 174, 87, 0, 1080, 1081, 5, 17, 0, 0, 1081, 217, 1, 0, 0, 0, 1082, 1084, 5, 2, 0, 0, 1083, 1085, 3, 222, 111, 0, 1084, 1083, 1, 0, 0, 0, 1085, 1086, 1, 0, 0, 0, 1086, 1084, 1, 0, 0, 0, 1086, 1087, 1, 0, 0, 0, 1087, 1088, 1, 0, 0, 0, 1088, 1089, 5, 3, 0, 0, 1089, 219, 1, 0, 0, 0, 1090, 1092, 5, 2, 0, 0, 1091, 1093, 3, 224, 112, 0, 1092, 1091, 1, 0, 0, 0, 1093, 1094, 1, 0, 0, 0, 1094, 1092, 1, 0, 0, 0, 1094, 1095, 1, 0, 0, 0, 1095, 1096, 1, 0, 0, 0, 1096, 1097, 5, 3, 0, 0, 1097, 221, 1, 0, 0, 0, 1098, 1101, 3, 226, 113, 0, 1099, 1101, 3, 210, 105, 0, 1100, 1098, 1, 0, 0, 0, 1100, 1099, 1, 0, 0, 0, 1101, 223, 1, 0, 0, 0, 1102, 1105, 3, 226, 113, 0, 1103, 1105, 3, 214, 107, 0, 1104, 1102, 1, 0, 0, 0, 1104, 1103, 1, 0, 0, 0, 1105, 225, 1, 0, 0, 0, 1106, 1109, 3, 230, 115, 0, 1107, 1109, 3, 232, 116, 0, 1108, 1106, 1, 0, 0, 0, 1108, 1107, 1, 0, 0, 0, 1109, 227, 1, 0, 0, 0, 1110, 1113, 3, 230, 115, 0, 1111, 1113, 3, 302, 151, 0, 1112, 1110, 1, 0, 0, 0, 1112, 1111, 1, 0, 0, 0, 1113, 229, 1, 0, 0, 0, 1114, 1115, 7, 3, 0, 0, 1115, 231, 1, 0, 0, 0, 1116, 1123, 3, 302, 151, 0, 1117, 1123, 3, 288, 144, 0, 1118, 1123, 3, 290, 145, 0, 1119, 1123, 3, 298, 149, 0, 1120, 1123, 3, 306, 153, 0, 1121, 1123, 5, 164, 0, 0, 1122, 1116, 1, 0, 0, 0, 1122, 1117, 1, 0, 0, 0, 1122, 1118, 1, 0, 0, 0, 1122, 1119, 1, 0, 0, 0, 1122, 1120, 1, 0, 0, 0, 1122, 1121, 1, 0, 0, 0, 1123, 233, 1, 0, 0, 0, 1124, 1125, 3, 236, 118, 0, 1125, 235, 1, 0, 0, 0, 1126, 1131, 3, 238, 119, 0, 1127, 1128, 5, 18, 0, 0, 1128, 1130, 3, 238, 119, 0, 1129, 1127, 1, 0, 0, 0, 1130, 1133, 1, 0, 0, 0, 1131, 1129, 1, 0, 0, 0, 1131, 1132, 1, 0, 0, 0, 1132, 237, 1, 0, 0, 0, 1133, 1131, 1, 0, 0, 0, 1134, 1139, 3, 240, 120, 0, 1135, 1136, 5, 19, 0, 0, 1136, 1138, 3, 240, 120, 0, 1137, 1135, 1, 0, 0, 0, 1138, 1141, 1, 0, 0, 0, 1139, 1137, 1, 0, 0, 0, 1139, 1140, 1, 0, 0, 0, 1140, 239, 1, 0, 0, 0, 1141, 1139, 1, 0, 0, 0, 1142, 1143, 3, 242, 121, 0, 1143, 241, 1, 0, 0, 0, 1144, 1162, 3, 244, 122, 0, 1145, 1146, 5, 20, 0, 0, 1146, 1163, 3, 244, 122, 0, 1147, 1148, 5, 21, 0, 0, 1148, 1163, 3, 244, 122, 0, 1149, 1150, 5, 22, 0, 0, 1150, 1163, 3, 244, 122, 0, 1151, 1152, 5, 23, 0, 0, 1152, 1163, 3, 244, 122, 0, 1153, 1154, 5, 24, 0, 0, 1154, 1163, 3, 244, 122, 0, 1155, 1156, 5, 25, 0, 0, 1156, 1163, 3, 244, 122, 0, 1157, 1158, 5, 79, 0, 0, 1158, 1163, 3, 152, 76, 0, 1159, 1160, 5, 78, 0, 0, 1160, 1161, 5, 79, 0, 0, 1161, 1163, 3, 152, 76, 0, 1162, 1145, 1, 0, 0, 0, 1162, 1147, 1, 0, 0, 0, 1162, 1149, 1, 0, 0, 0, 1162, 1151, 1, 0, 0, 0, 1162, 1153, 1, 0, 0, 0, 1162, 1155, 1, 0, 0, 0, 1162, 1157, 1, 0, 0, 0, 1162, 1159, 1, 0, 0, 0, 1162, 1163, 1, 0, 0, 0, 1163, 243, 1, 0, 0, 0, 1164, 1165, 3, 246, 123, 0, 1165, 245, 1, 0, 0, 0, 1166, 1170, 3, 256, 128, 0, 1167, 1169, 3, 248, 124, 0, 1168, 1167, 1, 0, 0, 0, 1169, 1172, 1, 0, 0, 0, 1170, 1168, 1, 0, 0, 0, 1170, 1171, 1, 0, 0, 0, 1171, 247, 1, 0, 0, 0, 1172, 1170, 1, 0, 0, 0, 1173, 1174, 5, 13, 0, 0, 1174, 1179, 3, 250, 125, 0, 1175, 1176, 5, 26, 0, 0, 1176, 1179, 3, 252, 126, 0, 1177, 1179, 3, 254, 127, 0, 1178, 1173, 1, 0, 0, 0, 1178, 1175, 1, 0, 0, 0, 1178, 1177, 1, 0, 0, 0, 1179, 249, 1, 0, 0, 0, 1180, 1181, 3, 256, 128, 0, 1181, 251, 1, 0, 0, 0, 1182, 1183, 3, 256, 128, 0, 1183, 253, 1, 0, 0, 0, 1184, 1187, 3, 294, 147, 0, 1185, 1187, 3, 296, 148, 0, 1186, 1184, 1, 0, 0, 0, 1186, 1185, 1, 0, 0, 0, 1187, 1191, 1, 0, 0, 0, 1188, 1190, 3, 258, 129, 0, 1189, 1188, 1, 0, 0, 0, 1190, 1193, 1, 0, 0, 0, 1191, 1189, 1, 0, 0, 0, 1191, 1192, 1, 0, 0, 0, 1192, 255, 1, 0, 0, 0, 1193, 1191, 1, 0, 0, 0, 1194, 1198, 3, 264, 132, 0, 1195, 1197, 3, 258, 129, 0, 1196, 1195, 1, 0, 0, 0, 1197, 1200, 1, 0, 0, 0, 1198, 1196, 1, 0, 0, 0, 1198, 1199, 1, 0, 0, 0, 1199, 257, 1, 0, 0, 0, 1200, 1198, 1, 0, 0, 0, 1201, 1204, 3, 260, 130, 0, 1202, 1204, 3, 262, 131, 0, 1203, 1201, 1, 0, 0, 0, 1203, 1202, 1, 0, 0, 0, 1204, 259, 1, 0, 0, 0, 1205, 1206, 5, 1, 0, 0, 1206, 1207, 3, 264, 132, 0, 1207, 261, 1, 0, 0, 0, 1208, 1209, 5, 11, 0, 0, 1209, 1210, 3, 264, 132, 0, 1210, 263, 1, 0, 0, 0, 1211, 1212, 5, 15, 0, 0, 1212, 1219, 3, 266, 133, 0, 1213, 1214, 5, 13, 0, 0, 1214, 1219, 3, 266, 133, 0, 1215, 1216, 5, 26, 0, 0, 1216, 1219, 3, 266, 133, 0, 1217, 1219, 3, 266, 133, 0, 1218, 1211, 1, 0, 0, 0, 1218, 1213, 1, 0, 0, 0, 1218, 1215, 1, 0, 0, 0, 1218, 1217, 1, 0, 0, 0, 1219, 265, 1, 0, 0, 0, 1220, 1228, 3, 268, 134, 0, 1221, 1228, 3, 270, 135, 0, 1222, 1228, 3, 286, 143, 0, 1223, 1228, 3, 288, 144, 0, 1224, 1228, 3, 290, 145, 0, 1225, 1228, 3, 298, 149, 0, 1226, 1228, 3, 230, 115, 0, 1227, 1220, 1, 0, 0, 0, 1227, 1221, 1, 0, 0, 0, 1227, 1222, 1, 0, 0, 0, 1227, 1223, 1, 0, 0, 0, 1227, 1224, 1, 0, 0, 0, 1227, 1225, 1, 0, 0, 0, 1227, 1226, 1, 0, 0, 0, 1228, 267, 1, 0, 0, 0, 1229, 1230, 5, 2, 0, 0, 1230, 1231, 3, 234, 117, 0, 1231, 1232, 5, 3, 0, 0, 1232, 269, 1, 0, 0, 0, 1233, 1488, 3, 284, 142, 0, 1234, 1235, 5, 80, 0, 0, 1235, 1236, 5, 2, 0, 0, 1236, 1237, 3, 234, 117, 0, 1237, 1238, 5, 3, 0, 0, 1238, 1488, 1, 0, 0, 0, 1239, 1488, 3, 274, 137, 0, 1240, 1241, 5, 82, 0, 0, 1241, 1242, 5, 2, 0, 0, 1242, 1243, 3, 234, 117, 0, 1243, 1244, 5, 8, 0, 0, 1244, 1245, 3, 234, 117, 0, 1245, 1246, 5, 3, 0, 0, 1246, 1488, 1, 0, 0, 0, 1247, 1248, 5, 83, 0, 0, 1248, 1249, 5, 2, 0, 0, 1249, 1250, 3, 234, 117, 0, 1250, 1251, 5, 3, 0, 0, 1251, 1488, 1, 0, 0, 0, 1252, 1253, 5, 84, 0, 0, 1253, 1254, 5, 2, 0, 0, 1254, 1255, 3, 230, 115, 0, 1255, 1256, 5, 3, 0, 0, 1256, 1488, 1, 0, 0, 0, 1257, 1258, 5, 85, 0, 0, 1258, 1259, 5, 2, 0, 0, 1259, 1260, 3, 234, 117, 0, 1260, 1261, 5, 3, 0, 0, 1261, 1488, 1, 0, 0, 0, 1262, 1263, 5, 86, 0, 0, 1263, 1264, 5, 2, 0, 0, 1264, 1265, 3, 234, 117, 0, 1265, 1266, 5, 3, 0, 0, 1266, 1488, 1, 0, 0, 0, 1267, 1273, 5, 87, 0, 0, 1268, 1269, 5, 2, 0, 0, 1269, 1270, 3, 234, 117, 0, 1270, 1271, 5, 3, 0, 0, 1271, 1274, 1, 0, 0, 0, 1272, 1274, 5, 164, 0, 0, 1273, 1268, 1, 0, 0, 0, 1273, 1272, 1, 0, 0, 0, 1274, 1488, 1, 0, 0, 0, 1275, 1276, 5, 88, 0, 0, 1276, 1488, 5, 164, 0, 0, 1277, 1278, 5, 89, 0, 0, 1278, 1279, 5, 2, 0, 0, 1279, 1280, 3, 234, 117, 0, 1280, 1281, 5, 3, 0, 0, 1281, 1488, 1, 0, 0, 0, 1282, 1283, 5, 90, 0, 0, 1283, 1284, 5, 2, 0, 0, 1284, 1285, 3, 234, 117, 0, 1285, 1286, 5, 3, 0, 0, 1286, 1488, 1, 0, 0, 0, 1287, 1288, 5, 91, 0, 0, 1288, 1289, 5, 2, 0, 0, 1289, 1290, 3, 234, 117, 0, 1290, 1291, 5, 3, 0, 0, 1291, 1488, 1, 0, 0, 0, 1292, 1293, 5, 92, 0, 0, 1293, 1294, 5, 2, 0, 0, 1294, 1295, 3, 234, 117, 0, 1295, 1296, 5, 3, 0, 0, 1296, 1488, 1, 0, 0, 0, 1297, 1298, 5, 93, 0, 0, 1298, 1488, 3, 152, 76, 0, 1299, 1488, 3, 276, 138, 0, 1300, 1301, 5, 94, 0, 0, 1301, 1302, 5, 2, 0, 0, 1302, 1303, 3, 234, 117, 0, 1303, 1304, 5, 3, 0, 0, 1304, 1488, 1, 0, 0, 0, 1305, 1488, 3, 278, 139, 0, 1306, 1307, 5, 95, 0, 0, 1307, 1308, 5, 2, 0, 0, 1308, 1309, 3, 234, 117, 0, 1309, 1310, 5, 3, 0, 0, 1310, 1488, 1, 0, 0, 0, 1311, 1312, 5, 96, 0, 0, 1312, 1313, 5, 2, 0, 0, 1313, 1314, 3, 234, 117, 0, 1314, 1315, 5, 3, 0, 0, 1315, 1488, 1, 0, 0, 0, 1316, 1317, 5, 97, 0, 0, 1317, 1318, 5, 2, 0, 0, 1318, 1319, 3, 234, 117, 0, 1319, 1320, 5, 3, 0, 0, 1320, 1488, 1, 0, 0, 0, 1321, 1322, 5, 99, 0, 0, 1322, 1323, 5, 2, 0, 0, 1323, 1324, 3, 234, 117, 0, 1324, 1325, 5, 8, 0, 0, 1325, 1326, 3, 234, 117, 0, 1326, 1327, 5, 3, 0, 0, 1327, 1488, 1, 0, 0, 0, 1328, 1329, 5, 100, 0, 0, 1329, 1330, 5, 2, 0, 0, 1330, 1331, 3, 234, 117, 0, 1331, 1332, 5, 8, 0, 0, 1332, 1333, 3, 234, 117, 0, 1333, 1334, 5, 3, 0, 0, 1334, 1488, 1, 0, 0, 0, 1335, 1336, 5, 101, 0, 0, 1336, 1337, 5, 2, 0, 0, 1337, 1338, 3, 234, 117, 0, 1338, 1339, 5, 8, 0, 0, 1339, 1340, 3, 234, 117, 0, 1340, 1341, 5, 3, 0, 0, 1341, 1488, 1, 0, 0, 0, 1342, 1343, 5, 102, 0, 0, 1343, 1344, 5, 2, 0, 0, 1344, 1345, 3, 234, 117, 0, 1345, 1346, 5, 8, 0, 0, 1346, 1347, 3, 234, 117, 0, 1347, 1348, 5, 3, 0, 0, 1348, 1488, 1, 0, 0, 0, 1349, 1350, 5, 103, 0, 0, 1350, 1351, 5, 2, 0, 0, 1351, 1352, 3, 234, 117, 0, 1352, 1353, 5, 8, 0, 0, 1353, 1354, 3, 234, 117, 0, 1354, 1355, 5, 3, 0, 0, 1355, 1488, 1, 0, 0, 0, 1356, 1357, 5, 104, 0, 0, 1357, 1358, 5, 2, 0, 0, 1358, 1359, 3, 234, 117, 0, 1359, 1360, 5, 3, 0, 0, 1360, 1488, 1, 0, 0, 0, 1361, 1362, 5, 105, 0, 0, 1362, 1363, 5, 2, 0, 0, 1363, 1364, 3, 234, 117, 0, 1364, 1365, 5, 3, 0, 0, 1365, 1488, 1, 0, 0, 0, 1366, 1367, 5, 106, 0, 0, 1367, 1368, 5, 2, 0, 0, 1368, 1369, 3, 234, 117, 0, 1369, 1370, 5, 3, 0, 0, 1370, 1488, 1, 0, 0, 0, 1371, 1372, 5, 107, 0, 0, 1372, 1373, 5, 2, 0, 0, 1373, 1374, 3, 234, 117, 0, 1374, 1375, 5, 3, 0, 0, 1375, 1488, 1, 0, 0, 0, 1376, 1377, 5, 108, 0, 0, 1377, 1378, 5, 2, 0, 0, 1378, 1379, 3, 234, 117, 0, 1379, 1380, 5, 3, 0, 0, 1380, 1488, 1, 0, 0, 0, 1381, 1382, 5, 109, 0, 0, 1382, 1383, 5, 2, 0, 0, 1383, 1384, 3, 234, 117, 0, 1384, 1385, 5, 3, 0, 0, 1385, 1488, 1, 0, 0, 0, 1386, 1387, 5, 110, 0, 0, 1387, 1388, 5, 2, 0, 0, 1388, 1389, 3, 234, 117, 0, 1389, 1390, 5, 3, 0, 0, 1390, 1488, 1, 0, 0, 0, 1391, 1392, 5, 111, 0, 0, 1392, 1393, 5, 2, 0, 0, 1393, 1394, 3, 234, 117, 0, 1394, 1395, 5, 3, 0, 0, 1395, 1488, 1, 0, 0, 0, 1396, 1397, 5, 112, 0, 0, 1397, 1488, 5, 164, 0, 0, 1398, 1399, 5, 113, 0, 0, 1399, 1488, 5, 164, 0, 0, 1400, 1401, 5, 114, 0, 0, 1401, 1488, 5, 164, 0, 0, 1402, 1403, 5, 119, 0, 0, 1403, 1404, 5, 2, 0, 0, 1404, 1405, 3, 234, 117, 0, 1405, 1406, 5, 3, 0, 0, 1406, 1488, 1, 0, 0, 0, 1407, 1408, 5, 115, 0, 0, 1408, 1409, 5, 2, 0, 0, 1409, 1410, 3, 234, 117, 0, 1410, 1411, 5, 3, 0, 0, 1411, 1488, 1, 0, 0, 0, 1412, 1413, 5, 116, 0, 0, 1413, 1414, 5, 2, 0, 0, 1414, 1415, 3, 234, 117, 0, 1415, 1416, 5, 3, 0, 0, 1416, 1488, 1, 0, 0, 0, 1417, 1418, 5, 117, 0, 0, 1418, 1419, 5, 2, 0, 0, 1419, 1420, 3, 234, 117, 0, 1420, 1421, 5, 3, 0, 0, 1421, 1488, 1, 0, 0, 0, 1422, 1423, 5, 118, 0, 0, 1423, 1424, 5, 2, 0, 0, 1424, 1425, 3, 234, 117, 0, 1425, 1426, 5, 3, 0, 0, 1426, 1488, 1, 0, 0, 0, 1427, 1428, 5, 120, 0, 0, 1428, 1488, 3, 152, 76, 0, 1429, 1430, 5, 121, 0, 0, 1430, 1431, 5, 2, 0, 0, 1431, 1432, 3, 234, 117, 0, 1432, 1433, 5, 8, 0, 0, 1433, 1434, 3, 234, 117, 0, 1434, 1435, 5, 8, 0, 0, 1435, 1436, 3, 234, 117, 0, 1436, 1437, 5, 3, 0, 0, 1437, 1488, 1, 0, 0, 0, 1438, 1439, 5, 122, 0, 0, 1439, 1440, 5, 2, 0, 0, 1440, 1441, 3, 234, 117, 0, 1441, 1442, 5, 8, 0, 0, 1442, 1443, 3, 234, 117, 0, 1443, 1444, 5, 3, 0, 0, 1444, 1488, 1, 0, 0, 0, 1445, 1446, 5, 123, 0, 0, 1446, 1447, 5, 2, 0, 0, 1447, 1448, 3, 234, 117, 0, 1448, 1449, 5, 8, 0, 0, 1449, 1450, 3, 234, 117, 0, 1450, 1451, 5, 3, 0, 0, 1451, 1488, 1, 0, 0, 0, 1452, 1453, 5, 124, 0, 0, 1453, 1454, 5, 2, 0, 0, 1454, 1455, 3, 234, 117, 0, 1455, 1456, 5, 8, 0, 0, 1456, 1457, 3, 234, 117, 0, 1457, 1458, 5, 3, 0, 0, 1458, 1488, 1, 0, 0, 0, 1459, 1460, 5, 125, 0, 0, 1460, 1461, 5, 2, 0, 0, 1461, 1462, 3, 234, 117, 0, 1462, 1463, 5, 3, 0, 0, 1463, 1488, 1, 0, 0, 0, 1464, 1465, 5, 126, 0, 0, 1465, 1466, 5, 2, 0, 0, 1466, 1467, 3, 234, 117, 0, 1467, 1468, 5, 3, 0, 0, 1468, 1488, 1, 0, 0, 0, 1469, 1470, 5, 127, 0, 0, 1470, 1471, 5, 2, 0, 0, 1471, 1472, 3, 234, 117, 0, 1472, 1473, 5, 3, 0, 0, 1473, 1488, 1, 0, 0, 0, 1474, 1475, 5, 128, 0, 0, 1475, 1476, 5, 2, 0, 0, 1476, 1477, 3, 234, 117, 0, 1477, 1478, 5, 3, 0, 0, 1478, 1488, 1, 0, 0, 0, 1479, 1480, 5, 129, 0, 0, 1480, 1481, 5, 2, 0, 0, 1481, 1482, 3, 234, 117, 0, 1482, 1483, 5, 3, 0, 0, 1483, 1488, 1, 0, 0, 0, 1484, 1488, 3, 272, 136, 0, 1485, 1488, 3, 280, 140, 0, 1486, 1488, 3, 282, 141, 0, 1487, 1233, 1, 0, 0, 0, 1487, 1234, 1, 0, 0, 0, 1487, 1239, 1, 0, 0, 0, 1487, 1240, 1, 0, 0, 0, 1487, 1247, 1, 0, 0, 0, 1487, 1252, 1, 0, 0, 0, 1487, 1257, 1, 0, 0, 0, 1487, 1262, 1, 0, 0, 0, 1487, 1267, 1, 0, 0, 0, 1487, 1275, 1, 0, 0, 0, 1487, 1277, 1, 0, 0, 0, 1487, 1282, 1, 0, 0, 0, 1487, 1287, 1, 0, 0, 0, 1487, 1292, 1, 0, 0, 0, 1487, 1297, 1, 0, 0, 0, 1487, 1299, 1, 0, 0, 0, 1487, 1300, 1, 0, 0, 0, 1487, 1305, 1, 0, 0, 0, 1487, 1306, 1, 0, 0, 0, 1487, 1311, 1, 0, 0, 0, 1487, 1316, 1, 0, 0, 0, 1487, 1321, 1, 0, 0, 0, 1487, 1328, 1, 0, 0, 0, 1487, 1335, 1, 0, 0, 0, 1487, 1342, 1, 0, 0, 0, 1487, 1349, 1, 0, 0, 0, 1487, 1356, 1, 0, 0, 0, 1487, 1361, 1, 0, 0, 0, 1487, 1366, 1, 0, 0, 0, 1487, 1371, 1, 0, 0, 0, 1487, 1376, 1, 0, 0, 0, 1487, 1381, 1, 0, 0, 0, 1487, 1386, 1, 0, 0, 0, 1487, 1391, 1, 0, 0, 0, 1487, 1396, 1, 0, 0, 0, 1487, 1398, 1, 0, 0, 0, 1487, 1400, 1, 0, 0, 0, 1487, 1402, 1, 0, 0, 0, 1487, 1407, 1, 0, 0, 0, 1487, 1412, 1, 0, 0, 0, 1487, 1417, 1, 0, 0, 0, 1487, 1422, 1, 0, 0, 0, 1487, 1427, 1, 0, 0, 0, 1487, 1429, 1, 0, 0, 0, 1487, 1438, 1, 0, 0, 0, 1487, 1445, 1, 0, 0, 0, 1487, 1452, 1, 0, 0, 0, 1487, 1459, 1, 0, 0, 0, 1487, 1464, 1, 0, 0, 0, 1487, 1469, 1, 0, 0, 0, 1487, 1474, 1, 0, 0, 0, 1487, 1479, 1, 0, 0, 0, 1487, 1484, 1, 0, 0, 0, 1487, 1485, 1, 0, 0, 0, 1487, 1486, 1, 0, 0, 0, 1488, 271, 1, 0, 0, 0, 1489, 1490, 5, 130, 0, 0, 1490, 1491, 5, 2, 0, 0, 1491, 1492, 3, 234, 117, 0, 1492, 1493, 5, 8, 0, 0, 1493, 1496, 3, 234, 117, 0, 1494, 1495, 5, 8, 0, 0, 1495, 1497, 3, 234, 117, 0, 1496, 1494, 1, 0, 0, 0, 1496, 1497, 1, 0, 0, 0, 1497, 1498, 1, 0, 0, 0, 1498, 1499, 5, 3, 0, 0, 1499, 273, 1, 0, 0, 0, 1500, 1501, 5, 81, 0, 0, 1501, 1502, 5, 2, 0, 0, 1502, 1503, 3, 234, 117, 0, 1503, 1504, 5, 3, 0, 0, 1504, 275, 1, 0, 0, 0, 1505, 1506, 5, 131, 0, 0, 1506, 1507, 5, 2, 0, 0, 1507, 1508, 3, 234, 117, 0, 1508, 1509, 5, 8, 0, 0, 1509, 1512, 3, 234, 117, 0, 1510, 1511, 5, 8, 0, 0, 1511, 1513, 3, 234, 117, 0, 1512, 1510, 1, 0, 0, 0, 1512, 1513, 1, 0, 0, 0, 1513, 1514, 1, 0, 0, 0, 1514, 1515, 5, 3, 0, 0, 1515, 277, 1, 0, 0, 0, 1516, 1517, 5, 132, 0, 0, 1517, 1518, 5, 2, 0, 0, 1518, 1519, 3, 234, 117, 0, 1519, 1520, 5, 8, 0, 0, 1520, 1521, 3, 234, 117, 0, 1521, 1522, 5, 8, 0, 0, 1522, 1525, 3, 234, 117, 0, 1523, 1524, 5, 8, 0, 0, 1524, 1526, 3, 234, 117, 0, 1525, 1523, 1, 0, 0, 0, 1525, 1526, 1, 0, 0, 0, 1526, 1527, 1, 0, 0, 0, 1527, 1528, 5, 3, 0, 0, 1528, 279, 1, 0, 0, 0, 1529, 1530, 5, 133, 0, 0, 1530, 1531, 3, 110, 55, 0, 1531, 281, 1, 0, 0, 0, 1532, 1533, 5, 78, 0, 0, 1533, 1534, 5, 133, 0, 0, 1534, 1535, 3, 110, 55, 0, 1535, 283, 1, 0, 0, 0, 1536, 1537, 5, 134, 0, 0, 1537, 1539, 5, 2, 0, 0, 1538, 1540, 5, 33, 0, 0, 1539, 1538, 1, 0, 0, 0, 1539, 1540, 1, 0, 0, 0, 1540, 1543, 1, 0, 0, 0, 1541, 1544, 5, 1, 0, 0, 1542, 1544, 3, 234, 117, 0, 1543, 1541, 1, 0, 0, 0, 1543, 1542, 1, 0, 0, 0, 1544, 1545, 1, 0, 0, 0, 1545, 1601, 5, 3, 0, 0, 1546, 1547, 5, 135, 0, 0, 1547, 1549, 5, 2, 0, 0, 1548, 1550, 5, 33, 0, 0, 1549, 1548, 1, 0, 0, 0, 1549, 1550, 1, 0, 0, 0, 1550, 1551, 1, 0, 0, 0, 1551, 1552, 3, 234, 117, 0, 1552, 1553, 5, 3, 0, 0, 1553, 1601, 1, 0, 0, 0, 1554, 1555, 5, 136, 0, 0, 1555, 1557, 5, 2, 0, 0, 1556, 1558, 5, 33, 0, 0, 1557, 1556, 1, 0, 0, 0, 1557, 1558, 1, 0, 0, 0, 1558, 1559, 1, 0, 0, 0, 1559, 1560, 3, 234, 117, 0, 1560, 1561, 5, 3, 0, 0, 1561, 1601, 1, 0, 0, 0, 1562, 1563, 5, 137, 0, 0, 1563, 1565, 5, 2, 0, 0, 1564, 1566, 5, 33, 0, 0, 1565, 1564, 1, 0, 0, 0, 1565, 1566, 1, 0, 0, 0, 1566, 1567, 1, 0, 0, 0, 1567, 1568, 3, 234, 117, 0, 1568, 1569, 5, 3, 0, 0, 1569, 1601, 1, 0, 0, 0, 1570, 1571, 5, 138, 0, 0, 1571, 1573, 5, 2, 0, 0, 1572, 1574, 5, 33, 0, 0, 1573, 1572, 1, 0, 0, 0, 1573, 1574, 1, 0, 0, 0, 1574, 1575, 1, 0, 0, 0, 1575, 1576, 3, 234, 117, 0, 1576, 1577, 5, 3, 0, 0, 1577, 1601, 1, 0, 0, 0, 1578, 1579, 5, 139, 0, 0, 1579, 1581, 5, 2, 0, 0, 1580, 1582, 5, 33, 0, 0, 1581, 1580, 1, 0, 0, 0, 1581, 1582, 1, 0, 0, 0, 1582, 1583, 1, 0, 0, 0, 1583, 1584, 3, 234, 117, 0, 1584, 1585, 5, 3, 0, 0, 1585, 1601, 1, 0, 0, 0, 1586, 1587, 5, 43, 0, 0, 1587, 1589, 5, 2, 0, 0, 1588, 1590, 5, 33, 0, 0, 1589, 1588, 1, 0, 0, 0, 1589, 1590, 1, 0, 0, 0, 1590, 1591, 1, 0, 0, 0, 1591, 1596, 3, 234, 117, 0, 1592, 1593, 5, 6, 0, 0, 1593, 1594, 5, 140, 0, 0, 1594, 1595, 5, 20, 0, 0, 1595, 1597, 3, 300, 150, 0, 1596, 1592, 1, 0, 0, 0, 1596, 1597, 1, 0, 0, 0, 1597, 1598, 1, 0, 0, 0, 1598, 1599, 5, 3, 0, 0, 1599, 1601, 1, 0, 0, 0, 1600, 1536, 1, 0, 0, 0, 1600, 1546, 1, 0, 0, 0, 1600, 1554, 1, 0, 0, 0, 1600, 1562, 1, 0, 0, 0, 1600, 1570, 1, 0, 0, 0, 1600, 1578, 1, 0, 0, 0, 1600, 1586, 1, 0, 0, 0, 1601, 285, 1, 0, 0, 0, 1602, 1604, 3, 302, 151, 0, 1603, 1605, 3, 150, 75, 0, 1604, 1603, 1, 0, 0, 0, 1604, 1605, 1, 0, 0, 0, 1605, 287, 1, 0, 0, 0, 1606, 1610, 3, 300, 150, 0, 1607, 1611, 5, 147, 0, 0, 1608, 1609, 5, 27, 0, 0, 1609, 1611, 3, 302, 151, 0, 1610, 1607, 1, 0, 0, 0, 1610, 1608, 1, 0, 0, 0, 1610, 1611, 1, 0, 0, 0, 1611, 289, 1, 0, 0, 0, 1612, 1616, 3, 292, 146, 0, 1613, 1616, 3, 294, 147, 0, 1614, 1616, 3, 296, 148, 0, 1615, 1612, 1, 0, 0, 0, 1615, 1613, 1, 0, 0, 0, 1615, 1614, 1, 0, 0, 0, 1616, 291, 1, 0, 0, 0, 1617, 1618, 7, 4, 0, 0, 1618, 293, 1, 0, 0, 0, 1619, 1620, 7, 5, 0, 0, 1620, 295, 1, 0, 0, 0, 1621, 1622, 7, 6, 0, 0, 1622, 297, 1, 0, 0, 0, 1623, 1624, 7, 7, 0, 0, 1624, 299, 1, 0, 0, 0, 1625, 1626, 7, 8, 0, 0, 1626, 301, 1, 0, 0, 0, 1627, 1629, 5, 148, 0, 0, 1628, 1627, 1, 0, 0, 0, 1628, 1629, 1, 0, 0, 0, 1629, 1632, 1, 0, 0, 0, 1630, 1633, 3, 308, 154, 0, 1631, 1633, 3, 304, 152, 0, 1632, 1630, 1, 0, 0, 0, 1632, 1631, 1, 0, 0, 0, 1633, 303, 1, 0, 0, 0, 1634, 1637, 3, 310, 155, 0, 1635, 1637, 3, 312, 156, 0, 1636, 1634, 1, 0, 0, 0, 1636, 1635, 1, 0, 0, 0, 1637, 305, 1, 0, 0, 0, 1638, 1639, 7, 9, 0, 0, 1639, 307, 1, 0, 0, 0, 1640, 1641, 5, 141, 0, 0, 1641, 309, 1, 0, 0, 0, 1642, 1643, 5, 143, 0, 0, 1643, 311, 1, 0, 0, 0, 1644, 1645, 5, 142, 0, 0, 1645, 313, 1, 0, 0, 0, 161, 316, 325, 331, 333, 347, 360, 365, 368, 372, 387, 396, 402, 406, 412, 415, 420, 424, 432, 441, 451, 456, 459, 462, 465, 471, 479, 484, 490, 496, 501, 507, 509, 513, 516, 520, 523, 527, 530, 534, 537, 541, 544, 548, 551, 553, 566, 572, 574, 587, 591, 596, 600, 606, 612, 618, 626, 634, 654, 658, 661, 666, 682, 687, 696, 707, 711, 714, 718, 725, 732, 734, 739, 744, 749, 754, 757, 762, 764, 774, 785, 802, 809, 819, 823, 829, 838, 843, 850, 860, 869, 877, 884, 889, 898, 903, 907, 914, 916, 924, 927, 935, 939, 944, 951, 962, 965, 970, 974, 989, 996, 1008, 1016, 1021, 1026, 1038, 1047, 1050, 1053, 1060, 1062, 1068, 1076, 1086, 1094, 1100, 1104, 1108, 1112, 1122, 1131, 1139, 1162, 1170, 1178, 1186, 1191, 1198, 1203, 1218, 1227, 1273, 1487, 1496, 1512, 1525, 1539, 1543, 1549, 1557, 1565, 1573, 1581, 1589, 1596, 1600, 1604, 1610, 1615, 1628, 1632, 1636] \ No newline at end of file +[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 diff --git a/src/parser/sparqlParser/generated/SparqlAutomatic.tokens b/src/parser/sparqlParser/generated/SparqlAutomatic.tokens index 4d2a3fec0c..0d98dd932a 100644 --- a/src/parser/sparqlParser/generated/SparqlAutomatic.tokens +++ b/src/parser/sparqlParser/generated/SparqlAutomatic.tokens @@ -136,43 +136,44 @@ SUM=135 MIN=136 MAX=137 AVG=138 -SAMPLE=139 -SEPARATOR=140 -IRI_REF=141 -PNAME_NS=142 -PNAME_LN=143 -BLANK_NODE_LABEL=144 -VAR1=145 -VAR2=146 -LANGTAG=147 -PREFIX_LANGTAG=148 -INTEGER=149 -DECIMAL=150 -DOUBLE=151 -INTEGER_POSITIVE=152 -DECIMAL_POSITIVE=153 -DOUBLE_POSITIVE=154 -INTEGER_NEGATIVE=155 -DECIMAL_NEGATIVE=156 -DOUBLE_NEGATIVE=157 -EXPONENT=158 -STRING_LITERAL1=159 -STRING_LITERAL2=160 -STRING_LITERAL_LONG1=161 -STRING_LITERAL_LONG2=162 -ECHAR=163 -NIL=164 -ANON=165 -PN_CHARS_U=166 -VARNAME=167 -PN_PREFIX=168 -PN_LOCAL=169 -PLX=170 -PERCENT=171 -HEX=172 -PN_LOCAL_ESC=173 -WS=174 -COMMENTS=175 +STDEV=139 +SAMPLE=140 +SEPARATOR=141 +IRI_REF=142 +PNAME_NS=143 +PNAME_LN=144 +BLANK_NODE_LABEL=145 +VAR1=146 +VAR2=147 +LANGTAG=148 +PREFIX_LANGTAG=149 +INTEGER=150 +DECIMAL=151 +DOUBLE=152 +INTEGER_POSITIVE=153 +DECIMAL_POSITIVE=154 +DOUBLE_POSITIVE=155 +INTEGER_NEGATIVE=156 +DECIMAL_NEGATIVE=157 +DOUBLE_NEGATIVE=158 +EXPONENT=159 +STRING_LITERAL1=160 +STRING_LITERAL2=161 +STRING_LITERAL_LONG1=162 +STRING_LITERAL_LONG2=163 +ECHAR=164 +NIL=165 +ANON=166 +PN_CHARS_U=167 +VARNAME=168 +PN_PREFIX=169 +PN_LOCAL=170 +PLX=171 +PERCENT=172 +HEX=173 +PN_LOCAL_ESC=174 +WS=175 +COMMENTS=176 '*'=1 '('=2 ')'=3 diff --git a/src/parser/sparqlParser/generated/SparqlAutomaticLexer.cpp b/src/parser/sparqlParser/generated/SparqlAutomaticLexer.cpp index a1c2bd1aaf..15306faaff 100644 --- a/src/parser/sparqlParser/generated/SparqlAutomaticLexer.cpp +++ b/src/parser/sparqlParser/generated/SparqlAutomaticLexer.cpp @@ -186,6 +186,7 @@ void sparqlautomaticlexerLexerInitialize() { "MIN", "MAX", "AVG", + "STDEV", "SAMPLE", "SEPARATOR", "IRI_REF", @@ -399,6 +400,7 @@ void sparqlautomaticlexerLexerInitialize() { "MIN", "MAX", "AVG", + "STDEV", "SAMPLE", "SEPARATOR", "IRI_REF", @@ -437,1259 +439,1265 @@ void sparqlautomaticlexerLexerInitialize() { "WS", "COMMENTS"}); static const int32_t serializedATNSegment[] = { - 4, 0, 175, 1590, 6, -1, 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, 2, 157, 7, 157, - 2, 158, 7, 158, 2, 159, 7, 159, 2, 160, 7, - 160, 2, 161, 7, 161, 2, 162, 7, 162, 2, 163, - 7, 163, 2, 164, 7, 164, 2, 165, 7, 165, 2, - 166, 7, 166, 2, 167, 7, 167, 2, 168, 7, 168, - 2, 169, 7, 169, 2, 170, 7, 170, 2, 171, 7, - 171, 2, 172, 7, 172, 2, 173, 7, 173, 2, 174, - 7, 174, 2, 175, 7, 175, 2, 176, 7, 176, 2, - 177, 7, 177, 2, 178, 7, 178, 2, 179, 7, 179, - 2, 180, 7, 180, 2, 181, 7, 181, 2, 182, 7, - 182, 2, 183, 7, 183, 2, 184, 7, 184, 2, 185, - 7, 185, 2, 186, 7, 186, 2, 187, 7, 187, 2, - 188, 7, 188, 2, 189, 7, 189, 2, 190, 7, 190, - 2, 191, 7, 191, 2, 192, 7, 192, 2, 193, 7, - 193, 2, 194, 7, 194, 2, 195, 7, 195, 2, 196, - 7, 196, 2, 197, 7, 197, 2, 198, 7, 198, 2, - 199, 7, 199, 2, 200, 7, 200, 2, 201, 7, 201, - 2, 202, 7, 202, 2, 203, 7, 203, 1, 0, 1, - 0, 1, 1, 1, 1, 1, 2, 1, 2, 1, 3, - 1, 3, 1, 4, 1, 4, 1, 5, 1, 5, 1, - 6, 1, 6, 1, 7, 1, 7, 1, 8, 1, 8, - 1, 9, 1, 9, 1, 10, 1, 10, 1, 11, 1, - 11, 1, 12, 1, 12, 1, 13, 1, 13, 1, 14, - 1, 14, 1, 15, 1, 15, 1, 16, 1, 16, 1, - 17, 1, 17, 1, 17, 1, 18, 1, 18, 1, 18, - 1, 19, 1, 19, 1, 20, 1, 20, 1, 20, 1, - 21, 1, 21, 1, 22, 1, 22, 1, 23, 1, 23, - 1, 23, 1, 24, 1, 24, 1, 24, 1, 25, 1, - 25, 1, 26, 1, 26, 1, 26, 1, 27, 1, 27, - 1, 27, 1, 27, 1, 27, 1, 28, 1, 28, 1, - 28, 1, 28, 1, 28, 1, 28, 1, 29, 1, 29, - 1, 29, 1, 29, 1, 29, 1, 30, 1, 30, 1, - 30, 1, 30, 1, 30, 1, 30, 1, 30, 1, 31, - 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, - 31, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, - 1, 32, 1, 32, 1, 32, 1, 32, 1, 33, 1, - 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, - 1, 33, 1, 34, 1, 34, 1, 34, 1, 35, 1, - 35, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, - 1, 35, 1, 35, 1, 35, 1, 36, 1, 36, 1, - 36, 1, 36, 1, 36, 1, 36, 1, 37, 1, 37, - 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, - 37, 1, 37, 1, 38, 1, 38, 1, 38, 1, 38, - 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, - 40, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, - 1, 41, 1, 41, 1, 41, 1, 41, 1, 41, 1, - 41, 4, 41, 566, 8, 41, 11, 41, 12, 41, 567, - 1, 41, 1, 41, 1, 41, 1, 42, 1, 42, 1, - 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, - 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, - 43, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, - 1, 43, 1, 44, 1, 44, 1, 44, 1, 44, 1, - 44, 1, 44, 4, 44, 599, 8, 44, 11, 44, 12, - 44, 600, 1, 44, 1, 44, 1, 44, 1, 45, 1, - 45, 1, 45, 1, 45, 1, 45, 1, 45, 1, 45, - 1, 45, 1, 45, 4, 45, 615, 8, 45, 11, 45, - 12, 45, 616, 1, 45, 1, 45, 1, 45, 1, 45, - 1, 45, 4, 45, 624, 8, 45, 11, 45, 12, 45, - 625, 1, 45, 1, 45, 1, 45, 1, 46, 1, 46, - 1, 46, 1, 46, 1, 47, 1, 47, 1, 47, 1, - 47, 1, 47, 1, 48, 1, 48, 1, 48, 1, 48, - 1, 48, 1, 48, 1, 49, 1, 49, 1, 49, 1, - 49, 1, 49, 1, 49, 1, 49, 1, 50, 1, 50, - 1, 50, 1, 50, 1, 50, 1, 50, 1, 50, 1, - 50, 1, 50, 1, 50, 1, 51, 1, 51, 1, 51, - 1, 51, 1, 51, 1, 51, 1, 51, 1, 52, 1, - 52, 1, 52, 1, 52, 1, 52, 1, 53, 1, 53, - 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, - 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 55, - 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, - 56, 1, 56, 1, 56, 1, 56, 1, 56, 1, 57, - 1, 57, 1, 57, 1, 57, 1, 57, 1, 57, 1, - 57, 1, 58, 1, 58, 1, 58, 1, 58, 1, 59, - 1, 59, 1, 59, 1, 60, 1, 60, 1, 60, 1, - 60, 1, 60, 1, 61, 1, 61, 1, 61, 1, 61, - 1, 61, 1, 62, 1, 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, - 64, 1, 64, 1, 64, 1, 64, 1, 65, 1, 65, - 1, 65, 1, 65, 1, 65, 1, 66, 1, 66, 1, - 66, 1, 66, 1, 66, 1, 66, 1, 67, 1, 67, - 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, - 67, 1, 68, 1, 68, 1, 68, 1, 68, 1, 68, - 1, 68, 1, 69, 1, 69, 1, 69, 1, 69, 1, - 70, 1, 70, 1, 70, 1, 70, 1, 70, 1, 70, - 1, 70, 1, 70, 1, 70, 1, 71, 1, 71, 1, - 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, - 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, - 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, - 1, 74, 1, 74, 1, 74, 1, 74, 1, 74, 1, - 74, 1, 75, 1, 75, 1, 75, 1, 75, 1, 75, - 1, 75, 1, 76, 1, 76, 1, 76, 1, 76, 1, - 76, 1, 76, 1, 76, 1, 77, 1, 77, 1, 77, - 1, 77, 1, 78, 1, 78, 1, 78, 1, 79, 1, - 79, 1, 79, 1, 79, 1, 80, 1, 80, 1, 80, - 1, 80, 1, 80, 1, 81, 1, 81, 1, 81, 1, - 81, 1, 81, 1, 81, 1, 81, 1, 81, 1, 81, - 1, 81, 1, 81, 1, 81, 1, 82, 1, 82, 1, - 82, 1, 82, 1, 82, 1, 82, 1, 82, 1, 82, - 1, 82, 1, 83, 1, 83, 1, 83, 1, 83, 1, - 83, 1, 83, 1, 84, 1, 84, 1, 84, 1, 84, - 1, 85, 1, 85, 1, 85, 1, 85, 1, 86, 1, - 86, 1, 86, 1, 86, 1, 86, 1, 86, 1, 87, - 1, 87, 1, 87, 1, 87, 1, 87, 1, 88, 1, - 88, 1, 88, 1, 88, 1, 89, 1, 89, 1, 89, - 1, 89, 1, 89, 1, 90, 1, 90, 1, 90, 1, - 90, 1, 90, 1, 90, 1, 91, 1, 91, 1, 91, - 1, 91, 1, 91, 1, 91, 1, 92, 1, 92, 1, - 92, 1, 92, 1, 92, 1, 92, 1, 92, 1, 93, - 1, 93, 1, 93, 1, 93, 1, 93, 1, 93, 1, - 93, 1, 94, 1, 94, 1, 94, 1, 94, 1, 94, - 1, 94, 1, 95, 1, 95, 1, 95, 1, 95, 1, - 95, 1, 95, 1, 96, 1, 96, 1, 96, 1, 96, - 1, 96, 1, 96, 1, 96, 1, 96, 1, 96, 1, - 96, 1, 96, 1, 96, 1, 96, 1, 96, 1, 96, - 1, 97, 1, 97, 1, 97, 1, 97, 1, 98, 1, - 98, 1, 98, 1, 98, 1, 98, 1, 98, 1, 98, - 1, 98, 1, 98, 1, 99, 1, 99, 1, 99, 1, - 99, 1, 99, 1, 99, 1, 99, 1, 99, 1, 99, - 1, 99, 1, 100, 1, 100, 1, 100, 1, 100, 1, - 100, 1, 100, 1, 100, 1, 100, 1, 101, 1, 101, - 1, 101, 1, 101, 1, 101, 1, 101, 1, 101, 1, - 101, 1, 101, 1, 101, 1, 102, 1, 102, 1, 102, - 1, 102, 1, 102, 1, 102, 1, 102, 1, 102, 1, - 102, 1, 103, 1, 103, 1, 103, 1, 103, 1, 103, - 1, 104, 1, 104, 1, 104, 1, 104, 1, 104, 1, - 104, 1, 105, 1, 105, 1, 105, 1, 105, 1, 106, - 1, 106, 1, 106, 1, 106, 1, 106, 1, 106, 1, - 107, 1, 107, 1, 107, 1, 107, 1, 107, 1, 107, - 1, 107, 1, 107, 1, 108, 1, 108, 1, 108, 1, - 108, 1, 108, 1, 108, 1, 108, 1, 108, 1, 109, - 1, 109, 1, 109, 1, 109, 1, 109, 1, 109, 1, - 109, 1, 109, 1, 109, 1, 110, 1, 110, 1, 110, - 1, 111, 1, 111, 1, 111, 1, 111, 1, 112, 1, - 112, 1, 112, 1, 112, 1, 112, 1, 113, 1, 113, - 1, 113, 1, 113, 1, 113, 1, 113, 1, 113, 1, - 113, 1, 114, 1, 114, 1, 114, 1, 114, 1, 114, - 1, 115, 1, 115, 1, 115, 1, 115, 1, 115, 1, - 115, 1, 115, 1, 116, 1, 116, 1, 116, 1, 116, - 1, 116, 1, 116, 1, 116, 1, 117, 1, 117, 1, - 117, 1, 117, 1, 117, 1, 117, 1, 117, 1, 118, - 1, 118, 1, 118, 1, 118, 1, 119, 1, 119, 1, - 119, 1, 119, 1, 119, 1, 119, 1, 119, 1, 119, - 1, 119, 1, 120, 1, 120, 1, 120, 1, 121, 1, - 121, 1, 121, 1, 121, 1, 121, 1, 121, 1, 121, - 1, 121, 1, 122, 1, 122, 1, 122, 1, 122, 1, - 122, 1, 122, 1, 123, 1, 123, 1, 123, 1, 123, - 1, 123, 1, 123, 1, 123, 1, 123, 1, 123, 1, - 124, 1, 124, 1, 124, 1, 124, 1, 124, 1, 124, - 1, 125, 1, 125, 1, 125, 1, 125, 1, 125, 1, - 125, 1, 126, 1, 126, 1, 126, 1, 126, 1, 126, - 1, 126, 1, 126, 1, 126, 1, 127, 1, 127, 1, - 127, 1, 127, 1, 127, 1, 127, 1, 127, 1, 127, - 1, 127, 1, 127, 1, 128, 1, 128, 1, 128, 1, - 128, 1, 128, 1, 128, 1, 128, 1, 128, 1, 128, - 1, 128, 1, 129, 1, 129, 1, 129, 1, 129, 1, - 129, 1, 129, 1, 130, 1, 130, 1, 130, 1, 130, - 1, 130, 1, 130, 1, 130, 1, 131, 1, 131, 1, - 131, 1, 131, 1, 131, 1, 131, 1, 131, 1, 131, - 1, 132, 1, 132, 1, 132, 1, 132, 1, 132, 1, - 132, 1, 132, 1, 133, 1, 133, 1, 133, 1, 133, - 1, 133, 1, 133, 1, 134, 1, 134, 1, 134, 1, - 134, 1, 135, 1, 135, 1, 135, 1, 135, 1, 136, - 1, 136, 1, 136, 1, 136, 1, 137, 1, 137, 1, - 137, 1, 137, 1, 138, 1, 138, 1, 138, 1, 138, - 1, 138, 1, 138, 1, 138, 1, 139, 1, 139, 1, - 139, 1, 139, 1, 139, 1, 139, 1, 139, 1, 139, - 1, 139, 1, 139, 1, 140, 1, 140, 5, 140, 1231, - 8, 140, 10, 140, 12, 140, 1234, 9, 140, 1, 140, - 1, 140, 1, 141, 3, 141, 1239, 8, 141, 1, 141, - 1, 141, 1, 142, 1, 142, 1, 142, 1, 143, 1, - 143, 1, 143, 1, 143, 1, 143, 3, 143, 1251, 8, - 143, 1, 143, 1, 143, 5, 143, 1255, 8, 143, 10, - 143, 12, 143, 1258, 9, 143, 1, 143, 3, 143, 1261, - 8, 143, 1, 144, 1, 144, 1, 144, 1, 145, 1, - 145, 1, 145, 1, 146, 1, 146, 4, 146, 1271, 8, - 146, 11, 146, 12, 146, 1272, 1, 146, 1, 146, 1, - 146, 4, 146, 1278, 8, 146, 11, 146, 12, 146, 1279, - 5, 146, 1282, 8, 146, 10, 146, 12, 146, 1285, 9, - 146, 1, 147, 1, 147, 1, 147, 1, 148, 4, 148, - 1291, 8, 148, 11, 148, 12, 148, 1292, 1, 149, 5, - 149, 1296, 8, 149, 10, 149, 12, 149, 1299, 9, 149, - 1, 149, 1, 149, 4, 149, 1303, 8, 149, 11, 149, - 12, 149, 1304, 1, 150, 4, 150, 1308, 8, 150, 11, - 150, 12, 150, 1309, 1, 150, 1, 150, 5, 150, 1314, - 8, 150, 10, 150, 12, 150, 1317, 9, 150, 1, 150, - 1, 150, 1, 150, 1, 150, 4, 150, 1323, 8, 150, - 11, 150, 12, 150, 1324, 1, 150, 1, 150, 1, 150, - 4, 150, 1330, 8, 150, 11, 150, 12, 150, 1331, 1, - 150, 1, 150, 3, 150, 1336, 8, 150, 1, 151, 1, - 151, 1, 151, 1, 152, 1, 152, 1, 152, 1, 153, - 1, 153, 1, 153, 1, 154, 1, 154, 1, 154, 1, - 155, 1, 155, 1, 155, 1, 156, 1, 156, 1, 156, - 1, 157, 1, 157, 3, 157, 1358, 8, 157, 1, 157, - 4, 157, 1361, 8, 157, 11, 157, 12, 157, 1362, 1, - 158, 1, 158, 1, 158, 5, 158, 1368, 8, 158, 10, - 158, 12, 158, 1371, 9, 158, 1, 158, 1, 158, 1, - 159, 1, 159, 1, 159, 5, 159, 1378, 8, 159, 10, - 159, 12, 159, 1381, 9, 159, 1, 159, 1, 159, 1, - 160, 1, 160, 1, 160, 1, 160, 1, 160, 1, 160, - 1, 160, 3, 160, 1392, 8, 160, 1, 160, 1, 160, - 3, 160, 1396, 8, 160, 5, 160, 1398, 8, 160, 10, - 160, 12, 160, 1401, 9, 160, 1, 160, 1, 160, 1, - 160, 1, 160, 1, 161, 1, 161, 1, 161, 1, 161, - 1, 161, 1, 161, 1, 161, 3, 161, 1414, 8, 161, - 1, 161, 1, 161, 3, 161, 1418, 8, 161, 5, 161, - 1420, 8, 161, 10, 161, 12, 161, 1423, 9, 161, 1, - 161, 1, 161, 1, 161, 1, 161, 1, 162, 1, 162, - 1, 162, 1, 163, 1, 163, 5, 163, 1434, 8, 163, - 10, 163, 12, 163, 1437, 9, 163, 1, 163, 1, 163, - 1, 164, 1, 164, 5, 164, 1443, 8, 164, 10, 164, - 12, 164, 1446, 9, 164, 1, 164, 1, 164, 1, 165, - 1, 165, 3, 165, 1452, 8, 165, 1, 166, 1, 166, - 3, 166, 1456, 8, 166, 1, 166, 1, 166, 1, 166, - 5, 166, 1461, 8, 166, 10, 166, 12, 166, 1464, 9, - 166, 1, 167, 1, 167, 1, 167, 1, 167, 3, 167, - 1470, 8, 167, 1, 168, 1, 168, 1, 168, 5, 168, - 1475, 8, 168, 10, 168, 12, 168, 1478, 9, 168, 1, - 168, 3, 168, 1481, 8, 168, 1, 169, 1, 169, 1, - 169, 1, 169, 3, 169, 1487, 8, 169, 1, 169, 1, - 169, 1, 169, 5, 169, 1492, 8, 169, 10, 169, 12, - 169, 1495, 9, 169, 1, 169, 1, 169, 1, 169, 3, - 169, 1500, 8, 169, 3, 169, 1502, 8, 169, 1, 170, - 1, 170, 3, 170, 1506, 8, 170, 1, 171, 1, 171, - 1, 171, 1, 171, 1, 172, 1, 172, 3, 172, 1514, - 8, 172, 1, 173, 1, 173, 1, 173, 1, 174, 1, - 174, 1, 175, 1, 175, 1, 176, 4, 176, 1524, 8, - 176, 11, 176, 12, 176, 1525, 1, 176, 1, 176, 1, - 177, 1, 177, 5, 177, 1532, 8, 177, 10, 177, 12, - 177, 1535, 9, 177, 1, 177, 1, 177, 1, 178, 1, - 178, 1, 179, 1, 179, 1, 180, 1, 180, 1, 181, - 1, 181, 1, 182, 1, 182, 1, 183, 1, 183, 1, - 184, 1, 184, 1, 185, 1, 185, 1, 186, 1, 186, - 1, 187, 1, 187, 1, 188, 1, 188, 1, 189, 1, - 189, 1, 190, 1, 190, 1, 191, 1, 191, 1, 192, - 1, 192, 1, 193, 1, 193, 1, 194, 1, 194, 1, - 195, 1, 195, 1, 196, 1, 196, 1, 197, 1, 197, - 1, 198, 1, 198, 1, 199, 1, 199, 1, 200, 1, - 200, 1, 201, 1, 201, 1, 202, 1, 202, 1, 203, - 1, 203, 0, 0, 204, 1, 1, 3, 2, 5, 3, - 7, 4, 9, 5, 11, 6, 13, 7, 15, 8, 17, - 9, 19, 10, 21, 11, 23, 12, 25, 13, 27, 14, - 29, 15, 31, 16, 33, 17, 35, 18, 37, 19, 39, - 20, 41, 21, 43, 22, 45, 23, 47, 24, 49, 25, - 51, 26, 53, 27, 55, 28, 57, 29, 59, 30, 61, - 31, 63, 32, 65, 33, 67, 34, 69, 35, 71, 36, - 73, 37, 75, 38, 77, 39, 79, 40, 81, 41, 83, - 42, 85, 43, 87, 44, 89, 45, 91, 46, 93, 47, - 95, 48, 97, 49, 99, 50, 101, 51, 103, 52, 105, - 53, 107, 54, 109, 55, 111, 56, 113, 57, 115, 58, - 117, 59, 119, 60, 121, 61, 123, 62, 125, 63, 127, - 64, 129, 65, 131, 66, 133, 67, 135, 68, 137, 69, - 139, 70, 141, 71, 143, 72, 145, 73, 147, 74, 149, - 75, 151, 76, 153, 77, 155, 78, 157, 79, 159, 80, - 161, 81, 163, 82, 165, 83, 167, 84, 169, 85, 171, - 86, 173, 87, 175, 88, 177, 89, 179, 90, 181, 91, - 183, 92, 185, 93, 187, 94, 189, 95, 191, 96, 193, - 97, 195, 98, 197, 99, 199, 100, 201, 101, 203, 102, - 205, 103, 207, 104, 209, 105, 211, 106, 213, 107, 215, - 108, 217, 109, 219, 110, 221, 111, 223, 112, 225, 113, - 227, 114, 229, 115, 231, 116, 233, 117, 235, 118, 237, - 119, 239, 120, 241, 121, 243, 122, 245, 123, 247, 124, - 249, 125, 251, 126, 253, 127, 255, 128, 257, 129, 259, - 130, 261, 131, 263, 132, 265, 133, 267, 134, 269, 135, - 271, 136, 273, 137, 275, 138, 277, 139, 279, 140, 281, - 141, 283, 142, 285, 143, 287, 144, 289, 145, 291, 146, - 293, 147, 295, 148, 297, 149, 299, 150, 301, 151, 303, - 152, 305, 153, 307, 154, 309, 155, 311, 156, 313, 157, - 315, 158, 317, 159, 319, 160, 321, 161, 323, 162, 325, - 163, 327, 164, 329, 165, 331, 166, 333, 167, 335, 0, - 337, 168, 339, 169, 341, 170, 343, 171, 345, 172, 347, - 173, 349, 0, 351, 0, 353, 174, 355, 175, 357, 0, - 359, 0, 361, 0, 363, 0, 365, 0, 367, 0, 369, - 0, 371, 0, 373, 0, 375, 0, 377, 0, 379, 0, - 381, 0, 383, 0, 385, 0, 387, 0, 389, 0, 391, - 0, 393, 0, 395, 0, 397, 0, 399, 0, 401, 0, - 403, 0, 405, 0, 407, 0, 1, 0, 41, 8, 0, - 0, 32, 34, 34, 60, 60, 62, 62, 92, 92, 94, - 94, 96, 96, 123, 125, 2, 0, 65, 90, 97, 122, - 2, 0, 69, 69, 101, 101, 2, 0, 43, 43, 45, - 45, 4, 0, 10, 10, 13, 13, 39, 39, 92, 92, - 4, 0, 10, 10, 13, 13, 34, 34, 92, 92, 2, - 0, 39, 39, 92, 92, 2, 0, 34, 34, 92, 92, - 8, 0, 34, 34, 39, 39, 92, 92, 98, 98, 102, - 102, 110, 110, 114, 114, 116, 116, 3, 0, 183, 183, - 768, 879, 8255, 8256, 2, 0, 46, 46, 58, 58, 2, - 0, 65, 70, 97, 102, 7, 0, 33, 33, 35, 47, - 59, 59, 61, 61, 63, 64, 95, 95, 126, 126, 13, - 0, 65, 90, 97, 122, 192, 214, 216, 246, 248, 767, - 880, 893, 895, 8191, 8204, 8205, 8304, 8591, 11264, 12271, 12289, - 55295, 63744, 64975, 65008, 65533, 3, 0, 9, 10, 13, 13, - 32, 32, 2, 0, 10, 10, 13, 13, 2, 0, 65, - 65, 97, 97, 2, 0, 66, 66, 98, 98, 2, 0, - 67, 67, 99, 99, 2, 0, 68, 68, 100, 100, 2, - 0, 70, 70, 102, 102, 2, 0, 71, 71, 103, 103, - 2, 0, 72, 72, 104, 104, 2, 0, 73, 73, 105, - 105, 2, 0, 74, 74, 106, 106, 2, 0, 75, 75, - 107, 107, 2, 0, 76, 76, 108, 108, 2, 0, 77, - 77, 109, 109, 2, 0, 78, 78, 110, 110, 2, 0, - 79, 79, 111, 111, 2, 0, 80, 80, 112, 112, 2, - 0, 81, 81, 113, 113, 2, 0, 82, 82, 114, 114, - 2, 0, 83, 83, 115, 115, 2, 0, 84, 84, 116, - 116, 2, 0, 85, 85, 117, 117, 2, 0, 86, 86, - 118, 118, 2, 0, 87, 87, 119, 119, 2, 0, 88, - 88, 120, 120, 2, 0, 89, 89, 121, 121, 2, 0, - 90, 90, 122, 122, 1623, 0, 1, 1, 0, 0, 0, - 0, 3, 1, 0, 0, 0, 0, 5, 1, 0, 0, - 0, 0, 7, 1, 0, 0, 0, 0, 9, 1, 0, - 0, 0, 0, 11, 1, 0, 0, 0, 0, 13, 1, - 0, 0, 0, 0, 15, 1, 0, 0, 0, 0, 17, - 1, 0, 0, 0, 0, 19, 1, 0, 0, 0, 0, - 21, 1, 0, 0, 0, 0, 23, 1, 0, 0, 0, - 0, 25, 1, 0, 0, 0, 0, 27, 1, 0, 0, - 0, 0, 29, 1, 0, 0, 0, 0, 31, 1, 0, - 0, 0, 0, 33, 1, 0, 0, 0, 0, 35, 1, - 0, 0, 0, 0, 37, 1, 0, 0, 0, 0, 39, - 1, 0, 0, 0, 0, 41, 1, 0, 0, 0, 0, - 43, 1, 0, 0, 0, 0, 45, 1, 0, 0, 0, - 0, 47, 1, 0, 0, 0, 0, 49, 1, 0, 0, - 0, 0, 51, 1, 0, 0, 0, 0, 53, 1, 0, - 0, 0, 0, 55, 1, 0, 0, 0, 0, 57, 1, - 0, 0, 0, 0, 59, 1, 0, 0, 0, 0, 61, - 1, 0, 0, 0, 0, 63, 1, 0, 0, 0, 0, - 65, 1, 0, 0, 0, 0, 67, 1, 0, 0, 0, - 0, 69, 1, 0, 0, 0, 0, 71, 1, 0, 0, - 0, 0, 73, 1, 0, 0, 0, 0, 75, 1, 0, - 0, 0, 0, 77, 1, 0, 0, 0, 0, 79, 1, - 0, 0, 0, 0, 81, 1, 0, 0, 0, 0, 83, - 1, 0, 0, 0, 0, 85, 1, 0, 0, 0, 0, - 87, 1, 0, 0, 0, 0, 89, 1, 0, 0, 0, - 0, 91, 1, 0, 0, 0, 0, 93, 1, 0, 0, - 0, 0, 95, 1, 0, 0, 0, 0, 97, 1, 0, - 0, 0, 0, 99, 1, 0, 0, 0, 0, 101, 1, - 0, 0, 0, 0, 103, 1, 0, 0, 0, 0, 105, - 1, 0, 0, 0, 0, 107, 1, 0, 0, 0, 0, - 109, 1, 0, 0, 0, 0, 111, 1, 0, 0, 0, - 0, 113, 1, 0, 0, 0, 0, 115, 1, 0, 0, - 0, 0, 117, 1, 0, 0, 0, 0, 119, 1, 0, - 0, 0, 0, 121, 1, 0, 0, 0, 0, 123, 1, - 0, 0, 0, 0, 125, 1, 0, 0, 0, 0, 127, - 1, 0, 0, 0, 0, 129, 1, 0, 0, 0, 0, - 131, 1, 0, 0, 0, 0, 133, 1, 0, 0, 0, - 0, 135, 1, 0, 0, 0, 0, 137, 1, 0, 0, - 0, 0, 139, 1, 0, 0, 0, 0, 141, 1, 0, - 0, 0, 0, 143, 1, 0, 0, 0, 0, 145, 1, - 0, 0, 0, 0, 147, 1, 0, 0, 0, 0, 149, - 1, 0, 0, 0, 0, 151, 1, 0, 0, 0, 0, - 153, 1, 0, 0, 0, 0, 155, 1, 0, 0, 0, - 0, 157, 1, 0, 0, 0, 0, 159, 1, 0, 0, - 0, 0, 161, 1, 0, 0, 0, 0, 163, 1, 0, - 0, 0, 0, 165, 1, 0, 0, 0, 0, 167, 1, - 0, 0, 0, 0, 169, 1, 0, 0, 0, 0, 171, - 1, 0, 0, 0, 0, 173, 1, 0, 0, 0, 0, - 175, 1, 0, 0, 0, 0, 177, 1, 0, 0, 0, - 0, 179, 1, 0, 0, 0, 0, 181, 1, 0, 0, - 0, 0, 183, 1, 0, 0, 0, 0, 185, 1, 0, - 0, 0, 0, 187, 1, 0, 0, 0, 0, 189, 1, - 0, 0, 0, 0, 191, 1, 0, 0, 0, 0, 193, - 1, 0, 0, 0, 0, 195, 1, 0, 0, 0, 0, - 197, 1, 0, 0, 0, 0, 199, 1, 0, 0, 0, - 0, 201, 1, 0, 0, 0, 0, 203, 1, 0, 0, - 0, 0, 205, 1, 0, 0, 0, 0, 207, 1, 0, - 0, 0, 0, 209, 1, 0, 0, 0, 0, 211, 1, - 0, 0, 0, 0, 213, 1, 0, 0, 0, 0, 215, - 1, 0, 0, 0, 0, 217, 1, 0, 0, 0, 0, - 219, 1, 0, 0, 0, 0, 221, 1, 0, 0, 0, - 0, 223, 1, 0, 0, 0, 0, 225, 1, 0, 0, - 0, 0, 227, 1, 0, 0, 0, 0, 229, 1, 0, - 0, 0, 0, 231, 1, 0, 0, 0, 0, 233, 1, - 0, 0, 0, 0, 235, 1, 0, 0, 0, 0, 237, - 1, 0, 0, 0, 0, 239, 1, 0, 0, 0, 0, - 241, 1, 0, 0, 0, 0, 243, 1, 0, 0, 0, - 0, 245, 1, 0, 0, 0, 0, 247, 1, 0, 0, - 0, 0, 249, 1, 0, 0, 0, 0, 251, 1, 0, - 0, 0, 0, 253, 1, 0, 0, 0, 0, 255, 1, - 0, 0, 0, 0, 257, 1, 0, 0, 0, 0, 259, - 1, 0, 0, 0, 0, 261, 1, 0, 0, 0, 0, - 263, 1, 0, 0, 0, 0, 265, 1, 0, 0, 0, - 0, 267, 1, 0, 0, 0, 0, 269, 1, 0, 0, - 0, 0, 271, 1, 0, 0, 0, 0, 273, 1, 0, - 0, 0, 0, 275, 1, 0, 0, 0, 0, 277, 1, - 0, 0, 0, 0, 279, 1, 0, 0, 0, 0, 281, - 1, 0, 0, 0, 0, 283, 1, 0, 0, 0, 0, - 285, 1, 0, 0, 0, 0, 287, 1, 0, 0, 0, - 0, 289, 1, 0, 0, 0, 0, 291, 1, 0, 0, - 0, 0, 293, 1, 0, 0, 0, 0, 295, 1, 0, - 0, 0, 0, 297, 1, 0, 0, 0, 0, 299, 1, - 0, 0, 0, 0, 301, 1, 0, 0, 0, 0, 303, - 1, 0, 0, 0, 0, 305, 1, 0, 0, 0, 0, - 307, 1, 0, 0, 0, 0, 309, 1, 0, 0, 0, - 0, 311, 1, 0, 0, 0, 0, 313, 1, 0, 0, - 0, 0, 315, 1, 0, 0, 0, 0, 317, 1, 0, - 0, 0, 0, 319, 1, 0, 0, 0, 0, 321, 1, - 0, 0, 0, 0, 323, 1, 0, 0, 0, 0, 325, - 1, 0, 0, 0, 0, 327, 1, 0, 0, 0, 0, - 329, 1, 0, 0, 0, 0, 331, 1, 0, 0, 0, - 0, 333, 1, 0, 0, 0, 0, 337, 1, 0, 0, - 0, 0, 339, 1, 0, 0, 0, 0, 341, 1, 0, - 0, 0, 0, 343, 1, 0, 0, 0, 0, 345, 1, - 0, 0, 0, 0, 347, 1, 0, 0, 0, 0, 353, - 1, 0, 0, 0, 0, 355, 1, 0, 0, 0, 1, - 409, 1, 0, 0, 0, 3, 411, 1, 0, 0, 0, - 5, 413, 1, 0, 0, 0, 7, 415, 1, 0, 0, - 0, 9, 417, 1, 0, 0, 0, 11, 419, 1, 0, - 0, 0, 13, 421, 1, 0, 0, 0, 15, 423, 1, - 0, 0, 0, 17, 425, 1, 0, 0, 0, 19, 427, - 1, 0, 0, 0, 21, 429, 1, 0, 0, 0, 23, - 431, 1, 0, 0, 0, 25, 433, 1, 0, 0, 0, - 27, 435, 1, 0, 0, 0, 29, 437, 1, 0, 0, - 0, 31, 439, 1, 0, 0, 0, 33, 441, 1, 0, - 0, 0, 35, 443, 1, 0, 0, 0, 37, 446, 1, - 0, 0, 0, 39, 449, 1, 0, 0, 0, 41, 451, - 1, 0, 0, 0, 43, 454, 1, 0, 0, 0, 45, - 456, 1, 0, 0, 0, 47, 458, 1, 0, 0, 0, - 49, 461, 1, 0, 0, 0, 51, 464, 1, 0, 0, - 0, 53, 466, 1, 0, 0, 0, 55, 469, 1, 0, - 0, 0, 57, 474, 1, 0, 0, 0, 59, 480, 1, - 0, 0, 0, 61, 485, 1, 0, 0, 0, 63, 492, - 1, 0, 0, 0, 65, 499, 1, 0, 0, 0, 67, - 508, 1, 0, 0, 0, 69, 516, 1, 0, 0, 0, - 71, 519, 1, 0, 0, 0, 73, 529, 1, 0, 0, - 0, 75, 535, 1, 0, 0, 0, 77, 544, 1, 0, - 0, 0, 79, 548, 1, 0, 0, 0, 81, 553, 1, - 0, 0, 0, 83, 559, 1, 0, 0, 0, 85, 572, - 1, 0, 0, 0, 87, 585, 1, 0, 0, 0, 89, - 592, 1, 0, 0, 0, 91, 605, 1, 0, 0, 0, - 93, 630, 1, 0, 0, 0, 95, 634, 1, 0, 0, - 0, 97, 639, 1, 0, 0, 0, 99, 645, 1, 0, - 0, 0, 101, 652, 1, 0, 0, 0, 103, 662, 1, - 0, 0, 0, 105, 669, 1, 0, 0, 0, 107, 674, - 1, 0, 0, 0, 109, 681, 1, 0, 0, 0, 111, - 686, 1, 0, 0, 0, 113, 692, 1, 0, 0, 0, - 115, 697, 1, 0, 0, 0, 117, 704, 1, 0, 0, - 0, 119, 708, 1, 0, 0, 0, 121, 711, 1, 0, - 0, 0, 123, 716, 1, 0, 0, 0, 125, 721, 1, - 0, 0, 0, 127, 726, 1, 0, 0, 0, 129, 733, - 1, 0, 0, 0, 131, 740, 1, 0, 0, 0, 133, - 745, 1, 0, 0, 0, 135, 751, 1, 0, 0, 0, - 137, 759, 1, 0, 0, 0, 139, 765, 1, 0, 0, - 0, 141, 769, 1, 0, 0, 0, 143, 778, 1, 0, - 0, 0, 145, 786, 1, 0, 0, 0, 147, 791, 1, - 0, 0, 0, 149, 797, 1, 0, 0, 0, 151, 803, - 1, 0, 0, 0, 153, 809, 1, 0, 0, 0, 155, - 816, 1, 0, 0, 0, 157, 820, 1, 0, 0, 0, - 159, 823, 1, 0, 0, 0, 161, 827, 1, 0, 0, - 0, 163, 832, 1, 0, 0, 0, 165, 844, 1, 0, - 0, 0, 167, 853, 1, 0, 0, 0, 169, 859, 1, - 0, 0, 0, 171, 863, 1, 0, 0, 0, 173, 867, - 1, 0, 0, 0, 175, 873, 1, 0, 0, 0, 177, - 878, 1, 0, 0, 0, 179, 882, 1, 0, 0, 0, - 181, 887, 1, 0, 0, 0, 183, 893, 1, 0, 0, - 0, 185, 899, 1, 0, 0, 0, 187, 906, 1, 0, - 0, 0, 189, 913, 1, 0, 0, 0, 191, 919, 1, - 0, 0, 0, 193, 925, 1, 0, 0, 0, 195, 940, - 1, 0, 0, 0, 197, 944, 1, 0, 0, 0, 199, - 953, 1, 0, 0, 0, 201, 963, 1, 0, 0, 0, - 203, 971, 1, 0, 0, 0, 205, 981, 1, 0, 0, - 0, 207, 990, 1, 0, 0, 0, 209, 995, 1, 0, - 0, 0, 211, 1001, 1, 0, 0, 0, 213, 1005, 1, - 0, 0, 0, 215, 1011, 1, 0, 0, 0, 217, 1019, - 1, 0, 0, 0, 219, 1027, 1, 0, 0, 0, 221, - 1036, 1, 0, 0, 0, 223, 1039, 1, 0, 0, 0, - 225, 1043, 1, 0, 0, 0, 227, 1048, 1, 0, 0, - 0, 229, 1056, 1, 0, 0, 0, 231, 1061, 1, 0, - 0, 0, 233, 1068, 1, 0, 0, 0, 235, 1075, 1, - 0, 0, 0, 237, 1082, 1, 0, 0, 0, 239, 1086, - 1, 0, 0, 0, 241, 1095, 1, 0, 0, 0, 243, - 1098, 1, 0, 0, 0, 245, 1106, 1, 0, 0, 0, - 247, 1112, 1, 0, 0, 0, 249, 1121, 1, 0, 0, - 0, 251, 1127, 1, 0, 0, 0, 253, 1133, 1, 0, - 0, 0, 255, 1141, 1, 0, 0, 0, 257, 1151, 1, - 0, 0, 0, 259, 1161, 1, 0, 0, 0, 261, 1167, - 1, 0, 0, 0, 263, 1174, 1, 0, 0, 0, 265, - 1182, 1, 0, 0, 0, 267, 1189, 1, 0, 0, 0, - 269, 1195, 1, 0, 0, 0, 271, 1199, 1, 0, 0, - 0, 273, 1203, 1, 0, 0, 0, 275, 1207, 1, 0, - 0, 0, 277, 1211, 1, 0, 0, 0, 279, 1218, 1, - 0, 0, 0, 281, 1228, 1, 0, 0, 0, 283, 1238, - 1, 0, 0, 0, 285, 1242, 1, 0, 0, 0, 287, - 1245, 1, 0, 0, 0, 289, 1262, 1, 0, 0, 0, - 291, 1265, 1, 0, 0, 0, 293, 1268, 1, 0, 0, - 0, 295, 1286, 1, 0, 0, 0, 297, 1290, 1, 0, - 0, 0, 299, 1297, 1, 0, 0, 0, 301, 1335, 1, - 0, 0, 0, 303, 1337, 1, 0, 0, 0, 305, 1340, - 1, 0, 0, 0, 307, 1343, 1, 0, 0, 0, 309, - 1346, 1, 0, 0, 0, 311, 1349, 1, 0, 0, 0, - 313, 1352, 1, 0, 0, 0, 315, 1355, 1, 0, 0, - 0, 317, 1364, 1, 0, 0, 0, 319, 1374, 1, 0, - 0, 0, 321, 1384, 1, 0, 0, 0, 323, 1406, 1, - 0, 0, 0, 325, 1428, 1, 0, 0, 0, 327, 1431, - 1, 0, 0, 0, 329, 1440, 1, 0, 0, 0, 331, - 1451, 1, 0, 0, 0, 333, 1455, 1, 0, 0, 0, - 335, 1469, 1, 0, 0, 0, 337, 1471, 1, 0, 0, - 0, 339, 1486, 1, 0, 0, 0, 341, 1505, 1, 0, - 0, 0, 343, 1507, 1, 0, 0, 0, 345, 1513, 1, - 0, 0, 0, 347, 1515, 1, 0, 0, 0, 349, 1518, - 1, 0, 0, 0, 351, 1520, 1, 0, 0, 0, 353, - 1523, 1, 0, 0, 0, 355, 1529, 1, 0, 0, 0, - 357, 1538, 1, 0, 0, 0, 359, 1540, 1, 0, 0, - 0, 361, 1542, 1, 0, 0, 0, 363, 1544, 1, 0, - 0, 0, 365, 1546, 1, 0, 0, 0, 367, 1548, 1, - 0, 0, 0, 369, 1550, 1, 0, 0, 0, 371, 1552, - 1, 0, 0, 0, 373, 1554, 1, 0, 0, 0, 375, - 1556, 1, 0, 0, 0, 377, 1558, 1, 0, 0, 0, - 379, 1560, 1, 0, 0, 0, 381, 1562, 1, 0, 0, - 0, 383, 1564, 1, 0, 0, 0, 385, 1566, 1, 0, - 0, 0, 387, 1568, 1, 0, 0, 0, 389, 1570, 1, - 0, 0, 0, 391, 1572, 1, 0, 0, 0, 393, 1574, - 1, 0, 0, 0, 395, 1576, 1, 0, 0, 0, 397, - 1578, 1, 0, 0, 0, 399, 1580, 1, 0, 0, 0, - 401, 1582, 1, 0, 0, 0, 403, 1584, 1, 0, 0, - 0, 405, 1586, 1, 0, 0, 0, 407, 1588, 1, 0, - 0, 0, 409, 410, 5, 42, 0, 0, 410, 2, 1, - 0, 0, 0, 411, 412, 5, 40, 0, 0, 412, 4, - 1, 0, 0, 0, 413, 414, 5, 41, 0, 0, 414, - 6, 1, 0, 0, 0, 415, 416, 5, 123, 0, 0, - 416, 8, 1, 0, 0, 0, 417, 418, 5, 125, 0, - 0, 418, 10, 1, 0, 0, 0, 419, 420, 5, 59, - 0, 0, 420, 12, 1, 0, 0, 0, 421, 422, 5, - 46, 0, 0, 422, 14, 1, 0, 0, 0, 423, 424, - 5, 44, 0, 0, 424, 16, 1, 0, 0, 0, 425, - 426, 5, 97, 0, 0, 426, 18, 1, 0, 0, 0, - 427, 428, 5, 124, 0, 0, 428, 20, 1, 0, 0, - 0, 429, 430, 5, 47, 0, 0, 430, 22, 1, 0, - 0, 0, 431, 432, 5, 94, 0, 0, 432, 24, 1, - 0, 0, 0, 433, 434, 5, 43, 0, 0, 434, 26, - 1, 0, 0, 0, 435, 436, 5, 63, 0, 0, 436, - 28, 1, 0, 0, 0, 437, 438, 5, 33, 0, 0, - 438, 30, 1, 0, 0, 0, 439, 440, 5, 91, 0, - 0, 440, 32, 1, 0, 0, 0, 441, 442, 5, 93, - 0, 0, 442, 34, 1, 0, 0, 0, 443, 444, 5, - 124, 0, 0, 444, 445, 5, 124, 0, 0, 445, 36, - 1, 0, 0, 0, 446, 447, 5, 38, 0, 0, 447, - 448, 5, 38, 0, 0, 448, 38, 1, 0, 0, 0, - 449, 450, 5, 61, 0, 0, 450, 40, 1, 0, 0, - 0, 451, 452, 5, 33, 0, 0, 452, 453, 5, 61, - 0, 0, 453, 42, 1, 0, 0, 0, 454, 455, 5, - 60, 0, 0, 455, 44, 1, 0, 0, 0, 456, 457, - 5, 62, 0, 0, 457, 46, 1, 0, 0, 0, 458, - 459, 5, 60, 0, 0, 459, 460, 5, 61, 0, 0, - 460, 48, 1, 0, 0, 0, 461, 462, 5, 62, 0, - 0, 462, 463, 5, 61, 0, 0, 463, 50, 1, 0, - 0, 0, 464, 465, 5, 45, 0, 0, 465, 52, 1, - 0, 0, 0, 466, 467, 5, 94, 0, 0, 467, 468, - 5, 94, 0, 0, 468, 54, 1, 0, 0, 0, 469, - 470, 5, 116, 0, 0, 470, 471, 5, 114, 0, 0, - 471, 472, 5, 117, 0, 0, 472, 473, 5, 101, 0, - 0, 473, 56, 1, 0, 0, 0, 474, 475, 5, 102, - 0, 0, 475, 476, 5, 97, 0, 0, 476, 477, 5, - 108, 0, 0, 477, 478, 5, 115, 0, 0, 478, 479, - 5, 101, 0, 0, 479, 58, 1, 0, 0, 0, 480, - 481, 3, 359, 179, 0, 481, 482, 3, 357, 178, 0, - 482, 483, 3, 393, 196, 0, 483, 484, 3, 365, 182, - 0, 484, 60, 1, 0, 0, 0, 485, 486, 3, 387, - 193, 0, 486, 487, 3, 391, 195, 0, 487, 488, 3, - 365, 182, 0, 488, 489, 3, 367, 183, 0, 489, 490, - 3, 373, 186, 0, 490, 491, 3, 403, 201, 0, 491, - 62, 1, 0, 0, 0, 492, 493, 3, 393, 196, 0, - 493, 494, 3, 365, 182, 0, 494, 495, 3, 379, 189, - 0, 495, 496, 3, 365, 182, 0, 496, 497, 3, 361, - 180, 0, 497, 498, 3, 395, 197, 0, 498, 64, 1, - 0, 0, 0, 499, 500, 3, 363, 181, 0, 500, 501, - 3, 373, 186, 0, 501, 502, 3, 393, 196, 0, 502, - 503, 3, 395, 197, 0, 503, 504, 3, 373, 186, 0, - 504, 505, 3, 383, 191, 0, 505, 506, 3, 361, 180, - 0, 506, 507, 3, 395, 197, 0, 507, 66, 1, 0, - 0, 0, 508, 509, 3, 391, 195, 0, 509, 510, 3, - 365, 182, 0, 510, 511, 3, 363, 181, 0, 511, 512, - 3, 397, 198, 0, 512, 513, 3, 361, 180, 0, 513, - 514, 3, 365, 182, 0, 514, 515, 3, 363, 181, 0, - 515, 68, 1, 0, 0, 0, 516, 517, 3, 357, 178, - 0, 517, 518, 3, 393, 196, 0, 518, 70, 1, 0, - 0, 0, 519, 520, 3, 361, 180, 0, 520, 521, 3, - 385, 192, 0, 521, 522, 3, 383, 191, 0, 522, 523, - 3, 393, 196, 0, 523, 524, 3, 395, 197, 0, 524, - 525, 3, 391, 195, 0, 525, 526, 3, 397, 198, 0, - 526, 527, 3, 361, 180, 0, 527, 528, 3, 395, 197, - 0, 528, 72, 1, 0, 0, 0, 529, 530, 3, 401, - 200, 0, 530, 531, 3, 371, 185, 0, 531, 532, 3, - 365, 182, 0, 532, 533, 3, 391, 195, 0, 533, 534, - 3, 365, 182, 0, 534, 74, 1, 0, 0, 0, 535, - 536, 3, 363, 181, 0, 536, 537, 3, 365, 182, 0, - 537, 538, 3, 393, 196, 0, 538, 539, 3, 361, 180, - 0, 539, 540, 3, 391, 195, 0, 540, 541, 3, 373, - 186, 0, 541, 542, 3, 359, 179, 0, 542, 543, 3, - 365, 182, 0, 543, 76, 1, 0, 0, 0, 544, 545, - 3, 357, 178, 0, 545, 546, 3, 393, 196, 0, 546, - 547, 3, 377, 188, 0, 547, 78, 1, 0, 0, 0, - 548, 549, 3, 367, 183, 0, 549, 550, 3, 391, 195, - 0, 550, 551, 3, 385, 192, 0, 551, 552, 3, 381, - 190, 0, 552, 80, 1, 0, 0, 0, 553, 554, 3, - 383, 191, 0, 554, 555, 3, 357, 178, 0, 555, 556, - 3, 381, 190, 0, 556, 557, 3, 365, 182, 0, 557, - 558, 3, 363, 181, 0, 558, 82, 1, 0, 0, 0, - 559, 560, 3, 369, 184, 0, 560, 561, 3, 391, 195, - 0, 561, 562, 3, 385, 192, 0, 562, 563, 3, 397, - 198, 0, 563, 565, 3, 387, 193, 0, 564, 566, 3, - 353, 176, 0, 565, 564, 1, 0, 0, 0, 566, 567, - 1, 0, 0, 0, 567, 565, 1, 0, 0, 0, 567, - 568, 1, 0, 0, 0, 568, 569, 1, 0, 0, 0, - 569, 570, 3, 359, 179, 0, 570, 571, 3, 405, 202, - 0, 571, 84, 1, 0, 0, 0, 572, 573, 3, 369, - 184, 0, 573, 574, 3, 391, 195, 0, 574, 575, 3, - 385, 192, 0, 575, 576, 3, 397, 198, 0, 576, 577, - 3, 387, 193, 0, 577, 578, 5, 95, 0, 0, 578, - 579, 3, 361, 180, 0, 579, 580, 3, 385, 192, 0, - 580, 581, 3, 383, 191, 0, 581, 582, 3, 361, 180, - 0, 582, 583, 3, 357, 178, 0, 583, 584, 3, 395, - 197, 0, 584, 86, 1, 0, 0, 0, 585, 586, 3, - 371, 185, 0, 586, 587, 3, 357, 178, 0, 587, 588, - 3, 399, 199, 0, 588, 589, 3, 373, 186, 0, 589, - 590, 3, 383, 191, 0, 590, 591, 3, 369, 184, 0, - 591, 88, 1, 0, 0, 0, 592, 593, 3, 385, 192, - 0, 593, 594, 3, 391, 195, 0, 594, 595, 3, 363, - 181, 0, 595, 596, 3, 365, 182, 0, 596, 598, 3, - 391, 195, 0, 597, 599, 3, 353, 176, 0, 598, 597, - 1, 0, 0, 0, 599, 600, 1, 0, 0, 0, 600, - 598, 1, 0, 0, 0, 600, 601, 1, 0, 0, 0, - 601, 602, 1, 0, 0, 0, 602, 603, 3, 359, 179, - 0, 603, 604, 3, 405, 202, 0, 604, 90, 1, 0, - 0, 0, 605, 606, 3, 373, 186, 0, 606, 607, 3, - 383, 191, 0, 607, 608, 3, 395, 197, 0, 608, 609, - 3, 365, 182, 0, 609, 610, 3, 391, 195, 0, 610, - 611, 3, 383, 191, 0, 611, 612, 3, 357, 178, 0, - 612, 614, 3, 379, 189, 0, 613, 615, 3, 353, 176, - 0, 614, 613, 1, 0, 0, 0, 615, 616, 1, 0, - 0, 0, 616, 614, 1, 0, 0, 0, 616, 617, 1, - 0, 0, 0, 617, 618, 1, 0, 0, 0, 618, 619, - 3, 393, 196, 0, 619, 620, 3, 385, 192, 0, 620, - 621, 3, 391, 195, 0, 621, 623, 3, 395, 197, 0, - 622, 624, 3, 353, 176, 0, 623, 622, 1, 0, 0, - 0, 624, 625, 1, 0, 0, 0, 625, 623, 1, 0, - 0, 0, 625, 626, 1, 0, 0, 0, 626, 627, 1, - 0, 0, 0, 627, 628, 3, 359, 179, 0, 628, 629, - 3, 405, 202, 0, 629, 92, 1, 0, 0, 0, 630, - 631, 3, 357, 178, 0, 631, 632, 3, 393, 196, 0, - 632, 633, 3, 361, 180, 0, 633, 94, 1, 0, 0, - 0, 634, 635, 3, 363, 181, 0, 635, 636, 3, 365, - 182, 0, 636, 637, 3, 393, 196, 0, 637, 638, 3, - 361, 180, 0, 638, 96, 1, 0, 0, 0, 639, 640, - 3, 379, 189, 0, 640, 641, 3, 373, 186, 0, 641, - 642, 3, 381, 190, 0, 642, 643, 3, 373, 186, 0, - 643, 644, 3, 395, 197, 0, 644, 98, 1, 0, 0, - 0, 645, 646, 3, 385, 192, 0, 646, 647, 3, 367, - 183, 0, 647, 648, 3, 367, 183, 0, 648, 649, 3, - 393, 196, 0, 649, 650, 3, 365, 182, 0, 650, 651, - 3, 395, 197, 0, 651, 100, 1, 0, 0, 0, 652, - 653, 3, 395, 197, 0, 653, 654, 3, 365, 182, 0, - 654, 655, 3, 403, 201, 0, 655, 656, 3, 395, 197, - 0, 656, 657, 3, 379, 189, 0, 657, 658, 3, 373, - 186, 0, 658, 659, 3, 381, 190, 0, 659, 660, 3, - 373, 186, 0, 660, 661, 3, 395, 197, 0, 661, 102, - 1, 0, 0, 0, 662, 663, 3, 399, 199, 0, 663, - 664, 3, 357, 178, 0, 664, 665, 3, 379, 189, 0, - 665, 666, 3, 397, 198, 0, 666, 667, 3, 365, 182, - 0, 667, 668, 3, 393, 196, 0, 668, 104, 1, 0, - 0, 0, 669, 670, 3, 379, 189, 0, 670, 671, 3, - 385, 192, 0, 671, 672, 3, 357, 178, 0, 672, 673, - 3, 363, 181, 0, 673, 106, 1, 0, 0, 0, 674, - 675, 3, 393, 196, 0, 675, 676, 3, 373, 186, 0, - 676, 677, 3, 379, 189, 0, 677, 678, 3, 365, 182, - 0, 678, 679, 3, 383, 191, 0, 679, 680, 3, 395, - 197, 0, 680, 108, 1, 0, 0, 0, 681, 682, 3, - 373, 186, 0, 682, 683, 3, 383, 191, 0, 683, 684, - 3, 395, 197, 0, 684, 685, 3, 385, 192, 0, 685, - 110, 1, 0, 0, 0, 686, 687, 3, 361, 180, 0, - 687, 688, 3, 379, 189, 0, 688, 689, 3, 365, 182, - 0, 689, 690, 3, 357, 178, 0, 690, 691, 3, 391, - 195, 0, 691, 112, 1, 0, 0, 0, 692, 693, 3, - 363, 181, 0, 693, 694, 3, 391, 195, 0, 694, 695, - 3, 385, 192, 0, 695, 696, 3, 387, 193, 0, 696, - 114, 1, 0, 0, 0, 697, 698, 3, 361, 180, 0, - 698, 699, 3, 391, 195, 0, 699, 700, 3, 365, 182, - 0, 700, 701, 3, 357, 178, 0, 701, 702, 3, 395, - 197, 0, 702, 703, 3, 365, 182, 0, 703, 116, 1, - 0, 0, 0, 704, 705, 3, 357, 178, 0, 705, 706, - 3, 363, 181, 0, 706, 707, 3, 363, 181, 0, 707, - 118, 1, 0, 0, 0, 708, 709, 3, 395, 197, 0, - 709, 710, 3, 385, 192, 0, 710, 120, 1, 0, 0, - 0, 711, 712, 3, 363, 181, 0, 712, 713, 3, 357, - 178, 0, 713, 714, 3, 395, 197, 0, 714, 715, 3, - 357, 178, 0, 715, 122, 1, 0, 0, 0, 716, 717, - 3, 381, 190, 0, 717, 718, 3, 385, 192, 0, 718, - 719, 3, 399, 199, 0, 719, 720, 3, 365, 182, 0, - 720, 124, 1, 0, 0, 0, 721, 722, 3, 361, 180, - 0, 722, 723, 3, 385, 192, 0, 723, 724, 3, 387, - 193, 0, 724, 725, 3, 405, 202, 0, 725, 126, 1, - 0, 0, 0, 726, 727, 3, 373, 186, 0, 727, 728, - 3, 383, 191, 0, 728, 729, 3, 393, 196, 0, 729, - 730, 3, 365, 182, 0, 730, 731, 3, 391, 195, 0, - 731, 732, 3, 395, 197, 0, 732, 128, 1, 0, 0, - 0, 733, 734, 3, 363, 181, 0, 734, 735, 3, 365, - 182, 0, 735, 736, 3, 379, 189, 0, 736, 737, 3, - 365, 182, 0, 737, 738, 3, 395, 197, 0, 738, 739, - 3, 365, 182, 0, 739, 130, 1, 0, 0, 0, 740, - 741, 3, 401, 200, 0, 741, 742, 3, 373, 186, 0, - 742, 743, 3, 395, 197, 0, 743, 744, 3, 371, 185, - 0, 744, 132, 1, 0, 0, 0, 745, 746, 3, 397, - 198, 0, 746, 747, 3, 393, 196, 0, 747, 748, 3, - 373, 186, 0, 748, 749, 3, 383, 191, 0, 749, 750, - 3, 369, 184, 0, 750, 134, 1, 0, 0, 0, 751, - 752, 3, 363, 181, 0, 752, 753, 3, 365, 182, 0, - 753, 754, 3, 367, 183, 0, 754, 755, 3, 357, 178, - 0, 755, 756, 3, 397, 198, 0, 756, 757, 3, 379, - 189, 0, 757, 758, 3, 395, 197, 0, 758, 136, 1, - 0, 0, 0, 759, 760, 3, 369, 184, 0, 760, 761, - 3, 391, 195, 0, 761, 762, 3, 357, 178, 0, 762, - 763, 3, 387, 193, 0, 763, 764, 3, 371, 185, 0, - 764, 138, 1, 0, 0, 0, 765, 766, 3, 357, 178, - 0, 766, 767, 3, 379, 189, 0, 767, 768, 3, 379, - 189, 0, 768, 140, 1, 0, 0, 0, 769, 770, 3, - 385, 192, 0, 770, 771, 3, 387, 193, 0, 771, 772, - 3, 395, 197, 0, 772, 773, 3, 373, 186, 0, 773, - 774, 3, 385, 192, 0, 774, 775, 3, 383, 191, 0, - 775, 776, 3, 357, 178, 0, 776, 777, 3, 379, 189, - 0, 777, 142, 1, 0, 0, 0, 778, 779, 3, 393, - 196, 0, 779, 780, 3, 365, 182, 0, 780, 781, 3, - 391, 195, 0, 781, 782, 3, 399, 199, 0, 782, 783, - 3, 373, 186, 0, 783, 784, 3, 361, 180, 0, 784, - 785, 3, 365, 182, 0, 785, 144, 1, 0, 0, 0, - 786, 787, 3, 359, 179, 0, 787, 788, 3, 373, 186, - 0, 788, 789, 3, 383, 191, 0, 789, 790, 3, 363, - 181, 0, 790, 146, 1, 0, 0, 0, 791, 792, 3, - 397, 198, 0, 792, 793, 3, 383, 191, 0, 793, 794, - 3, 363, 181, 0, 794, 795, 3, 365, 182, 0, 795, - 796, 3, 367, 183, 0, 796, 148, 1, 0, 0, 0, - 797, 798, 3, 381, 190, 0, 798, 799, 3, 373, 186, - 0, 799, 800, 3, 383, 191, 0, 800, 801, 3, 397, - 198, 0, 801, 802, 3, 393, 196, 0, 802, 150, 1, - 0, 0, 0, 803, 804, 3, 397, 198, 0, 804, 805, - 3, 383, 191, 0, 805, 806, 3, 373, 186, 0, 806, - 807, 3, 385, 192, 0, 807, 808, 3, 383, 191, 0, - 808, 152, 1, 0, 0, 0, 809, 810, 3, 367, 183, - 0, 810, 811, 3, 373, 186, 0, 811, 812, 3, 379, - 189, 0, 812, 813, 3, 395, 197, 0, 813, 814, 3, - 365, 182, 0, 814, 815, 3, 391, 195, 0, 815, 154, - 1, 0, 0, 0, 816, 817, 3, 383, 191, 0, 817, - 818, 3, 385, 192, 0, 818, 819, 3, 395, 197, 0, - 819, 156, 1, 0, 0, 0, 820, 821, 3, 373, 186, - 0, 821, 822, 3, 383, 191, 0, 822, 158, 1, 0, - 0, 0, 823, 824, 3, 393, 196, 0, 824, 825, 3, - 395, 197, 0, 825, 826, 3, 391, 195, 0, 826, 160, - 1, 0, 0, 0, 827, 828, 3, 379, 189, 0, 828, - 829, 3, 357, 178, 0, 829, 830, 3, 383, 191, 0, - 830, 831, 3, 369, 184, 0, 831, 162, 1, 0, 0, - 0, 832, 833, 3, 379, 189, 0, 833, 834, 3, 357, - 178, 0, 834, 835, 3, 383, 191, 0, 835, 836, 3, - 369, 184, 0, 836, 837, 3, 381, 190, 0, 837, 838, - 3, 357, 178, 0, 838, 839, 3, 395, 197, 0, 839, - 840, 3, 361, 180, 0, 840, 841, 3, 371, 185, 0, - 841, 842, 3, 365, 182, 0, 842, 843, 3, 393, 196, - 0, 843, 164, 1, 0, 0, 0, 844, 845, 3, 363, - 181, 0, 845, 846, 3, 357, 178, 0, 846, 847, 3, - 395, 197, 0, 847, 848, 3, 357, 178, 0, 848, 849, - 3, 395, 197, 0, 849, 850, 3, 405, 202, 0, 850, - 851, 3, 387, 193, 0, 851, 852, 3, 365, 182, 0, - 852, 166, 1, 0, 0, 0, 853, 854, 3, 359, 179, - 0, 854, 855, 3, 385, 192, 0, 855, 856, 3, 397, - 198, 0, 856, 857, 3, 383, 191, 0, 857, 858, 3, - 363, 181, 0, 858, 168, 1, 0, 0, 0, 859, 860, - 3, 373, 186, 0, 860, 861, 3, 391, 195, 0, 861, - 862, 3, 373, 186, 0, 862, 170, 1, 0, 0, 0, - 863, 864, 3, 397, 198, 0, 864, 865, 3, 391, 195, - 0, 865, 866, 3, 373, 186, 0, 866, 172, 1, 0, - 0, 0, 867, 868, 3, 359, 179, 0, 868, 869, 3, - 383, 191, 0, 869, 870, 3, 385, 192, 0, 870, 871, - 3, 363, 181, 0, 871, 872, 3, 365, 182, 0, 872, - 174, 1, 0, 0, 0, 873, 874, 3, 391, 195, 0, - 874, 875, 3, 357, 178, 0, 875, 876, 3, 383, 191, - 0, 876, 877, 3, 363, 181, 0, 877, 176, 1, 0, - 0, 0, 878, 879, 3, 357, 178, 0, 879, 880, 3, - 359, 179, 0, 880, 881, 3, 393, 196, 0, 881, 178, - 1, 0, 0, 0, 882, 883, 3, 361, 180, 0, 883, - 884, 3, 365, 182, 0, 884, 885, 3, 373, 186, 0, - 885, 886, 3, 379, 189, 0, 886, 180, 1, 0, 0, - 0, 887, 888, 3, 367, 183, 0, 888, 889, 3, 379, - 189, 0, 889, 890, 3, 385, 192, 0, 890, 891, 3, - 385, 192, 0, 891, 892, 3, 391, 195, 0, 892, 182, - 1, 0, 0, 0, 893, 894, 3, 391, 195, 0, 894, - 895, 3, 385, 192, 0, 895, 896, 3, 397, 198, 0, - 896, 897, 3, 383, 191, 0, 897, 898, 3, 363, 181, - 0, 898, 184, 1, 0, 0, 0, 899, 900, 3, 361, - 180, 0, 900, 901, 3, 385, 192, 0, 901, 902, 3, - 383, 191, 0, 902, 903, 3, 361, 180, 0, 903, 904, - 3, 357, 178, 0, 904, 905, 3, 395, 197, 0, 905, - 186, 1, 0, 0, 0, 906, 907, 3, 393, 196, 0, - 907, 908, 3, 395, 197, 0, 908, 909, 3, 391, 195, - 0, 909, 910, 3, 379, 189, 0, 910, 911, 3, 365, - 182, 0, 911, 912, 3, 383, 191, 0, 912, 188, 1, - 0, 0, 0, 913, 914, 3, 397, 198, 0, 914, 915, - 3, 361, 180, 0, 915, 916, 3, 357, 178, 0, 916, - 917, 3, 393, 196, 0, 917, 918, 3, 365, 182, 0, - 918, 190, 1, 0, 0, 0, 919, 920, 3, 379, 189, - 0, 920, 921, 3, 361, 180, 0, 921, 922, 3, 357, - 178, 0, 922, 923, 3, 393, 196, 0, 923, 924, 3, - 365, 182, 0, 924, 192, 1, 0, 0, 0, 925, 926, - 3, 365, 182, 0, 926, 927, 3, 383, 191, 0, 927, - 928, 3, 361, 180, 0, 928, 929, 3, 385, 192, 0, - 929, 930, 3, 363, 181, 0, 930, 931, 3, 365, 182, - 0, 931, 932, 5, 95, 0, 0, 932, 933, 3, 367, - 183, 0, 933, 934, 3, 385, 192, 0, 934, 935, 3, - 391, 195, 0, 935, 936, 5, 95, 0, 0, 936, 937, - 3, 397, 198, 0, 937, 938, 3, 391, 195, 0, 938, - 939, 3, 373, 186, 0, 939, 194, 1, 0, 0, 0, - 940, 941, 3, 367, 183, 0, 941, 942, 3, 385, 192, - 0, 942, 943, 3, 391, 195, 0, 943, 196, 1, 0, - 0, 0, 944, 945, 3, 361, 180, 0, 945, 946, 3, - 385, 192, 0, 946, 947, 3, 383, 191, 0, 947, 948, - 3, 395, 197, 0, 948, 949, 3, 357, 178, 0, 949, - 950, 3, 373, 186, 0, 950, 951, 3, 383, 191, 0, - 951, 952, 3, 393, 196, 0, 952, 198, 1, 0, 0, - 0, 953, 954, 3, 393, 196, 0, 954, 955, 3, 395, - 197, 0, 955, 956, 3, 391, 195, 0, 956, 957, 3, - 393, 196, 0, 957, 958, 3, 395, 197, 0, 958, 959, - 3, 357, 178, 0, 959, 960, 3, 391, 195, 0, 960, - 961, 3, 395, 197, 0, 961, 962, 3, 393, 196, 0, - 962, 200, 1, 0, 0, 0, 963, 964, 3, 393, 196, - 0, 964, 965, 3, 395, 197, 0, 965, 966, 3, 391, - 195, 0, 966, 967, 3, 365, 182, 0, 967, 968, 3, - 383, 191, 0, 968, 969, 3, 363, 181, 0, 969, 970, - 3, 393, 196, 0, 970, 202, 1, 0, 0, 0, 971, - 972, 3, 393, 196, 0, 972, 973, 3, 395, 197, 0, - 973, 974, 3, 391, 195, 0, 974, 975, 3, 359, 179, - 0, 975, 976, 3, 365, 182, 0, 976, 977, 3, 367, - 183, 0, 977, 978, 3, 385, 192, 0, 978, 979, 3, - 391, 195, 0, 979, 980, 3, 365, 182, 0, 980, 204, - 1, 0, 0, 0, 981, 982, 3, 393, 196, 0, 982, - 983, 3, 395, 197, 0, 983, 984, 3, 391, 195, 0, - 984, 985, 3, 357, 178, 0, 985, 986, 3, 367, 183, - 0, 986, 987, 3, 395, 197, 0, 987, 988, 3, 365, - 182, 0, 988, 989, 3, 391, 195, 0, 989, 206, 1, - 0, 0, 0, 990, 991, 3, 405, 202, 0, 991, 992, - 3, 365, 182, 0, 992, 993, 3, 357, 178, 0, 993, - 994, 3, 391, 195, 0, 994, 208, 1, 0, 0, 0, - 995, 996, 3, 381, 190, 0, 996, 997, 3, 385, 192, - 0, 997, 998, 3, 383, 191, 0, 998, 999, 3, 395, - 197, 0, 999, 1000, 3, 371, 185, 0, 1000, 210, 1, - 0, 0, 0, 1001, 1002, 3, 363, 181, 0, 1002, 1003, - 3, 357, 178, 0, 1003, 1004, 3, 405, 202, 0, 1004, - 212, 1, 0, 0, 0, 1005, 1006, 3, 371, 185, 0, - 1006, 1007, 3, 385, 192, 0, 1007, 1008, 3, 397, 198, - 0, 1008, 1009, 3, 391, 195, 0, 1009, 1010, 3, 393, - 196, 0, 1010, 214, 1, 0, 0, 0, 1011, 1012, 3, - 381, 190, 0, 1012, 1013, 3, 373, 186, 0, 1013, 1014, - 3, 383, 191, 0, 1014, 1015, 3, 397, 198, 0, 1015, - 1016, 3, 395, 197, 0, 1016, 1017, 3, 365, 182, 0, - 1017, 1018, 3, 393, 196, 0, 1018, 216, 1, 0, 0, - 0, 1019, 1020, 3, 393, 196, 0, 1020, 1021, 3, 365, - 182, 0, 1021, 1022, 3, 361, 180, 0, 1022, 1023, 3, - 385, 192, 0, 1023, 1024, 3, 383, 191, 0, 1024, 1025, - 3, 363, 181, 0, 1025, 1026, 3, 393, 196, 0, 1026, - 218, 1, 0, 0, 0, 1027, 1028, 3, 395, 197, 0, - 1028, 1029, 3, 373, 186, 0, 1029, 1030, 3, 381, 190, - 0, 1030, 1031, 3, 365, 182, 0, 1031, 1032, 3, 407, - 203, 0, 1032, 1033, 3, 385, 192, 0, 1033, 1034, 3, - 383, 191, 0, 1034, 1035, 3, 365, 182, 0, 1035, 220, - 1, 0, 0, 0, 1036, 1037, 3, 395, 197, 0, 1037, - 1038, 3, 407, 203, 0, 1038, 222, 1, 0, 0, 0, - 1039, 1040, 3, 383, 191, 0, 1040, 1041, 3, 385, 192, - 0, 1041, 1042, 3, 401, 200, 0, 1042, 224, 1, 0, - 0, 0, 1043, 1044, 3, 397, 198, 0, 1044, 1045, 3, - 397, 198, 0, 1045, 1046, 3, 373, 186, 0, 1046, 1047, - 3, 363, 181, 0, 1047, 226, 1, 0, 0, 0, 1048, - 1049, 3, 393, 196, 0, 1049, 1050, 3, 395, 197, 0, - 1050, 1051, 3, 391, 195, 0, 1051, 1052, 3, 397, 198, - 0, 1052, 1053, 3, 397, 198, 0, 1053, 1054, 3, 373, - 186, 0, 1054, 1055, 3, 363, 181, 0, 1055, 228, 1, - 0, 0, 0, 1056, 1057, 3, 393, 196, 0, 1057, 1058, - 3, 371, 185, 0, 1058, 1059, 3, 357, 178, 0, 1059, - 1060, 5, 49, 0, 0, 1060, 230, 1, 0, 0, 0, - 1061, 1062, 3, 393, 196, 0, 1062, 1063, 3, 371, 185, - 0, 1063, 1064, 3, 357, 178, 0, 1064, 1065, 5, 50, - 0, 0, 1065, 1066, 5, 53, 0, 0, 1066, 1067, 5, - 54, 0, 0, 1067, 232, 1, 0, 0, 0, 1068, 1069, - 3, 393, 196, 0, 1069, 1070, 3, 371, 185, 0, 1070, - 1071, 3, 357, 178, 0, 1071, 1072, 5, 51, 0, 0, - 1072, 1073, 5, 56, 0, 0, 1073, 1074, 5, 52, 0, - 0, 1074, 234, 1, 0, 0, 0, 1075, 1076, 3, 393, - 196, 0, 1076, 1077, 3, 371, 185, 0, 1077, 1078, 3, - 357, 178, 0, 1078, 1079, 5, 53, 0, 0, 1079, 1080, - 5, 49, 0, 0, 1080, 1081, 5, 50, 0, 0, 1081, - 236, 1, 0, 0, 0, 1082, 1083, 3, 381, 190, 0, - 1083, 1084, 3, 363, 181, 0, 1084, 1085, 5, 53, 0, - 0, 1085, 238, 1, 0, 0, 0, 1086, 1087, 3, 361, - 180, 0, 1087, 1088, 3, 385, 192, 0, 1088, 1089, 3, - 357, 178, 0, 1089, 1090, 3, 379, 189, 0, 1090, 1091, - 3, 365, 182, 0, 1091, 1092, 3, 393, 196, 0, 1092, - 1093, 3, 361, 180, 0, 1093, 1094, 3, 365, 182, 0, - 1094, 240, 1, 0, 0, 0, 1095, 1096, 3, 373, 186, - 0, 1096, 1097, 3, 367, 183, 0, 1097, 242, 1, 0, - 0, 0, 1098, 1099, 3, 393, 196, 0, 1099, 1100, 3, - 395, 197, 0, 1100, 1101, 3, 391, 195, 0, 1101, 1102, - 3, 379, 189, 0, 1102, 1103, 3, 357, 178, 0, 1103, - 1104, 3, 383, 191, 0, 1104, 1105, 3, 369, 184, 0, - 1105, 244, 1, 0, 0, 0, 1106, 1107, 3, 393, 196, - 0, 1107, 1108, 3, 395, 197, 0, 1108, 1109, 3, 391, - 195, 0, 1109, 1110, 3, 363, 181, 0, 1110, 1111, 3, - 395, 197, 0, 1111, 246, 1, 0, 0, 0, 1112, 1113, - 3, 393, 196, 0, 1113, 1114, 3, 357, 178, 0, 1114, - 1115, 3, 381, 190, 0, 1115, 1116, 3, 365, 182, 0, - 1116, 1117, 3, 395, 197, 0, 1117, 1118, 3, 365, 182, - 0, 1118, 1119, 3, 391, 195, 0, 1119, 1120, 3, 381, - 190, 0, 1120, 248, 1, 0, 0, 0, 1121, 1122, 3, - 373, 186, 0, 1122, 1123, 3, 393, 196, 0, 1123, 1124, - 3, 373, 186, 0, 1124, 1125, 3, 391, 195, 0, 1125, - 1126, 3, 373, 186, 0, 1126, 250, 1, 0, 0, 0, - 1127, 1128, 3, 373, 186, 0, 1128, 1129, 3, 393, 196, - 0, 1129, 1130, 3, 397, 198, 0, 1130, 1131, 3, 391, - 195, 0, 1131, 1132, 3, 373, 186, 0, 1132, 252, 1, - 0, 0, 0, 1133, 1134, 3, 373, 186, 0, 1134, 1135, - 3, 393, 196, 0, 1135, 1136, 3, 359, 179, 0, 1136, - 1137, 3, 379, 189, 0, 1137, 1138, 3, 357, 178, 0, - 1138, 1139, 3, 383, 191, 0, 1139, 1140, 3, 377, 188, - 0, 1140, 254, 1, 0, 0, 0, 1141, 1142, 3, 373, - 186, 0, 1142, 1143, 3, 393, 196, 0, 1143, 1144, 3, - 379, 189, 0, 1144, 1145, 3, 373, 186, 0, 1145, 1146, - 3, 395, 197, 0, 1146, 1147, 3, 365, 182, 0, 1147, - 1148, 3, 391, 195, 0, 1148, 1149, 3, 357, 178, 0, - 1149, 1150, 3, 379, 189, 0, 1150, 256, 1, 0, 0, - 0, 1151, 1152, 3, 373, 186, 0, 1152, 1153, 3, 393, - 196, 0, 1153, 1154, 3, 383, 191, 0, 1154, 1155, 3, - 397, 198, 0, 1155, 1156, 3, 381, 190, 0, 1156, 1157, - 3, 365, 182, 0, 1157, 1158, 3, 391, 195, 0, 1158, - 1159, 3, 373, 186, 0, 1159, 1160, 3, 361, 180, 0, - 1160, 258, 1, 0, 0, 0, 1161, 1162, 3, 391, 195, - 0, 1162, 1163, 3, 365, 182, 0, 1163, 1164, 3, 369, - 184, 0, 1164, 1165, 3, 365, 182, 0, 1165, 1166, 3, - 403, 201, 0, 1166, 260, 1, 0, 0, 0, 1167, 1168, - 3, 393, 196, 0, 1168, 1169, 3, 397, 198, 0, 1169, - 1170, 3, 359, 179, 0, 1170, 1171, 3, 393, 196, 0, - 1171, 1172, 3, 395, 197, 0, 1172, 1173, 3, 391, 195, - 0, 1173, 262, 1, 0, 0, 0, 1174, 1175, 3, 391, - 195, 0, 1175, 1176, 3, 365, 182, 0, 1176, 1177, 3, - 387, 193, 0, 1177, 1178, 3, 379, 189, 0, 1178, 1179, - 3, 357, 178, 0, 1179, 1180, 3, 361, 180, 0, 1180, - 1181, 3, 365, 182, 0, 1181, 264, 1, 0, 0, 0, - 1182, 1183, 3, 365, 182, 0, 1183, 1184, 3, 403, 201, - 0, 1184, 1185, 3, 373, 186, 0, 1185, 1186, 3, 393, - 196, 0, 1186, 1187, 3, 395, 197, 0, 1187, 1188, 3, - 393, 196, 0, 1188, 266, 1, 0, 0, 0, 1189, 1190, - 3, 361, 180, 0, 1190, 1191, 3, 385, 192, 0, 1191, - 1192, 3, 397, 198, 0, 1192, 1193, 3, 383, 191, 0, - 1193, 1194, 3, 395, 197, 0, 1194, 268, 1, 0, 0, - 0, 1195, 1196, 3, 393, 196, 0, 1196, 1197, 3, 397, - 198, 0, 1197, 1198, 3, 381, 190, 0, 1198, 270, 1, - 0, 0, 0, 1199, 1200, 3, 381, 190, 0, 1200, 1201, - 3, 373, 186, 0, 1201, 1202, 3, 383, 191, 0, 1202, - 272, 1, 0, 0, 0, 1203, 1204, 3, 381, 190, 0, - 1204, 1205, 3, 357, 178, 0, 1205, 1206, 3, 403, 201, - 0, 1206, 274, 1, 0, 0, 0, 1207, 1208, 3, 357, - 178, 0, 1208, 1209, 3, 399, 199, 0, 1209, 1210, 3, - 369, 184, 0, 1210, 276, 1, 0, 0, 0, 1211, 1212, - 3, 393, 196, 0, 1212, 1213, 3, 357, 178, 0, 1213, - 1214, 3, 381, 190, 0, 1214, 1215, 3, 387, 193, 0, - 1215, 1216, 3, 379, 189, 0, 1216, 1217, 3, 365, 182, - 0, 1217, 278, 1, 0, 0, 0, 1218, 1219, 3, 393, - 196, 0, 1219, 1220, 3, 365, 182, 0, 1220, 1221, 3, - 387, 193, 0, 1221, 1222, 3, 357, 178, 0, 1222, 1223, - 3, 391, 195, 0, 1223, 1224, 3, 357, 178, 0, 1224, - 1225, 3, 395, 197, 0, 1225, 1226, 3, 385, 192, 0, - 1226, 1227, 3, 391, 195, 0, 1227, 280, 1, 0, 0, - 0, 1228, 1232, 5, 60, 0, 0, 1229, 1231, 8, 0, - 0, 0, 1230, 1229, 1, 0, 0, 0, 1231, 1234, 1, - 0, 0, 0, 1232, 1230, 1, 0, 0, 0, 1232, 1233, - 1, 0, 0, 0, 1233, 1235, 1, 0, 0, 0, 1234, - 1232, 1, 0, 0, 0, 1235, 1236, 5, 62, 0, 0, - 1236, 282, 1, 0, 0, 0, 1237, 1239, 3, 337, 168, - 0, 1238, 1237, 1, 0, 0, 0, 1238, 1239, 1, 0, - 0, 0, 1239, 1240, 1, 0, 0, 0, 1240, 1241, 5, - 58, 0, 0, 1241, 284, 1, 0, 0, 0, 1242, 1243, - 3, 283, 141, 0, 1243, 1244, 3, 339, 169, 0, 1244, - 286, 1, 0, 0, 0, 1245, 1246, 5, 95, 0, 0, - 1246, 1247, 5, 58, 0, 0, 1247, 1250, 1, 0, 0, - 0, 1248, 1251, 3, 331, 165, 0, 1249, 1251, 3, 351, - 175, 0, 1250, 1248, 1, 0, 0, 0, 1250, 1249, 1, - 0, 0, 0, 1251, 1260, 1, 0, 0, 0, 1252, 1255, - 3, 335, 167, 0, 1253, 1255, 5, 46, 0, 0, 1254, - 1252, 1, 0, 0, 0, 1254, 1253, 1, 0, 0, 0, - 1255, 1258, 1, 0, 0, 0, 1256, 1254, 1, 0, 0, - 0, 1256, 1257, 1, 0, 0, 0, 1257, 1259, 1, 0, - 0, 0, 1258, 1256, 1, 0, 0, 0, 1259, 1261, 3, - 335, 167, 0, 1260, 1256, 1, 0, 0, 0, 1260, 1261, - 1, 0, 0, 0, 1261, 288, 1, 0, 0, 0, 1262, - 1263, 5, 63, 0, 0, 1263, 1264, 3, 333, 166, 0, - 1264, 290, 1, 0, 0, 0, 1265, 1266, 5, 36, 0, - 0, 1266, 1267, 3, 333, 166, 0, 1267, 292, 1, 0, - 0, 0, 1268, 1270, 5, 64, 0, 0, 1269, 1271, 7, - 1, 0, 0, 1270, 1269, 1, 0, 0, 0, 1271, 1272, - 1, 0, 0, 0, 1272, 1270, 1, 0, 0, 0, 1272, - 1273, 1, 0, 0, 0, 1273, 1283, 1, 0, 0, 0, - 1274, 1277, 5, 45, 0, 0, 1275, 1278, 7, 1, 0, - 0, 1276, 1278, 3, 351, 175, 0, 1277, 1275, 1, 0, - 0, 0, 1277, 1276, 1, 0, 0, 0, 1278, 1279, 1, - 0, 0, 0, 1279, 1277, 1, 0, 0, 0, 1279, 1280, - 1, 0, 0, 0, 1280, 1282, 1, 0, 0, 0, 1281, - 1274, 1, 0, 0, 0, 1282, 1285, 1, 0, 0, 0, - 1283, 1281, 1, 0, 0, 0, 1283, 1284, 1, 0, 0, - 0, 1284, 294, 1, 0, 0, 0, 1285, 1283, 1, 0, - 0, 0, 1286, 1287, 3, 293, 146, 0, 1287, 1288, 5, - 64, 0, 0, 1288, 296, 1, 0, 0, 0, 1289, 1291, - 3, 351, 175, 0, 1290, 1289, 1, 0, 0, 0, 1291, - 1292, 1, 0, 0, 0, 1292, 1290, 1, 0, 0, 0, - 1292, 1293, 1, 0, 0, 0, 1293, 298, 1, 0, 0, - 0, 1294, 1296, 3, 351, 175, 0, 1295, 1294, 1, 0, - 0, 0, 1296, 1299, 1, 0, 0, 0, 1297, 1295, 1, - 0, 0, 0, 1297, 1298, 1, 0, 0, 0, 1298, 1300, - 1, 0, 0, 0, 1299, 1297, 1, 0, 0, 0, 1300, - 1302, 5, 46, 0, 0, 1301, 1303, 3, 351, 175, 0, - 1302, 1301, 1, 0, 0, 0, 1303, 1304, 1, 0, 0, - 0, 1304, 1302, 1, 0, 0, 0, 1304, 1305, 1, 0, - 0, 0, 1305, 300, 1, 0, 0, 0, 1306, 1308, 3, - 351, 175, 0, 1307, 1306, 1, 0, 0, 0, 1308, 1309, - 1, 0, 0, 0, 1309, 1307, 1, 0, 0, 0, 1309, - 1310, 1, 0, 0, 0, 1310, 1311, 1, 0, 0, 0, - 1311, 1315, 5, 46, 0, 0, 1312, 1314, 3, 351, 175, - 0, 1313, 1312, 1, 0, 0, 0, 1314, 1317, 1, 0, - 0, 0, 1315, 1313, 1, 0, 0, 0, 1315, 1316, 1, - 0, 0, 0, 1316, 1318, 1, 0, 0, 0, 1317, 1315, - 1, 0, 0, 0, 1318, 1319, 3, 315, 157, 0, 1319, - 1336, 1, 0, 0, 0, 1320, 1322, 5, 46, 0, 0, - 1321, 1323, 3, 351, 175, 0, 1322, 1321, 1, 0, 0, - 0, 1323, 1324, 1, 0, 0, 0, 1324, 1322, 1, 0, - 0, 0, 1324, 1325, 1, 0, 0, 0, 1325, 1326, 1, - 0, 0, 0, 1326, 1327, 3, 315, 157, 0, 1327, 1336, - 1, 0, 0, 0, 1328, 1330, 3, 351, 175, 0, 1329, - 1328, 1, 0, 0, 0, 1330, 1331, 1, 0, 0, 0, - 1331, 1329, 1, 0, 0, 0, 1331, 1332, 1, 0, 0, - 0, 1332, 1333, 1, 0, 0, 0, 1333, 1334, 3, 315, - 157, 0, 1334, 1336, 1, 0, 0, 0, 1335, 1307, 1, - 0, 0, 0, 1335, 1320, 1, 0, 0, 0, 1335, 1329, - 1, 0, 0, 0, 1336, 302, 1, 0, 0, 0, 1337, - 1338, 5, 43, 0, 0, 1338, 1339, 3, 297, 148, 0, - 1339, 304, 1, 0, 0, 0, 1340, 1341, 5, 43, 0, - 0, 1341, 1342, 3, 299, 149, 0, 1342, 306, 1, 0, - 0, 0, 1343, 1344, 5, 43, 0, 0, 1344, 1345, 3, - 301, 150, 0, 1345, 308, 1, 0, 0, 0, 1346, 1347, - 5, 45, 0, 0, 1347, 1348, 3, 297, 148, 0, 1348, - 310, 1, 0, 0, 0, 1349, 1350, 5, 45, 0, 0, - 1350, 1351, 3, 299, 149, 0, 1351, 312, 1, 0, 0, - 0, 1352, 1353, 5, 45, 0, 0, 1353, 1354, 3, 301, - 150, 0, 1354, 314, 1, 0, 0, 0, 1355, 1357, 7, - 2, 0, 0, 1356, 1358, 7, 3, 0, 0, 1357, 1356, - 1, 0, 0, 0, 1357, 1358, 1, 0, 0, 0, 1358, - 1360, 1, 0, 0, 0, 1359, 1361, 3, 351, 175, 0, - 1360, 1359, 1, 0, 0, 0, 1361, 1362, 1, 0, 0, - 0, 1362, 1360, 1, 0, 0, 0, 1362, 1363, 1, 0, - 0, 0, 1363, 316, 1, 0, 0, 0, 1364, 1369, 5, - 39, 0, 0, 1365, 1368, 8, 4, 0, 0, 1366, 1368, - 3, 325, 162, 0, 1367, 1365, 1, 0, 0, 0, 1367, - 1366, 1, 0, 0, 0, 1368, 1371, 1, 0, 0, 0, - 1369, 1367, 1, 0, 0, 0, 1369, 1370, 1, 0, 0, - 0, 1370, 1372, 1, 0, 0, 0, 1371, 1369, 1, 0, - 0, 0, 1372, 1373, 5, 39, 0, 0, 1373, 318, 1, - 0, 0, 0, 1374, 1379, 5, 34, 0, 0, 1375, 1378, - 8, 5, 0, 0, 1376, 1378, 3, 325, 162, 0, 1377, - 1375, 1, 0, 0, 0, 1377, 1376, 1, 0, 0, 0, - 1378, 1381, 1, 0, 0, 0, 1379, 1377, 1, 0, 0, - 0, 1379, 1380, 1, 0, 0, 0, 1380, 1382, 1, 0, - 0, 0, 1381, 1379, 1, 0, 0, 0, 1382, 1383, 5, - 34, 0, 0, 1383, 320, 1, 0, 0, 0, 1384, 1385, - 5, 39, 0, 0, 1385, 1386, 5, 39, 0, 0, 1386, - 1387, 5, 39, 0, 0, 1387, 1399, 1, 0, 0, 0, - 1388, 1392, 5, 39, 0, 0, 1389, 1390, 5, 39, 0, - 0, 1390, 1392, 5, 39, 0, 0, 1391, 1388, 1, 0, - 0, 0, 1391, 1389, 1, 0, 0, 0, 1391, 1392, 1, - 0, 0, 0, 1392, 1395, 1, 0, 0, 0, 1393, 1396, - 8, 6, 0, 0, 1394, 1396, 3, 325, 162, 0, 1395, - 1393, 1, 0, 0, 0, 1395, 1394, 1, 0, 0, 0, - 1396, 1398, 1, 0, 0, 0, 1397, 1391, 1, 0, 0, - 0, 1398, 1401, 1, 0, 0, 0, 1399, 1397, 1, 0, - 0, 0, 1399, 1400, 1, 0, 0, 0, 1400, 1402, 1, - 0, 0, 0, 1401, 1399, 1, 0, 0, 0, 1402, 1403, - 5, 39, 0, 0, 1403, 1404, 5, 39, 0, 0, 1404, - 1405, 5, 39, 0, 0, 1405, 322, 1, 0, 0, 0, - 1406, 1407, 5, 34, 0, 0, 1407, 1408, 5, 34, 0, - 0, 1408, 1409, 5, 34, 0, 0, 1409, 1421, 1, 0, - 0, 0, 1410, 1414, 5, 34, 0, 0, 1411, 1412, 5, - 34, 0, 0, 1412, 1414, 5, 34, 0, 0, 1413, 1410, - 1, 0, 0, 0, 1413, 1411, 1, 0, 0, 0, 1413, - 1414, 1, 0, 0, 0, 1414, 1417, 1, 0, 0, 0, - 1415, 1418, 8, 7, 0, 0, 1416, 1418, 3, 325, 162, - 0, 1417, 1415, 1, 0, 0, 0, 1417, 1416, 1, 0, - 0, 0, 1418, 1420, 1, 0, 0, 0, 1419, 1413, 1, - 0, 0, 0, 1420, 1423, 1, 0, 0, 0, 1421, 1419, - 1, 0, 0, 0, 1421, 1422, 1, 0, 0, 0, 1422, - 1424, 1, 0, 0, 0, 1423, 1421, 1, 0, 0, 0, - 1424, 1425, 5, 34, 0, 0, 1425, 1426, 5, 34, 0, - 0, 1426, 1427, 5, 34, 0, 0, 1427, 324, 1, 0, - 0, 0, 1428, 1429, 5, 92, 0, 0, 1429, 1430, 7, - 8, 0, 0, 1430, 326, 1, 0, 0, 0, 1431, 1435, - 5, 40, 0, 0, 1432, 1434, 3, 353, 176, 0, 1433, - 1432, 1, 0, 0, 0, 1434, 1437, 1, 0, 0, 0, - 1435, 1433, 1, 0, 0, 0, 1435, 1436, 1, 0, 0, - 0, 1436, 1438, 1, 0, 0, 0, 1437, 1435, 1, 0, - 0, 0, 1438, 1439, 5, 41, 0, 0, 1439, 328, 1, - 0, 0, 0, 1440, 1444, 5, 91, 0, 0, 1441, 1443, - 3, 353, 176, 0, 1442, 1441, 1, 0, 0, 0, 1443, - 1446, 1, 0, 0, 0, 1444, 1442, 1, 0, 0, 0, - 1444, 1445, 1, 0, 0, 0, 1445, 1447, 1, 0, 0, - 0, 1446, 1444, 1, 0, 0, 0, 1447, 1448, 5, 93, - 0, 0, 1448, 330, 1, 0, 0, 0, 1449, 1452, 3, - 349, 174, 0, 1450, 1452, 5, 95, 0, 0, 1451, 1449, - 1, 0, 0, 0, 1451, 1450, 1, 0, 0, 0, 1452, - 332, 1, 0, 0, 0, 1453, 1456, 3, 331, 165, 0, - 1454, 1456, 3, 351, 175, 0, 1455, 1453, 1, 0, 0, - 0, 1455, 1454, 1, 0, 0, 0, 1456, 1462, 1, 0, - 0, 0, 1457, 1461, 3, 331, 165, 0, 1458, 1461, 3, - 351, 175, 0, 1459, 1461, 7, 9, 0, 0, 1460, 1457, - 1, 0, 0, 0, 1460, 1458, 1, 0, 0, 0, 1460, - 1459, 1, 0, 0, 0, 1461, 1464, 1, 0, 0, 0, - 1462, 1460, 1, 0, 0, 0, 1462, 1463, 1, 0, 0, - 0, 1463, 334, 1, 0, 0, 0, 1464, 1462, 1, 0, - 0, 0, 1465, 1470, 3, 331, 165, 0, 1466, 1470, 5, - 45, 0, 0, 1467, 1470, 3, 351, 175, 0, 1468, 1470, - 7, 9, 0, 0, 1469, 1465, 1, 0, 0, 0, 1469, - 1466, 1, 0, 0, 0, 1469, 1467, 1, 0, 0, 0, - 1469, 1468, 1, 0, 0, 0, 1470, 336, 1, 0, 0, - 0, 1471, 1480, 3, 349, 174, 0, 1472, 1475, 3, 335, - 167, 0, 1473, 1475, 5, 46, 0, 0, 1474, 1472, 1, - 0, 0, 0, 1474, 1473, 1, 0, 0, 0, 1475, 1478, - 1, 0, 0, 0, 1476, 1474, 1, 0, 0, 0, 1476, - 1477, 1, 0, 0, 0, 1477, 1479, 1, 0, 0, 0, - 1478, 1476, 1, 0, 0, 0, 1479, 1481, 3, 335, 167, - 0, 1480, 1476, 1, 0, 0, 0, 1480, 1481, 1, 0, - 0, 0, 1481, 338, 1, 0, 0, 0, 1482, 1487, 3, - 331, 165, 0, 1483, 1487, 5, 58, 0, 0, 1484, 1487, - 3, 351, 175, 0, 1485, 1487, 3, 341, 170, 0, 1486, - 1482, 1, 0, 0, 0, 1486, 1483, 1, 0, 0, 0, - 1486, 1484, 1, 0, 0, 0, 1486, 1485, 1, 0, 0, - 0, 1487, 1501, 1, 0, 0, 0, 1488, 1492, 3, 335, - 167, 0, 1489, 1492, 7, 10, 0, 0, 1490, 1492, 3, - 341, 170, 0, 1491, 1488, 1, 0, 0, 0, 1491, 1489, - 1, 0, 0, 0, 1491, 1490, 1, 0, 0, 0, 1492, - 1495, 1, 0, 0, 0, 1493, 1491, 1, 0, 0, 0, - 1493, 1494, 1, 0, 0, 0, 1494, 1499, 1, 0, 0, - 0, 1495, 1493, 1, 0, 0, 0, 1496, 1500, 3, 335, - 167, 0, 1497, 1500, 5, 58, 0, 0, 1498, 1500, 3, - 341, 170, 0, 1499, 1496, 1, 0, 0, 0, 1499, 1497, - 1, 0, 0, 0, 1499, 1498, 1, 0, 0, 0, 1500, - 1502, 1, 0, 0, 0, 1501, 1493, 1, 0, 0, 0, - 1501, 1502, 1, 0, 0, 0, 1502, 340, 1, 0, 0, - 0, 1503, 1506, 3, 343, 171, 0, 1504, 1506, 3, 347, - 173, 0, 1505, 1503, 1, 0, 0, 0, 1505, 1504, 1, - 0, 0, 0, 1506, 342, 1, 0, 0, 0, 1507, 1508, - 5, 37, 0, 0, 1508, 1509, 3, 345, 172, 0, 1509, - 1510, 3, 345, 172, 0, 1510, 344, 1, 0, 0, 0, - 1511, 1514, 3, 351, 175, 0, 1512, 1514, 7, 11, 0, - 0, 1513, 1511, 1, 0, 0, 0, 1513, 1512, 1, 0, - 0, 0, 1514, 346, 1, 0, 0, 0, 1515, 1516, 5, - 92, 0, 0, 1516, 1517, 7, 12, 0, 0, 1517, 348, - 1, 0, 0, 0, 1518, 1519, 7, 13, 0, 0, 1519, - 350, 1, 0, 0, 0, 1520, 1521, 2, 48, 57, 0, - 1521, 352, 1, 0, 0, 0, 1522, 1524, 7, 14, 0, - 0, 1523, 1522, 1, 0, 0, 0, 1524, 1525, 1, 0, - 0, 0, 1525, 1523, 1, 0, 0, 0, 1525, 1526, 1, - 0, 0, 0, 1526, 1527, 1, 0, 0, 0, 1527, 1528, - 6, 176, 0, 0, 1528, 354, 1, 0, 0, 0, 1529, - 1533, 5, 35, 0, 0, 1530, 1532, 8, 15, 0, 0, - 1531, 1530, 1, 0, 0, 0, 1532, 1535, 1, 0, 0, - 0, 1533, 1531, 1, 0, 0, 0, 1533, 1534, 1, 0, - 0, 0, 1534, 1536, 1, 0, 0, 0, 1535, 1533, 1, - 0, 0, 0, 1536, 1537, 6, 177, 0, 0, 1537, 356, - 1, 0, 0, 0, 1538, 1539, 7, 16, 0, 0, 1539, - 358, 1, 0, 0, 0, 1540, 1541, 7, 17, 0, 0, - 1541, 360, 1, 0, 0, 0, 1542, 1543, 7, 18, 0, - 0, 1543, 362, 1, 0, 0, 0, 1544, 1545, 7, 19, - 0, 0, 1545, 364, 1, 0, 0, 0, 1546, 1547, 7, - 2, 0, 0, 1547, 366, 1, 0, 0, 0, 1548, 1549, - 7, 20, 0, 0, 1549, 368, 1, 0, 0, 0, 1550, - 1551, 7, 21, 0, 0, 1551, 370, 1, 0, 0, 0, - 1552, 1553, 7, 22, 0, 0, 1553, 372, 1, 0, 0, - 0, 1554, 1555, 7, 23, 0, 0, 1555, 374, 1, 0, - 0, 0, 1556, 1557, 7, 24, 0, 0, 1557, 376, 1, - 0, 0, 0, 1558, 1559, 7, 25, 0, 0, 1559, 378, - 1, 0, 0, 0, 1560, 1561, 7, 26, 0, 0, 1561, - 380, 1, 0, 0, 0, 1562, 1563, 7, 27, 0, 0, - 1563, 382, 1, 0, 0, 0, 1564, 1565, 7, 28, 0, - 0, 1565, 384, 1, 0, 0, 0, 1566, 1567, 7, 29, - 0, 0, 1567, 386, 1, 0, 0, 0, 1568, 1569, 7, - 30, 0, 0, 1569, 388, 1, 0, 0, 0, 1570, 1571, - 7, 31, 0, 0, 1571, 390, 1, 0, 0, 0, 1572, - 1573, 7, 32, 0, 0, 1573, 392, 1, 0, 0, 0, - 1574, 1575, 7, 33, 0, 0, 1575, 394, 1, 0, 0, - 0, 1576, 1577, 7, 34, 0, 0, 1577, 396, 1, 0, - 0, 0, 1578, 1579, 7, 35, 0, 0, 1579, 398, 1, - 0, 0, 0, 1580, 1581, 7, 36, 0, 0, 1581, 400, - 1, 0, 0, 0, 1582, 1583, 7, 37, 0, 0, 1583, - 402, 1, 0, 0, 0, 1584, 1585, 7, 38, 0, 0, - 1585, 404, 1, 0, 0, 0, 1586, 1587, 7, 39, 0, - 0, 1587, 406, 1, 0, 0, 0, 1588, 1589, 7, 40, - 0, 0, 1589, 408, 1, 0, 0, 0, 54, 0, 567, - 600, 616, 625, 1232, 1238, 1250, 1254, 1256, 1260, 1272, 1277, - 1279, 1283, 1292, 1297, 1304, 1309, 1315, 1324, 1331, 1335, 1357, - 1362, 1367, 1369, 1377, 1379, 1391, 1395, 1399, 1413, 1417, 1421, - 1435, 1444, 1451, 1455, 1460, 1462, 1469, 1474, 1476, 1480, 1486, - 1491, 1493, 1499, 1501, 1505, 1513, 1525, 1533, 1, 6, 0, + 4, 0, 176, 1598, 6, -1, 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, 2, 157, 7, 157, + 2, 158, 7, 158, 2, 159, 7, 159, 2, 160, 7, + 160, 2, 161, 7, 161, 2, 162, 7, 162, 2, 163, + 7, 163, 2, 164, 7, 164, 2, 165, 7, 165, 2, + 166, 7, 166, 2, 167, 7, 167, 2, 168, 7, 168, + 2, 169, 7, 169, 2, 170, 7, 170, 2, 171, 7, + 171, 2, 172, 7, 172, 2, 173, 7, 173, 2, 174, + 7, 174, 2, 175, 7, 175, 2, 176, 7, 176, 2, + 177, 7, 177, 2, 178, 7, 178, 2, 179, 7, 179, + 2, 180, 7, 180, 2, 181, 7, 181, 2, 182, 7, + 182, 2, 183, 7, 183, 2, 184, 7, 184, 2, 185, + 7, 185, 2, 186, 7, 186, 2, 187, 7, 187, 2, + 188, 7, 188, 2, 189, 7, 189, 2, 190, 7, 190, + 2, 191, 7, 191, 2, 192, 7, 192, 2, 193, 7, + 193, 2, 194, 7, 194, 2, 195, 7, 195, 2, 196, + 7, 196, 2, 197, 7, 197, 2, 198, 7, 198, 2, + 199, 7, 199, 2, 200, 7, 200, 2, 201, 7, 201, + 2, 202, 7, 202, 2, 203, 7, 203, 2, 204, 7, + 204, 1, 0, 1, 0, 1, 1, 1, 1, 1, 2, + 1, 2, 1, 3, 1, 3, 1, 4, 1, 4, 1, + 5, 1, 5, 1, 6, 1, 6, 1, 7, 1, 7, + 1, 8, 1, 8, 1, 9, 1, 9, 1, 10, 1, + 10, 1, 11, 1, 11, 1, 12, 1, 12, 1, 13, + 1, 13, 1, 14, 1, 14, 1, 15, 1, 15, 1, + 16, 1, 16, 1, 17, 1, 17, 1, 17, 1, 18, + 1, 18, 1, 18, 1, 19, 1, 19, 1, 20, 1, + 20, 1, 20, 1, 21, 1, 21, 1, 22, 1, 22, + 1, 23, 1, 23, 1, 23, 1, 24, 1, 24, 1, + 24, 1, 25, 1, 25, 1, 26, 1, 26, 1, 26, + 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 1, + 28, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, + 1, 29, 1, 29, 1, 29, 1, 29, 1, 29, 1, + 30, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, + 1, 30, 1, 31, 1, 31, 1, 31, 1, 31, 1, + 31, 1, 31, 1, 31, 1, 32, 1, 32, 1, 32, + 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, + 32, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, + 1, 33, 1, 33, 1, 33, 1, 34, 1, 34, 1, + 34, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, + 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 1, + 36, 1, 36, 1, 36, 1, 36, 1, 36, 1, 36, + 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, + 37, 1, 37, 1, 37, 1, 37, 1, 38, 1, 38, + 1, 38, 1, 38, 1, 39, 1, 39, 1, 39, 1, + 39, 1, 39, 1, 40, 1, 40, 1, 40, 1, 40, + 1, 40, 1, 40, 1, 41, 1, 41, 1, 41, 1, + 41, 1, 41, 1, 41, 4, 41, 568, 8, 41, 11, + 41, 12, 41, 569, 1, 41, 1, 41, 1, 41, 1, + 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, + 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, + 42, 1, 42, 1, 43, 1, 43, 1, 43, 1, 43, + 1, 43, 1, 43, 1, 43, 1, 44, 1, 44, 1, + 44, 1, 44, 1, 44, 1, 44, 4, 44, 601, 8, + 44, 11, 44, 12, 44, 602, 1, 44, 1, 44, 1, + 44, 1, 45, 1, 45, 1, 45, 1, 45, 1, 45, + 1, 45, 1, 45, 1, 45, 1, 45, 4, 45, 617, + 8, 45, 11, 45, 12, 45, 618, 1, 45, 1, 45, + 1, 45, 1, 45, 1, 45, 4, 45, 626, 8, 45, + 11, 45, 12, 45, 627, 1, 45, 1, 45, 1, 45, + 1, 46, 1, 46, 1, 46, 1, 46, 1, 47, 1, + 47, 1, 47, 1, 47, 1, 47, 1, 48, 1, 48, + 1, 48, 1, 48, 1, 48, 1, 48, 1, 49, 1, + 49, 1, 49, 1, 49, 1, 49, 1, 49, 1, 49, + 1, 50, 1, 50, 1, 50, 1, 50, 1, 50, 1, + 50, 1, 50, 1, 50, 1, 50, 1, 50, 1, 51, + 1, 51, 1, 51, 1, 51, 1, 51, 1, 51, 1, + 51, 1, 52, 1, 52, 1, 52, 1, 52, 1, 52, + 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, + 53, 1, 53, 1, 54, 1, 54, 1, 54, 1, 54, + 1, 54, 1, 55, 1, 55, 1, 55, 1, 55, 1, + 55, 1, 55, 1, 56, 1, 56, 1, 56, 1, 56, + 1, 56, 1, 57, 1, 57, 1, 57, 1, 57, 1, + 57, 1, 57, 1, 57, 1, 58, 1, 58, 1, 58, + 1, 58, 1, 59, 1, 59, 1, 59, 1, 60, 1, + 60, 1, 60, 1, 60, 1, 60, 1, 61, 1, 61, + 1, 61, 1, 61, 1, 61, 1, 62, 1, 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, 64, 1, 64, 1, 64, 1, 64, + 1, 65, 1, 65, 1, 65, 1, 65, 1, 65, 1, + 66, 1, 66, 1, 66, 1, 66, 1, 66, 1, 66, + 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, + 67, 1, 67, 1, 67, 1, 68, 1, 68, 1, 68, + 1, 68, 1, 68, 1, 68, 1, 69, 1, 69, 1, + 69, 1, 69, 1, 70, 1, 70, 1, 70, 1, 70, + 1, 70, 1, 70, 1, 70, 1, 70, 1, 70, 1, + 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, + 1, 71, 1, 71, 1, 72, 1, 72, 1, 72, 1, + 72, 1, 72, 1, 73, 1, 73, 1, 73, 1, 73, + 1, 73, 1, 73, 1, 74, 1, 74, 1, 74, 1, + 74, 1, 74, 1, 74, 1, 75, 1, 75, 1, 75, + 1, 75, 1, 75, 1, 75, 1, 76, 1, 76, 1, + 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 77, + 1, 77, 1, 77, 1, 77, 1, 78, 1, 78, 1, + 78, 1, 79, 1, 79, 1, 79, 1, 79, 1, 80, + 1, 80, 1, 80, 1, 80, 1, 80, 1, 81, 1, + 81, 1, 81, 1, 81, 1, 81, 1, 81, 1, 81, + 1, 81, 1, 81, 1, 81, 1, 81, 1, 81, 1, + 82, 1, 82, 1, 82, 1, 82, 1, 82, 1, 82, + 1, 82, 1, 82, 1, 82, 1, 83, 1, 83, 1, + 83, 1, 83, 1, 83, 1, 83, 1, 84, 1, 84, + 1, 84, 1, 84, 1, 85, 1, 85, 1, 85, 1, + 85, 1, 86, 1, 86, 1, 86, 1, 86, 1, 86, + 1, 86, 1, 87, 1, 87, 1, 87, 1, 87, 1, + 87, 1, 88, 1, 88, 1, 88, 1, 88, 1, 89, + 1, 89, 1, 89, 1, 89, 1, 89, 1, 90, 1, + 90, 1, 90, 1, 90, 1, 90, 1, 90, 1, 91, + 1, 91, 1, 91, 1, 91, 1, 91, 1, 91, 1, + 92, 1, 92, 1, 92, 1, 92, 1, 92, 1, 92, + 1, 92, 1, 93, 1, 93, 1, 93, 1, 93, 1, + 93, 1, 93, 1, 93, 1, 94, 1, 94, 1, 94, + 1, 94, 1, 94, 1, 94, 1, 95, 1, 95, 1, + 95, 1, 95, 1, 95, 1, 95, 1, 96, 1, 96, + 1, 96, 1, 96, 1, 96, 1, 96, 1, 96, 1, + 96, 1, 96, 1, 96, 1, 96, 1, 96, 1, 96, + 1, 96, 1, 96, 1, 97, 1, 97, 1, 97, 1, + 97, 1, 98, 1, 98, 1, 98, 1, 98, 1, 98, + 1, 98, 1, 98, 1, 98, 1, 98, 1, 99, 1, + 99, 1, 99, 1, 99, 1, 99, 1, 99, 1, 99, + 1, 99, 1, 99, 1, 99, 1, 100, 1, 100, 1, + 100, 1, 100, 1, 100, 1, 100, 1, 100, 1, 100, + 1, 101, 1, 101, 1, 101, 1, 101, 1, 101, 1, + 101, 1, 101, 1, 101, 1, 101, 1, 101, 1, 102, + 1, 102, 1, 102, 1, 102, 1, 102, 1, 102, 1, + 102, 1, 102, 1, 102, 1, 103, 1, 103, 1, 103, + 1, 103, 1, 103, 1, 104, 1, 104, 1, 104, 1, + 104, 1, 104, 1, 104, 1, 105, 1, 105, 1, 105, + 1, 105, 1, 106, 1, 106, 1, 106, 1, 106, 1, + 106, 1, 106, 1, 107, 1, 107, 1, 107, 1, 107, + 1, 107, 1, 107, 1, 107, 1, 107, 1, 108, 1, + 108, 1, 108, 1, 108, 1, 108, 1, 108, 1, 108, + 1, 108, 1, 109, 1, 109, 1, 109, 1, 109, 1, + 109, 1, 109, 1, 109, 1, 109, 1, 109, 1, 110, + 1, 110, 1, 110, 1, 111, 1, 111, 1, 111, 1, + 111, 1, 112, 1, 112, 1, 112, 1, 112, 1, 112, + 1, 113, 1, 113, 1, 113, 1, 113, 1, 113, 1, + 113, 1, 113, 1, 113, 1, 114, 1, 114, 1, 114, + 1, 114, 1, 114, 1, 115, 1, 115, 1, 115, 1, + 115, 1, 115, 1, 115, 1, 115, 1, 116, 1, 116, + 1, 116, 1, 116, 1, 116, 1, 116, 1, 116, 1, + 117, 1, 117, 1, 117, 1, 117, 1, 117, 1, 117, + 1, 117, 1, 118, 1, 118, 1, 118, 1, 118, 1, + 119, 1, 119, 1, 119, 1, 119, 1, 119, 1, 119, + 1, 119, 1, 119, 1, 119, 1, 120, 1, 120, 1, + 120, 1, 121, 1, 121, 1, 121, 1, 121, 1, 121, + 1, 121, 1, 121, 1, 121, 1, 122, 1, 122, 1, + 122, 1, 122, 1, 122, 1, 122, 1, 123, 1, 123, + 1, 123, 1, 123, 1, 123, 1, 123, 1, 123, 1, + 123, 1, 123, 1, 124, 1, 124, 1, 124, 1, 124, + 1, 124, 1, 124, 1, 125, 1, 125, 1, 125, 1, + 125, 1, 125, 1, 125, 1, 126, 1, 126, 1, 126, + 1, 126, 1, 126, 1, 126, 1, 126, 1, 126, 1, + 127, 1, 127, 1, 127, 1, 127, 1, 127, 1, 127, + 1, 127, 1, 127, 1, 127, 1, 127, 1, 128, 1, + 128, 1, 128, 1, 128, 1, 128, 1, 128, 1, 128, + 1, 128, 1, 128, 1, 128, 1, 129, 1, 129, 1, + 129, 1, 129, 1, 129, 1, 129, 1, 130, 1, 130, + 1, 130, 1, 130, 1, 130, 1, 130, 1, 130, 1, + 131, 1, 131, 1, 131, 1, 131, 1, 131, 1, 131, + 1, 131, 1, 131, 1, 132, 1, 132, 1, 132, 1, + 132, 1, 132, 1, 132, 1, 132, 1, 133, 1, 133, + 1, 133, 1, 133, 1, 133, 1, 133, 1, 134, 1, + 134, 1, 134, 1, 134, 1, 135, 1, 135, 1, 135, + 1, 135, 1, 136, 1, 136, 1, 136, 1, 136, 1, + 137, 1, 137, 1, 137, 1, 137, 1, 138, 1, 138, + 1, 138, 1, 138, 1, 138, 1, 138, 1, 139, 1, + 139, 1, 139, 1, 139, 1, 139, 1, 139, 1, 139, + 1, 140, 1, 140, 1, 140, 1, 140, 1, 140, 1, + 140, 1, 140, 1, 140, 1, 140, 1, 140, 1, 141, + 1, 141, 5, 141, 1239, 8, 141, 10, 141, 12, 141, + 1242, 9, 141, 1, 141, 1, 141, 1, 142, 3, 142, + 1247, 8, 142, 1, 142, 1, 142, 1, 143, 1, 143, + 1, 143, 1, 144, 1, 144, 1, 144, 1, 144, 1, + 144, 3, 144, 1259, 8, 144, 1, 144, 1, 144, 5, + 144, 1263, 8, 144, 10, 144, 12, 144, 1266, 9, 144, + 1, 144, 3, 144, 1269, 8, 144, 1, 145, 1, 145, + 1, 145, 1, 146, 1, 146, 1, 146, 1, 147, 1, + 147, 4, 147, 1279, 8, 147, 11, 147, 12, 147, 1280, + 1, 147, 1, 147, 1, 147, 4, 147, 1286, 8, 147, + 11, 147, 12, 147, 1287, 5, 147, 1290, 8, 147, 10, + 147, 12, 147, 1293, 9, 147, 1, 148, 1, 148, 1, + 148, 1, 149, 4, 149, 1299, 8, 149, 11, 149, 12, + 149, 1300, 1, 150, 5, 150, 1304, 8, 150, 10, 150, + 12, 150, 1307, 9, 150, 1, 150, 1, 150, 4, 150, + 1311, 8, 150, 11, 150, 12, 150, 1312, 1, 151, 4, + 151, 1316, 8, 151, 11, 151, 12, 151, 1317, 1, 151, + 1, 151, 5, 151, 1322, 8, 151, 10, 151, 12, 151, + 1325, 9, 151, 1, 151, 1, 151, 1, 151, 1, 151, + 4, 151, 1331, 8, 151, 11, 151, 12, 151, 1332, 1, + 151, 1, 151, 1, 151, 4, 151, 1338, 8, 151, 11, + 151, 12, 151, 1339, 1, 151, 1, 151, 3, 151, 1344, + 8, 151, 1, 152, 1, 152, 1, 152, 1, 153, 1, + 153, 1, 153, 1, 154, 1, 154, 1, 154, 1, 155, + 1, 155, 1, 155, 1, 156, 1, 156, 1, 156, 1, + 157, 1, 157, 1, 157, 1, 158, 1, 158, 3, 158, + 1366, 8, 158, 1, 158, 4, 158, 1369, 8, 158, 11, + 158, 12, 158, 1370, 1, 159, 1, 159, 1, 159, 5, + 159, 1376, 8, 159, 10, 159, 12, 159, 1379, 9, 159, + 1, 159, 1, 159, 1, 160, 1, 160, 1, 160, 5, + 160, 1386, 8, 160, 10, 160, 12, 160, 1389, 9, 160, + 1, 160, 1, 160, 1, 161, 1, 161, 1, 161, 1, + 161, 1, 161, 1, 161, 1, 161, 3, 161, 1400, 8, + 161, 1, 161, 1, 161, 3, 161, 1404, 8, 161, 5, + 161, 1406, 8, 161, 10, 161, 12, 161, 1409, 9, 161, + 1, 161, 1, 161, 1, 161, 1, 161, 1, 162, 1, + 162, 1, 162, 1, 162, 1, 162, 1, 162, 1, 162, + 3, 162, 1422, 8, 162, 1, 162, 1, 162, 3, 162, + 1426, 8, 162, 5, 162, 1428, 8, 162, 10, 162, 12, + 162, 1431, 9, 162, 1, 162, 1, 162, 1, 162, 1, + 162, 1, 163, 1, 163, 1, 163, 1, 164, 1, 164, + 5, 164, 1442, 8, 164, 10, 164, 12, 164, 1445, 9, + 164, 1, 164, 1, 164, 1, 165, 1, 165, 5, 165, + 1451, 8, 165, 10, 165, 12, 165, 1454, 9, 165, 1, + 165, 1, 165, 1, 166, 1, 166, 3, 166, 1460, 8, + 166, 1, 167, 1, 167, 3, 167, 1464, 8, 167, 1, + 167, 1, 167, 1, 167, 5, 167, 1469, 8, 167, 10, + 167, 12, 167, 1472, 9, 167, 1, 168, 1, 168, 1, + 168, 1, 168, 3, 168, 1478, 8, 168, 1, 169, 1, + 169, 1, 169, 5, 169, 1483, 8, 169, 10, 169, 12, + 169, 1486, 9, 169, 1, 169, 3, 169, 1489, 8, 169, + 1, 170, 1, 170, 1, 170, 1, 170, 3, 170, 1495, + 8, 170, 1, 170, 1, 170, 1, 170, 5, 170, 1500, + 8, 170, 10, 170, 12, 170, 1503, 9, 170, 1, 170, + 1, 170, 1, 170, 3, 170, 1508, 8, 170, 3, 170, + 1510, 8, 170, 1, 171, 1, 171, 3, 171, 1514, 8, + 171, 1, 172, 1, 172, 1, 172, 1, 172, 1, 173, + 1, 173, 3, 173, 1522, 8, 173, 1, 174, 1, 174, + 1, 174, 1, 175, 1, 175, 1, 176, 1, 176, 1, + 177, 4, 177, 1532, 8, 177, 11, 177, 12, 177, 1533, + 1, 177, 1, 177, 1, 178, 1, 178, 5, 178, 1540, + 8, 178, 10, 178, 12, 178, 1543, 9, 178, 1, 178, + 1, 178, 1, 179, 1, 179, 1, 180, 1, 180, 1, + 181, 1, 181, 1, 182, 1, 182, 1, 183, 1, 183, + 1, 184, 1, 184, 1, 185, 1, 185, 1, 186, 1, + 186, 1, 187, 1, 187, 1, 188, 1, 188, 1, 189, + 1, 189, 1, 190, 1, 190, 1, 191, 1, 191, 1, + 192, 1, 192, 1, 193, 1, 193, 1, 194, 1, 194, + 1, 195, 1, 195, 1, 196, 1, 196, 1, 197, 1, + 197, 1, 198, 1, 198, 1, 199, 1, 199, 1, 200, + 1, 200, 1, 201, 1, 201, 1, 202, 1, 202, 1, + 203, 1, 203, 1, 204, 1, 204, 0, 0, 205, 1, + 1, 3, 2, 5, 3, 7, 4, 9, 5, 11, 6, + 13, 7, 15, 8, 17, 9, 19, 10, 21, 11, 23, + 12, 25, 13, 27, 14, 29, 15, 31, 16, 33, 17, + 35, 18, 37, 19, 39, 20, 41, 21, 43, 22, 45, + 23, 47, 24, 49, 25, 51, 26, 53, 27, 55, 28, + 57, 29, 59, 30, 61, 31, 63, 32, 65, 33, 67, + 34, 69, 35, 71, 36, 73, 37, 75, 38, 77, 39, + 79, 40, 81, 41, 83, 42, 85, 43, 87, 44, 89, + 45, 91, 46, 93, 47, 95, 48, 97, 49, 99, 50, + 101, 51, 103, 52, 105, 53, 107, 54, 109, 55, 111, + 56, 113, 57, 115, 58, 117, 59, 119, 60, 121, 61, + 123, 62, 125, 63, 127, 64, 129, 65, 131, 66, 133, + 67, 135, 68, 137, 69, 139, 70, 141, 71, 143, 72, + 145, 73, 147, 74, 149, 75, 151, 76, 153, 77, 155, + 78, 157, 79, 159, 80, 161, 81, 163, 82, 165, 83, + 167, 84, 169, 85, 171, 86, 173, 87, 175, 88, 177, + 89, 179, 90, 181, 91, 183, 92, 185, 93, 187, 94, + 189, 95, 191, 96, 193, 97, 195, 98, 197, 99, 199, + 100, 201, 101, 203, 102, 205, 103, 207, 104, 209, 105, + 211, 106, 213, 107, 215, 108, 217, 109, 219, 110, 221, + 111, 223, 112, 225, 113, 227, 114, 229, 115, 231, 116, + 233, 117, 235, 118, 237, 119, 239, 120, 241, 121, 243, + 122, 245, 123, 247, 124, 249, 125, 251, 126, 253, 127, + 255, 128, 257, 129, 259, 130, 261, 131, 263, 132, 265, + 133, 267, 134, 269, 135, 271, 136, 273, 137, 275, 138, + 277, 139, 279, 140, 281, 141, 283, 142, 285, 143, 287, + 144, 289, 145, 291, 146, 293, 147, 295, 148, 297, 149, + 299, 150, 301, 151, 303, 152, 305, 153, 307, 154, 309, + 155, 311, 156, 313, 157, 315, 158, 317, 159, 319, 160, + 321, 161, 323, 162, 325, 163, 327, 164, 329, 165, 331, + 166, 333, 167, 335, 168, 337, 0, 339, 169, 341, 170, + 343, 171, 345, 172, 347, 173, 349, 174, 351, 0, 353, + 0, 355, 175, 357, 176, 359, 0, 361, 0, 363, 0, + 365, 0, 367, 0, 369, 0, 371, 0, 373, 0, 375, + 0, 377, 0, 379, 0, 381, 0, 383, 0, 385, 0, + 387, 0, 389, 0, 391, 0, 393, 0, 395, 0, 397, + 0, 399, 0, 401, 0, 403, 0, 405, 0, 407, 0, + 409, 0, 1, 0, 41, 8, 0, 0, 32, 34, 34, + 60, 60, 62, 62, 92, 92, 94, 94, 96, 96, 123, + 125, 2, 0, 65, 90, 97, 122, 2, 0, 69, 69, + 101, 101, 2, 0, 43, 43, 45, 45, 4, 0, 10, + 10, 13, 13, 39, 39, 92, 92, 4, 0, 10, 10, + 13, 13, 34, 34, 92, 92, 2, 0, 39, 39, 92, + 92, 2, 0, 34, 34, 92, 92, 8, 0, 34, 34, + 39, 39, 92, 92, 98, 98, 102, 102, 110, 110, 114, + 114, 116, 116, 3, 0, 183, 183, 768, 879, 8255, 8256, + 2, 0, 46, 46, 58, 58, 2, 0, 65, 70, 97, + 102, 7, 0, 33, 33, 35, 47, 59, 59, 61, 61, + 63, 64, 95, 95, 126, 126, 13, 0, 65, 90, 97, + 122, 192, 214, 216, 246, 248, 767, 880, 893, 895, 8191, + 8204, 8205, 8304, 8591, 11264, 12271, 12289, 55295, 63744, 64975, 65008, + 65533, 3, 0, 9, 10, 13, 13, 32, 32, 2, 0, + 10, 10, 13, 13, 2, 0, 65, 65, 97, 97, 2, + 0, 66, 66, 98, 98, 2, 0, 67, 67, 99, 99, + 2, 0, 68, 68, 100, 100, 2, 0, 70, 70, 102, + 102, 2, 0, 71, 71, 103, 103, 2, 0, 72, 72, + 104, 104, 2, 0, 73, 73, 105, 105, 2, 0, 74, + 74, 106, 106, 2, 0, 75, 75, 107, 107, 2, 0, + 76, 76, 108, 108, 2, 0, 77, 77, 109, 109, 2, + 0, 78, 78, 110, 110, 2, 0, 79, 79, 111, 111, + 2, 0, 80, 80, 112, 112, 2, 0, 81, 81, 113, + 113, 2, 0, 82, 82, 114, 114, 2, 0, 83, 83, + 115, 115, 2, 0, 84, 84, 116, 116, 2, 0, 85, + 85, 117, 117, 2, 0, 86, 86, 118, 118, 2, 0, + 87, 87, 119, 119, 2, 0, 88, 88, 120, 120, 2, + 0, 89, 89, 121, 121, 2, 0, 90, 90, 122, 122, + 1631, 0, 1, 1, 0, 0, 0, 0, 3, 1, 0, + 0, 0, 0, 5, 1, 0, 0, 0, 0, 7, 1, + 0, 0, 0, 0, 9, 1, 0, 0, 0, 0, 11, + 1, 0, 0, 0, 0, 13, 1, 0, 0, 0, 0, + 15, 1, 0, 0, 0, 0, 17, 1, 0, 0, 0, + 0, 19, 1, 0, 0, 0, 0, 21, 1, 0, 0, + 0, 0, 23, 1, 0, 0, 0, 0, 25, 1, 0, + 0, 0, 0, 27, 1, 0, 0, 0, 0, 29, 1, + 0, 0, 0, 0, 31, 1, 0, 0, 0, 0, 33, + 1, 0, 0, 0, 0, 35, 1, 0, 0, 0, 0, + 37, 1, 0, 0, 0, 0, 39, 1, 0, 0, 0, + 0, 41, 1, 0, 0, 0, 0, 43, 1, 0, 0, + 0, 0, 45, 1, 0, 0, 0, 0, 47, 1, 0, + 0, 0, 0, 49, 1, 0, 0, 0, 0, 51, 1, + 0, 0, 0, 0, 53, 1, 0, 0, 0, 0, 55, + 1, 0, 0, 0, 0, 57, 1, 0, 0, 0, 0, + 59, 1, 0, 0, 0, 0, 61, 1, 0, 0, 0, + 0, 63, 1, 0, 0, 0, 0, 65, 1, 0, 0, + 0, 0, 67, 1, 0, 0, 0, 0, 69, 1, 0, + 0, 0, 0, 71, 1, 0, 0, 0, 0, 73, 1, + 0, 0, 0, 0, 75, 1, 0, 0, 0, 0, 77, + 1, 0, 0, 0, 0, 79, 1, 0, 0, 0, 0, + 81, 1, 0, 0, 0, 0, 83, 1, 0, 0, 0, + 0, 85, 1, 0, 0, 0, 0, 87, 1, 0, 0, + 0, 0, 89, 1, 0, 0, 0, 0, 91, 1, 0, + 0, 0, 0, 93, 1, 0, 0, 0, 0, 95, 1, + 0, 0, 0, 0, 97, 1, 0, 0, 0, 0, 99, + 1, 0, 0, 0, 0, 101, 1, 0, 0, 0, 0, + 103, 1, 0, 0, 0, 0, 105, 1, 0, 0, 0, + 0, 107, 1, 0, 0, 0, 0, 109, 1, 0, 0, + 0, 0, 111, 1, 0, 0, 0, 0, 113, 1, 0, + 0, 0, 0, 115, 1, 0, 0, 0, 0, 117, 1, + 0, 0, 0, 0, 119, 1, 0, 0, 0, 0, 121, + 1, 0, 0, 0, 0, 123, 1, 0, 0, 0, 0, + 125, 1, 0, 0, 0, 0, 127, 1, 0, 0, 0, + 0, 129, 1, 0, 0, 0, 0, 131, 1, 0, 0, + 0, 0, 133, 1, 0, 0, 0, 0, 135, 1, 0, + 0, 0, 0, 137, 1, 0, 0, 0, 0, 139, 1, + 0, 0, 0, 0, 141, 1, 0, 0, 0, 0, 143, + 1, 0, 0, 0, 0, 145, 1, 0, 0, 0, 0, + 147, 1, 0, 0, 0, 0, 149, 1, 0, 0, 0, + 0, 151, 1, 0, 0, 0, 0, 153, 1, 0, 0, + 0, 0, 155, 1, 0, 0, 0, 0, 157, 1, 0, + 0, 0, 0, 159, 1, 0, 0, 0, 0, 161, 1, + 0, 0, 0, 0, 163, 1, 0, 0, 0, 0, 165, + 1, 0, 0, 0, 0, 167, 1, 0, 0, 0, 0, + 169, 1, 0, 0, 0, 0, 171, 1, 0, 0, 0, + 0, 173, 1, 0, 0, 0, 0, 175, 1, 0, 0, + 0, 0, 177, 1, 0, 0, 0, 0, 179, 1, 0, + 0, 0, 0, 181, 1, 0, 0, 0, 0, 183, 1, + 0, 0, 0, 0, 185, 1, 0, 0, 0, 0, 187, + 1, 0, 0, 0, 0, 189, 1, 0, 0, 0, 0, + 191, 1, 0, 0, 0, 0, 193, 1, 0, 0, 0, + 0, 195, 1, 0, 0, 0, 0, 197, 1, 0, 0, + 0, 0, 199, 1, 0, 0, 0, 0, 201, 1, 0, + 0, 0, 0, 203, 1, 0, 0, 0, 0, 205, 1, + 0, 0, 0, 0, 207, 1, 0, 0, 0, 0, 209, + 1, 0, 0, 0, 0, 211, 1, 0, 0, 0, 0, + 213, 1, 0, 0, 0, 0, 215, 1, 0, 0, 0, + 0, 217, 1, 0, 0, 0, 0, 219, 1, 0, 0, + 0, 0, 221, 1, 0, 0, 0, 0, 223, 1, 0, + 0, 0, 0, 225, 1, 0, 0, 0, 0, 227, 1, + 0, 0, 0, 0, 229, 1, 0, 0, 0, 0, 231, + 1, 0, 0, 0, 0, 233, 1, 0, 0, 0, 0, + 235, 1, 0, 0, 0, 0, 237, 1, 0, 0, 0, + 0, 239, 1, 0, 0, 0, 0, 241, 1, 0, 0, + 0, 0, 243, 1, 0, 0, 0, 0, 245, 1, 0, + 0, 0, 0, 247, 1, 0, 0, 0, 0, 249, 1, + 0, 0, 0, 0, 251, 1, 0, 0, 0, 0, 253, + 1, 0, 0, 0, 0, 255, 1, 0, 0, 0, 0, + 257, 1, 0, 0, 0, 0, 259, 1, 0, 0, 0, + 0, 261, 1, 0, 0, 0, 0, 263, 1, 0, 0, + 0, 0, 265, 1, 0, 0, 0, 0, 267, 1, 0, + 0, 0, 0, 269, 1, 0, 0, 0, 0, 271, 1, + 0, 0, 0, 0, 273, 1, 0, 0, 0, 0, 275, + 1, 0, 0, 0, 0, 277, 1, 0, 0, 0, 0, + 279, 1, 0, 0, 0, 0, 281, 1, 0, 0, 0, + 0, 283, 1, 0, 0, 0, 0, 285, 1, 0, 0, + 0, 0, 287, 1, 0, 0, 0, 0, 289, 1, 0, + 0, 0, 0, 291, 1, 0, 0, 0, 0, 293, 1, + 0, 0, 0, 0, 295, 1, 0, 0, 0, 0, 297, + 1, 0, 0, 0, 0, 299, 1, 0, 0, 0, 0, + 301, 1, 0, 0, 0, 0, 303, 1, 0, 0, 0, + 0, 305, 1, 0, 0, 0, 0, 307, 1, 0, 0, + 0, 0, 309, 1, 0, 0, 0, 0, 311, 1, 0, + 0, 0, 0, 313, 1, 0, 0, 0, 0, 315, 1, + 0, 0, 0, 0, 317, 1, 0, 0, 0, 0, 319, + 1, 0, 0, 0, 0, 321, 1, 0, 0, 0, 0, + 323, 1, 0, 0, 0, 0, 325, 1, 0, 0, 0, + 0, 327, 1, 0, 0, 0, 0, 329, 1, 0, 0, + 0, 0, 331, 1, 0, 0, 0, 0, 333, 1, 0, + 0, 0, 0, 335, 1, 0, 0, 0, 0, 339, 1, + 0, 0, 0, 0, 341, 1, 0, 0, 0, 0, 343, + 1, 0, 0, 0, 0, 345, 1, 0, 0, 0, 0, + 347, 1, 0, 0, 0, 0, 349, 1, 0, 0, 0, + 0, 355, 1, 0, 0, 0, 0, 357, 1, 0, 0, + 0, 1, 411, 1, 0, 0, 0, 3, 413, 1, 0, + 0, 0, 5, 415, 1, 0, 0, 0, 7, 417, 1, + 0, 0, 0, 9, 419, 1, 0, 0, 0, 11, 421, + 1, 0, 0, 0, 13, 423, 1, 0, 0, 0, 15, + 425, 1, 0, 0, 0, 17, 427, 1, 0, 0, 0, + 19, 429, 1, 0, 0, 0, 21, 431, 1, 0, 0, + 0, 23, 433, 1, 0, 0, 0, 25, 435, 1, 0, + 0, 0, 27, 437, 1, 0, 0, 0, 29, 439, 1, + 0, 0, 0, 31, 441, 1, 0, 0, 0, 33, 443, + 1, 0, 0, 0, 35, 445, 1, 0, 0, 0, 37, + 448, 1, 0, 0, 0, 39, 451, 1, 0, 0, 0, + 41, 453, 1, 0, 0, 0, 43, 456, 1, 0, 0, + 0, 45, 458, 1, 0, 0, 0, 47, 460, 1, 0, + 0, 0, 49, 463, 1, 0, 0, 0, 51, 466, 1, + 0, 0, 0, 53, 468, 1, 0, 0, 0, 55, 471, + 1, 0, 0, 0, 57, 476, 1, 0, 0, 0, 59, + 482, 1, 0, 0, 0, 61, 487, 1, 0, 0, 0, + 63, 494, 1, 0, 0, 0, 65, 501, 1, 0, 0, + 0, 67, 510, 1, 0, 0, 0, 69, 518, 1, 0, + 0, 0, 71, 521, 1, 0, 0, 0, 73, 531, 1, + 0, 0, 0, 75, 537, 1, 0, 0, 0, 77, 546, + 1, 0, 0, 0, 79, 550, 1, 0, 0, 0, 81, + 555, 1, 0, 0, 0, 83, 561, 1, 0, 0, 0, + 85, 574, 1, 0, 0, 0, 87, 587, 1, 0, 0, + 0, 89, 594, 1, 0, 0, 0, 91, 607, 1, 0, + 0, 0, 93, 632, 1, 0, 0, 0, 95, 636, 1, + 0, 0, 0, 97, 641, 1, 0, 0, 0, 99, 647, + 1, 0, 0, 0, 101, 654, 1, 0, 0, 0, 103, + 664, 1, 0, 0, 0, 105, 671, 1, 0, 0, 0, + 107, 676, 1, 0, 0, 0, 109, 683, 1, 0, 0, + 0, 111, 688, 1, 0, 0, 0, 113, 694, 1, 0, + 0, 0, 115, 699, 1, 0, 0, 0, 117, 706, 1, + 0, 0, 0, 119, 710, 1, 0, 0, 0, 121, 713, + 1, 0, 0, 0, 123, 718, 1, 0, 0, 0, 125, + 723, 1, 0, 0, 0, 127, 728, 1, 0, 0, 0, + 129, 735, 1, 0, 0, 0, 131, 742, 1, 0, 0, + 0, 133, 747, 1, 0, 0, 0, 135, 753, 1, 0, + 0, 0, 137, 761, 1, 0, 0, 0, 139, 767, 1, + 0, 0, 0, 141, 771, 1, 0, 0, 0, 143, 780, + 1, 0, 0, 0, 145, 788, 1, 0, 0, 0, 147, + 793, 1, 0, 0, 0, 149, 799, 1, 0, 0, 0, + 151, 805, 1, 0, 0, 0, 153, 811, 1, 0, 0, + 0, 155, 818, 1, 0, 0, 0, 157, 822, 1, 0, + 0, 0, 159, 825, 1, 0, 0, 0, 161, 829, 1, + 0, 0, 0, 163, 834, 1, 0, 0, 0, 165, 846, + 1, 0, 0, 0, 167, 855, 1, 0, 0, 0, 169, + 861, 1, 0, 0, 0, 171, 865, 1, 0, 0, 0, + 173, 869, 1, 0, 0, 0, 175, 875, 1, 0, 0, + 0, 177, 880, 1, 0, 0, 0, 179, 884, 1, 0, + 0, 0, 181, 889, 1, 0, 0, 0, 183, 895, 1, + 0, 0, 0, 185, 901, 1, 0, 0, 0, 187, 908, + 1, 0, 0, 0, 189, 915, 1, 0, 0, 0, 191, + 921, 1, 0, 0, 0, 193, 927, 1, 0, 0, 0, + 195, 942, 1, 0, 0, 0, 197, 946, 1, 0, 0, + 0, 199, 955, 1, 0, 0, 0, 201, 965, 1, 0, + 0, 0, 203, 973, 1, 0, 0, 0, 205, 983, 1, + 0, 0, 0, 207, 992, 1, 0, 0, 0, 209, 997, + 1, 0, 0, 0, 211, 1003, 1, 0, 0, 0, 213, + 1007, 1, 0, 0, 0, 215, 1013, 1, 0, 0, 0, + 217, 1021, 1, 0, 0, 0, 219, 1029, 1, 0, 0, + 0, 221, 1038, 1, 0, 0, 0, 223, 1041, 1, 0, + 0, 0, 225, 1045, 1, 0, 0, 0, 227, 1050, 1, + 0, 0, 0, 229, 1058, 1, 0, 0, 0, 231, 1063, + 1, 0, 0, 0, 233, 1070, 1, 0, 0, 0, 235, + 1077, 1, 0, 0, 0, 237, 1084, 1, 0, 0, 0, + 239, 1088, 1, 0, 0, 0, 241, 1097, 1, 0, 0, + 0, 243, 1100, 1, 0, 0, 0, 245, 1108, 1, 0, + 0, 0, 247, 1114, 1, 0, 0, 0, 249, 1123, 1, + 0, 0, 0, 251, 1129, 1, 0, 0, 0, 253, 1135, + 1, 0, 0, 0, 255, 1143, 1, 0, 0, 0, 257, + 1153, 1, 0, 0, 0, 259, 1163, 1, 0, 0, 0, + 261, 1169, 1, 0, 0, 0, 263, 1176, 1, 0, 0, + 0, 265, 1184, 1, 0, 0, 0, 267, 1191, 1, 0, + 0, 0, 269, 1197, 1, 0, 0, 0, 271, 1201, 1, + 0, 0, 0, 273, 1205, 1, 0, 0, 0, 275, 1209, + 1, 0, 0, 0, 277, 1213, 1, 0, 0, 0, 279, + 1219, 1, 0, 0, 0, 281, 1226, 1, 0, 0, 0, + 283, 1236, 1, 0, 0, 0, 285, 1246, 1, 0, 0, + 0, 287, 1250, 1, 0, 0, 0, 289, 1253, 1, 0, + 0, 0, 291, 1270, 1, 0, 0, 0, 293, 1273, 1, + 0, 0, 0, 295, 1276, 1, 0, 0, 0, 297, 1294, + 1, 0, 0, 0, 299, 1298, 1, 0, 0, 0, 301, + 1305, 1, 0, 0, 0, 303, 1343, 1, 0, 0, 0, + 305, 1345, 1, 0, 0, 0, 307, 1348, 1, 0, 0, + 0, 309, 1351, 1, 0, 0, 0, 311, 1354, 1, 0, + 0, 0, 313, 1357, 1, 0, 0, 0, 315, 1360, 1, + 0, 0, 0, 317, 1363, 1, 0, 0, 0, 319, 1372, + 1, 0, 0, 0, 321, 1382, 1, 0, 0, 0, 323, + 1392, 1, 0, 0, 0, 325, 1414, 1, 0, 0, 0, + 327, 1436, 1, 0, 0, 0, 329, 1439, 1, 0, 0, + 0, 331, 1448, 1, 0, 0, 0, 333, 1459, 1, 0, + 0, 0, 335, 1463, 1, 0, 0, 0, 337, 1477, 1, + 0, 0, 0, 339, 1479, 1, 0, 0, 0, 341, 1494, + 1, 0, 0, 0, 343, 1513, 1, 0, 0, 0, 345, + 1515, 1, 0, 0, 0, 347, 1521, 1, 0, 0, 0, + 349, 1523, 1, 0, 0, 0, 351, 1526, 1, 0, 0, + 0, 353, 1528, 1, 0, 0, 0, 355, 1531, 1, 0, + 0, 0, 357, 1537, 1, 0, 0, 0, 359, 1546, 1, + 0, 0, 0, 361, 1548, 1, 0, 0, 0, 363, 1550, + 1, 0, 0, 0, 365, 1552, 1, 0, 0, 0, 367, + 1554, 1, 0, 0, 0, 369, 1556, 1, 0, 0, 0, + 371, 1558, 1, 0, 0, 0, 373, 1560, 1, 0, 0, + 0, 375, 1562, 1, 0, 0, 0, 377, 1564, 1, 0, + 0, 0, 379, 1566, 1, 0, 0, 0, 381, 1568, 1, + 0, 0, 0, 383, 1570, 1, 0, 0, 0, 385, 1572, + 1, 0, 0, 0, 387, 1574, 1, 0, 0, 0, 389, + 1576, 1, 0, 0, 0, 391, 1578, 1, 0, 0, 0, + 393, 1580, 1, 0, 0, 0, 395, 1582, 1, 0, 0, + 0, 397, 1584, 1, 0, 0, 0, 399, 1586, 1, 0, + 0, 0, 401, 1588, 1, 0, 0, 0, 403, 1590, 1, + 0, 0, 0, 405, 1592, 1, 0, 0, 0, 407, 1594, + 1, 0, 0, 0, 409, 1596, 1, 0, 0, 0, 411, + 412, 5, 42, 0, 0, 412, 2, 1, 0, 0, 0, + 413, 414, 5, 40, 0, 0, 414, 4, 1, 0, 0, + 0, 415, 416, 5, 41, 0, 0, 416, 6, 1, 0, + 0, 0, 417, 418, 5, 123, 0, 0, 418, 8, 1, + 0, 0, 0, 419, 420, 5, 125, 0, 0, 420, 10, + 1, 0, 0, 0, 421, 422, 5, 59, 0, 0, 422, + 12, 1, 0, 0, 0, 423, 424, 5, 46, 0, 0, + 424, 14, 1, 0, 0, 0, 425, 426, 5, 44, 0, + 0, 426, 16, 1, 0, 0, 0, 427, 428, 5, 97, + 0, 0, 428, 18, 1, 0, 0, 0, 429, 430, 5, + 124, 0, 0, 430, 20, 1, 0, 0, 0, 431, 432, + 5, 47, 0, 0, 432, 22, 1, 0, 0, 0, 433, + 434, 5, 94, 0, 0, 434, 24, 1, 0, 0, 0, + 435, 436, 5, 43, 0, 0, 436, 26, 1, 0, 0, + 0, 437, 438, 5, 63, 0, 0, 438, 28, 1, 0, + 0, 0, 439, 440, 5, 33, 0, 0, 440, 30, 1, + 0, 0, 0, 441, 442, 5, 91, 0, 0, 442, 32, + 1, 0, 0, 0, 443, 444, 5, 93, 0, 0, 444, + 34, 1, 0, 0, 0, 445, 446, 5, 124, 0, 0, + 446, 447, 5, 124, 0, 0, 447, 36, 1, 0, 0, + 0, 448, 449, 5, 38, 0, 0, 449, 450, 5, 38, + 0, 0, 450, 38, 1, 0, 0, 0, 451, 452, 5, + 61, 0, 0, 452, 40, 1, 0, 0, 0, 453, 454, + 5, 33, 0, 0, 454, 455, 5, 61, 0, 0, 455, + 42, 1, 0, 0, 0, 456, 457, 5, 60, 0, 0, + 457, 44, 1, 0, 0, 0, 458, 459, 5, 62, 0, + 0, 459, 46, 1, 0, 0, 0, 460, 461, 5, 60, + 0, 0, 461, 462, 5, 61, 0, 0, 462, 48, 1, + 0, 0, 0, 463, 464, 5, 62, 0, 0, 464, 465, + 5, 61, 0, 0, 465, 50, 1, 0, 0, 0, 466, + 467, 5, 45, 0, 0, 467, 52, 1, 0, 0, 0, + 468, 469, 5, 94, 0, 0, 469, 470, 5, 94, 0, + 0, 470, 54, 1, 0, 0, 0, 471, 472, 5, 116, + 0, 0, 472, 473, 5, 114, 0, 0, 473, 474, 5, + 117, 0, 0, 474, 475, 5, 101, 0, 0, 475, 56, + 1, 0, 0, 0, 476, 477, 5, 102, 0, 0, 477, + 478, 5, 97, 0, 0, 478, 479, 5, 108, 0, 0, + 479, 480, 5, 115, 0, 0, 480, 481, 5, 101, 0, + 0, 481, 58, 1, 0, 0, 0, 482, 483, 3, 361, + 180, 0, 483, 484, 3, 359, 179, 0, 484, 485, 3, + 395, 197, 0, 485, 486, 3, 367, 183, 0, 486, 60, + 1, 0, 0, 0, 487, 488, 3, 389, 194, 0, 488, + 489, 3, 393, 196, 0, 489, 490, 3, 367, 183, 0, + 490, 491, 3, 369, 184, 0, 491, 492, 3, 375, 187, + 0, 492, 493, 3, 405, 202, 0, 493, 62, 1, 0, + 0, 0, 494, 495, 3, 395, 197, 0, 495, 496, 3, + 367, 183, 0, 496, 497, 3, 381, 190, 0, 497, 498, + 3, 367, 183, 0, 498, 499, 3, 363, 181, 0, 499, + 500, 3, 397, 198, 0, 500, 64, 1, 0, 0, 0, + 501, 502, 3, 365, 182, 0, 502, 503, 3, 375, 187, + 0, 503, 504, 3, 395, 197, 0, 504, 505, 3, 397, + 198, 0, 505, 506, 3, 375, 187, 0, 506, 507, 3, + 385, 192, 0, 507, 508, 3, 363, 181, 0, 508, 509, + 3, 397, 198, 0, 509, 66, 1, 0, 0, 0, 510, + 511, 3, 393, 196, 0, 511, 512, 3, 367, 183, 0, + 512, 513, 3, 365, 182, 0, 513, 514, 3, 399, 199, + 0, 514, 515, 3, 363, 181, 0, 515, 516, 3, 367, + 183, 0, 516, 517, 3, 365, 182, 0, 517, 68, 1, + 0, 0, 0, 518, 519, 3, 359, 179, 0, 519, 520, + 3, 395, 197, 0, 520, 70, 1, 0, 0, 0, 521, + 522, 3, 363, 181, 0, 522, 523, 3, 387, 193, 0, + 523, 524, 3, 385, 192, 0, 524, 525, 3, 395, 197, + 0, 525, 526, 3, 397, 198, 0, 526, 527, 3, 393, + 196, 0, 527, 528, 3, 399, 199, 0, 528, 529, 3, + 363, 181, 0, 529, 530, 3, 397, 198, 0, 530, 72, + 1, 0, 0, 0, 531, 532, 3, 403, 201, 0, 532, + 533, 3, 373, 186, 0, 533, 534, 3, 367, 183, 0, + 534, 535, 3, 393, 196, 0, 535, 536, 3, 367, 183, + 0, 536, 74, 1, 0, 0, 0, 537, 538, 3, 365, + 182, 0, 538, 539, 3, 367, 183, 0, 539, 540, 3, + 395, 197, 0, 540, 541, 3, 363, 181, 0, 541, 542, + 3, 393, 196, 0, 542, 543, 3, 375, 187, 0, 543, + 544, 3, 361, 180, 0, 544, 545, 3, 367, 183, 0, + 545, 76, 1, 0, 0, 0, 546, 547, 3, 359, 179, + 0, 547, 548, 3, 395, 197, 0, 548, 549, 3, 379, + 189, 0, 549, 78, 1, 0, 0, 0, 550, 551, 3, + 369, 184, 0, 551, 552, 3, 393, 196, 0, 552, 553, + 3, 387, 193, 0, 553, 554, 3, 383, 191, 0, 554, + 80, 1, 0, 0, 0, 555, 556, 3, 385, 192, 0, + 556, 557, 3, 359, 179, 0, 557, 558, 3, 383, 191, + 0, 558, 559, 3, 367, 183, 0, 559, 560, 3, 365, + 182, 0, 560, 82, 1, 0, 0, 0, 561, 562, 3, + 371, 185, 0, 562, 563, 3, 393, 196, 0, 563, 564, + 3, 387, 193, 0, 564, 565, 3, 399, 199, 0, 565, + 567, 3, 389, 194, 0, 566, 568, 3, 355, 177, 0, + 567, 566, 1, 0, 0, 0, 568, 569, 1, 0, 0, + 0, 569, 567, 1, 0, 0, 0, 569, 570, 1, 0, + 0, 0, 570, 571, 1, 0, 0, 0, 571, 572, 3, + 361, 180, 0, 572, 573, 3, 407, 203, 0, 573, 84, + 1, 0, 0, 0, 574, 575, 3, 371, 185, 0, 575, + 576, 3, 393, 196, 0, 576, 577, 3, 387, 193, 0, + 577, 578, 3, 399, 199, 0, 578, 579, 3, 389, 194, + 0, 579, 580, 5, 95, 0, 0, 580, 581, 3, 363, + 181, 0, 581, 582, 3, 387, 193, 0, 582, 583, 3, + 385, 192, 0, 583, 584, 3, 363, 181, 0, 584, 585, + 3, 359, 179, 0, 585, 586, 3, 397, 198, 0, 586, + 86, 1, 0, 0, 0, 587, 588, 3, 373, 186, 0, + 588, 589, 3, 359, 179, 0, 589, 590, 3, 401, 200, + 0, 590, 591, 3, 375, 187, 0, 591, 592, 3, 385, + 192, 0, 592, 593, 3, 371, 185, 0, 593, 88, 1, + 0, 0, 0, 594, 595, 3, 387, 193, 0, 595, 596, + 3, 393, 196, 0, 596, 597, 3, 365, 182, 0, 597, + 598, 3, 367, 183, 0, 598, 600, 3, 393, 196, 0, + 599, 601, 3, 355, 177, 0, 600, 599, 1, 0, 0, + 0, 601, 602, 1, 0, 0, 0, 602, 600, 1, 0, + 0, 0, 602, 603, 1, 0, 0, 0, 603, 604, 1, + 0, 0, 0, 604, 605, 3, 361, 180, 0, 605, 606, + 3, 407, 203, 0, 606, 90, 1, 0, 0, 0, 607, + 608, 3, 375, 187, 0, 608, 609, 3, 385, 192, 0, + 609, 610, 3, 397, 198, 0, 610, 611, 3, 367, 183, + 0, 611, 612, 3, 393, 196, 0, 612, 613, 3, 385, + 192, 0, 613, 614, 3, 359, 179, 0, 614, 616, 3, + 381, 190, 0, 615, 617, 3, 355, 177, 0, 616, 615, + 1, 0, 0, 0, 617, 618, 1, 0, 0, 0, 618, + 616, 1, 0, 0, 0, 618, 619, 1, 0, 0, 0, + 619, 620, 1, 0, 0, 0, 620, 621, 3, 395, 197, + 0, 621, 622, 3, 387, 193, 0, 622, 623, 3, 393, + 196, 0, 623, 625, 3, 397, 198, 0, 624, 626, 3, + 355, 177, 0, 625, 624, 1, 0, 0, 0, 626, 627, + 1, 0, 0, 0, 627, 625, 1, 0, 0, 0, 627, + 628, 1, 0, 0, 0, 628, 629, 1, 0, 0, 0, + 629, 630, 3, 361, 180, 0, 630, 631, 3, 407, 203, + 0, 631, 92, 1, 0, 0, 0, 632, 633, 3, 359, + 179, 0, 633, 634, 3, 395, 197, 0, 634, 635, 3, + 363, 181, 0, 635, 94, 1, 0, 0, 0, 636, 637, + 3, 365, 182, 0, 637, 638, 3, 367, 183, 0, 638, + 639, 3, 395, 197, 0, 639, 640, 3, 363, 181, 0, + 640, 96, 1, 0, 0, 0, 641, 642, 3, 381, 190, + 0, 642, 643, 3, 375, 187, 0, 643, 644, 3, 383, + 191, 0, 644, 645, 3, 375, 187, 0, 645, 646, 3, + 397, 198, 0, 646, 98, 1, 0, 0, 0, 647, 648, + 3, 387, 193, 0, 648, 649, 3, 369, 184, 0, 649, + 650, 3, 369, 184, 0, 650, 651, 3, 395, 197, 0, + 651, 652, 3, 367, 183, 0, 652, 653, 3, 397, 198, + 0, 653, 100, 1, 0, 0, 0, 654, 655, 3, 397, + 198, 0, 655, 656, 3, 367, 183, 0, 656, 657, 3, + 405, 202, 0, 657, 658, 3, 397, 198, 0, 658, 659, + 3, 381, 190, 0, 659, 660, 3, 375, 187, 0, 660, + 661, 3, 383, 191, 0, 661, 662, 3, 375, 187, 0, + 662, 663, 3, 397, 198, 0, 663, 102, 1, 0, 0, + 0, 664, 665, 3, 401, 200, 0, 665, 666, 3, 359, + 179, 0, 666, 667, 3, 381, 190, 0, 667, 668, 3, + 399, 199, 0, 668, 669, 3, 367, 183, 0, 669, 670, + 3, 395, 197, 0, 670, 104, 1, 0, 0, 0, 671, + 672, 3, 381, 190, 0, 672, 673, 3, 387, 193, 0, + 673, 674, 3, 359, 179, 0, 674, 675, 3, 365, 182, + 0, 675, 106, 1, 0, 0, 0, 676, 677, 3, 395, + 197, 0, 677, 678, 3, 375, 187, 0, 678, 679, 3, + 381, 190, 0, 679, 680, 3, 367, 183, 0, 680, 681, + 3, 385, 192, 0, 681, 682, 3, 397, 198, 0, 682, + 108, 1, 0, 0, 0, 683, 684, 3, 375, 187, 0, + 684, 685, 3, 385, 192, 0, 685, 686, 3, 397, 198, + 0, 686, 687, 3, 387, 193, 0, 687, 110, 1, 0, + 0, 0, 688, 689, 3, 363, 181, 0, 689, 690, 3, + 381, 190, 0, 690, 691, 3, 367, 183, 0, 691, 692, + 3, 359, 179, 0, 692, 693, 3, 393, 196, 0, 693, + 112, 1, 0, 0, 0, 694, 695, 3, 365, 182, 0, + 695, 696, 3, 393, 196, 0, 696, 697, 3, 387, 193, + 0, 697, 698, 3, 389, 194, 0, 698, 114, 1, 0, + 0, 0, 699, 700, 3, 363, 181, 0, 700, 701, 3, + 393, 196, 0, 701, 702, 3, 367, 183, 0, 702, 703, + 3, 359, 179, 0, 703, 704, 3, 397, 198, 0, 704, + 705, 3, 367, 183, 0, 705, 116, 1, 0, 0, 0, + 706, 707, 3, 359, 179, 0, 707, 708, 3, 365, 182, + 0, 708, 709, 3, 365, 182, 0, 709, 118, 1, 0, + 0, 0, 710, 711, 3, 397, 198, 0, 711, 712, 3, + 387, 193, 0, 712, 120, 1, 0, 0, 0, 713, 714, + 3, 365, 182, 0, 714, 715, 3, 359, 179, 0, 715, + 716, 3, 397, 198, 0, 716, 717, 3, 359, 179, 0, + 717, 122, 1, 0, 0, 0, 718, 719, 3, 383, 191, + 0, 719, 720, 3, 387, 193, 0, 720, 721, 3, 401, + 200, 0, 721, 722, 3, 367, 183, 0, 722, 124, 1, + 0, 0, 0, 723, 724, 3, 363, 181, 0, 724, 725, + 3, 387, 193, 0, 725, 726, 3, 389, 194, 0, 726, + 727, 3, 407, 203, 0, 727, 126, 1, 0, 0, 0, + 728, 729, 3, 375, 187, 0, 729, 730, 3, 385, 192, + 0, 730, 731, 3, 395, 197, 0, 731, 732, 3, 367, + 183, 0, 732, 733, 3, 393, 196, 0, 733, 734, 3, + 397, 198, 0, 734, 128, 1, 0, 0, 0, 735, 736, + 3, 365, 182, 0, 736, 737, 3, 367, 183, 0, 737, + 738, 3, 381, 190, 0, 738, 739, 3, 367, 183, 0, + 739, 740, 3, 397, 198, 0, 740, 741, 3, 367, 183, + 0, 741, 130, 1, 0, 0, 0, 742, 743, 3, 403, + 201, 0, 743, 744, 3, 375, 187, 0, 744, 745, 3, + 397, 198, 0, 745, 746, 3, 373, 186, 0, 746, 132, + 1, 0, 0, 0, 747, 748, 3, 399, 199, 0, 748, + 749, 3, 395, 197, 0, 749, 750, 3, 375, 187, 0, + 750, 751, 3, 385, 192, 0, 751, 752, 3, 371, 185, + 0, 752, 134, 1, 0, 0, 0, 753, 754, 3, 365, + 182, 0, 754, 755, 3, 367, 183, 0, 755, 756, 3, + 369, 184, 0, 756, 757, 3, 359, 179, 0, 757, 758, + 3, 399, 199, 0, 758, 759, 3, 381, 190, 0, 759, + 760, 3, 397, 198, 0, 760, 136, 1, 0, 0, 0, + 761, 762, 3, 371, 185, 0, 762, 763, 3, 393, 196, + 0, 763, 764, 3, 359, 179, 0, 764, 765, 3, 389, + 194, 0, 765, 766, 3, 373, 186, 0, 766, 138, 1, + 0, 0, 0, 767, 768, 3, 359, 179, 0, 768, 769, + 3, 381, 190, 0, 769, 770, 3, 381, 190, 0, 770, + 140, 1, 0, 0, 0, 771, 772, 3, 387, 193, 0, + 772, 773, 3, 389, 194, 0, 773, 774, 3, 397, 198, + 0, 774, 775, 3, 375, 187, 0, 775, 776, 3, 387, + 193, 0, 776, 777, 3, 385, 192, 0, 777, 778, 3, + 359, 179, 0, 778, 779, 3, 381, 190, 0, 779, 142, + 1, 0, 0, 0, 780, 781, 3, 395, 197, 0, 781, + 782, 3, 367, 183, 0, 782, 783, 3, 393, 196, 0, + 783, 784, 3, 401, 200, 0, 784, 785, 3, 375, 187, + 0, 785, 786, 3, 363, 181, 0, 786, 787, 3, 367, + 183, 0, 787, 144, 1, 0, 0, 0, 788, 789, 3, + 361, 180, 0, 789, 790, 3, 375, 187, 0, 790, 791, + 3, 385, 192, 0, 791, 792, 3, 365, 182, 0, 792, + 146, 1, 0, 0, 0, 793, 794, 3, 399, 199, 0, + 794, 795, 3, 385, 192, 0, 795, 796, 3, 365, 182, + 0, 796, 797, 3, 367, 183, 0, 797, 798, 3, 369, + 184, 0, 798, 148, 1, 0, 0, 0, 799, 800, 3, + 383, 191, 0, 800, 801, 3, 375, 187, 0, 801, 802, + 3, 385, 192, 0, 802, 803, 3, 399, 199, 0, 803, + 804, 3, 395, 197, 0, 804, 150, 1, 0, 0, 0, + 805, 806, 3, 399, 199, 0, 806, 807, 3, 385, 192, + 0, 807, 808, 3, 375, 187, 0, 808, 809, 3, 387, + 193, 0, 809, 810, 3, 385, 192, 0, 810, 152, 1, + 0, 0, 0, 811, 812, 3, 369, 184, 0, 812, 813, + 3, 375, 187, 0, 813, 814, 3, 381, 190, 0, 814, + 815, 3, 397, 198, 0, 815, 816, 3, 367, 183, 0, + 816, 817, 3, 393, 196, 0, 817, 154, 1, 0, 0, + 0, 818, 819, 3, 385, 192, 0, 819, 820, 3, 387, + 193, 0, 820, 821, 3, 397, 198, 0, 821, 156, 1, + 0, 0, 0, 822, 823, 3, 375, 187, 0, 823, 824, + 3, 385, 192, 0, 824, 158, 1, 0, 0, 0, 825, + 826, 3, 395, 197, 0, 826, 827, 3, 397, 198, 0, + 827, 828, 3, 393, 196, 0, 828, 160, 1, 0, 0, + 0, 829, 830, 3, 381, 190, 0, 830, 831, 3, 359, + 179, 0, 831, 832, 3, 385, 192, 0, 832, 833, 3, + 371, 185, 0, 833, 162, 1, 0, 0, 0, 834, 835, + 3, 381, 190, 0, 835, 836, 3, 359, 179, 0, 836, + 837, 3, 385, 192, 0, 837, 838, 3, 371, 185, 0, + 838, 839, 3, 383, 191, 0, 839, 840, 3, 359, 179, + 0, 840, 841, 3, 397, 198, 0, 841, 842, 3, 363, + 181, 0, 842, 843, 3, 373, 186, 0, 843, 844, 3, + 367, 183, 0, 844, 845, 3, 395, 197, 0, 845, 164, + 1, 0, 0, 0, 846, 847, 3, 365, 182, 0, 847, + 848, 3, 359, 179, 0, 848, 849, 3, 397, 198, 0, + 849, 850, 3, 359, 179, 0, 850, 851, 3, 397, 198, + 0, 851, 852, 3, 407, 203, 0, 852, 853, 3, 389, + 194, 0, 853, 854, 3, 367, 183, 0, 854, 166, 1, + 0, 0, 0, 855, 856, 3, 361, 180, 0, 856, 857, + 3, 387, 193, 0, 857, 858, 3, 399, 199, 0, 858, + 859, 3, 385, 192, 0, 859, 860, 3, 365, 182, 0, + 860, 168, 1, 0, 0, 0, 861, 862, 3, 375, 187, + 0, 862, 863, 3, 393, 196, 0, 863, 864, 3, 375, + 187, 0, 864, 170, 1, 0, 0, 0, 865, 866, 3, + 399, 199, 0, 866, 867, 3, 393, 196, 0, 867, 868, + 3, 375, 187, 0, 868, 172, 1, 0, 0, 0, 869, + 870, 3, 361, 180, 0, 870, 871, 3, 385, 192, 0, + 871, 872, 3, 387, 193, 0, 872, 873, 3, 365, 182, + 0, 873, 874, 3, 367, 183, 0, 874, 174, 1, 0, + 0, 0, 875, 876, 3, 393, 196, 0, 876, 877, 3, + 359, 179, 0, 877, 878, 3, 385, 192, 0, 878, 879, + 3, 365, 182, 0, 879, 176, 1, 0, 0, 0, 880, + 881, 3, 359, 179, 0, 881, 882, 3, 361, 180, 0, + 882, 883, 3, 395, 197, 0, 883, 178, 1, 0, 0, + 0, 884, 885, 3, 363, 181, 0, 885, 886, 3, 367, + 183, 0, 886, 887, 3, 375, 187, 0, 887, 888, 3, + 381, 190, 0, 888, 180, 1, 0, 0, 0, 889, 890, + 3, 369, 184, 0, 890, 891, 3, 381, 190, 0, 891, + 892, 3, 387, 193, 0, 892, 893, 3, 387, 193, 0, + 893, 894, 3, 393, 196, 0, 894, 182, 1, 0, 0, + 0, 895, 896, 3, 393, 196, 0, 896, 897, 3, 387, + 193, 0, 897, 898, 3, 399, 199, 0, 898, 899, 3, + 385, 192, 0, 899, 900, 3, 365, 182, 0, 900, 184, + 1, 0, 0, 0, 901, 902, 3, 363, 181, 0, 902, + 903, 3, 387, 193, 0, 903, 904, 3, 385, 192, 0, + 904, 905, 3, 363, 181, 0, 905, 906, 3, 359, 179, + 0, 906, 907, 3, 397, 198, 0, 907, 186, 1, 0, + 0, 0, 908, 909, 3, 395, 197, 0, 909, 910, 3, + 397, 198, 0, 910, 911, 3, 393, 196, 0, 911, 912, + 3, 381, 190, 0, 912, 913, 3, 367, 183, 0, 913, + 914, 3, 385, 192, 0, 914, 188, 1, 0, 0, 0, + 915, 916, 3, 399, 199, 0, 916, 917, 3, 363, 181, + 0, 917, 918, 3, 359, 179, 0, 918, 919, 3, 395, + 197, 0, 919, 920, 3, 367, 183, 0, 920, 190, 1, + 0, 0, 0, 921, 922, 3, 381, 190, 0, 922, 923, + 3, 363, 181, 0, 923, 924, 3, 359, 179, 0, 924, + 925, 3, 395, 197, 0, 925, 926, 3, 367, 183, 0, + 926, 192, 1, 0, 0, 0, 927, 928, 3, 367, 183, + 0, 928, 929, 3, 385, 192, 0, 929, 930, 3, 363, + 181, 0, 930, 931, 3, 387, 193, 0, 931, 932, 3, + 365, 182, 0, 932, 933, 3, 367, 183, 0, 933, 934, + 5, 95, 0, 0, 934, 935, 3, 369, 184, 0, 935, + 936, 3, 387, 193, 0, 936, 937, 3, 393, 196, 0, + 937, 938, 5, 95, 0, 0, 938, 939, 3, 399, 199, + 0, 939, 940, 3, 393, 196, 0, 940, 941, 3, 375, + 187, 0, 941, 194, 1, 0, 0, 0, 942, 943, 3, + 369, 184, 0, 943, 944, 3, 387, 193, 0, 944, 945, + 3, 393, 196, 0, 945, 196, 1, 0, 0, 0, 946, + 947, 3, 363, 181, 0, 947, 948, 3, 387, 193, 0, + 948, 949, 3, 385, 192, 0, 949, 950, 3, 397, 198, + 0, 950, 951, 3, 359, 179, 0, 951, 952, 3, 375, + 187, 0, 952, 953, 3, 385, 192, 0, 953, 954, 3, + 395, 197, 0, 954, 198, 1, 0, 0, 0, 955, 956, + 3, 395, 197, 0, 956, 957, 3, 397, 198, 0, 957, + 958, 3, 393, 196, 0, 958, 959, 3, 395, 197, 0, + 959, 960, 3, 397, 198, 0, 960, 961, 3, 359, 179, + 0, 961, 962, 3, 393, 196, 0, 962, 963, 3, 397, + 198, 0, 963, 964, 3, 395, 197, 0, 964, 200, 1, + 0, 0, 0, 965, 966, 3, 395, 197, 0, 966, 967, + 3, 397, 198, 0, 967, 968, 3, 393, 196, 0, 968, + 969, 3, 367, 183, 0, 969, 970, 3, 385, 192, 0, + 970, 971, 3, 365, 182, 0, 971, 972, 3, 395, 197, + 0, 972, 202, 1, 0, 0, 0, 973, 974, 3, 395, + 197, 0, 974, 975, 3, 397, 198, 0, 975, 976, 3, + 393, 196, 0, 976, 977, 3, 361, 180, 0, 977, 978, + 3, 367, 183, 0, 978, 979, 3, 369, 184, 0, 979, + 980, 3, 387, 193, 0, 980, 981, 3, 393, 196, 0, + 981, 982, 3, 367, 183, 0, 982, 204, 1, 0, 0, + 0, 983, 984, 3, 395, 197, 0, 984, 985, 3, 397, + 198, 0, 985, 986, 3, 393, 196, 0, 986, 987, 3, + 359, 179, 0, 987, 988, 3, 369, 184, 0, 988, 989, + 3, 397, 198, 0, 989, 990, 3, 367, 183, 0, 990, + 991, 3, 393, 196, 0, 991, 206, 1, 0, 0, 0, + 992, 993, 3, 407, 203, 0, 993, 994, 3, 367, 183, + 0, 994, 995, 3, 359, 179, 0, 995, 996, 3, 393, + 196, 0, 996, 208, 1, 0, 0, 0, 997, 998, 3, + 383, 191, 0, 998, 999, 3, 387, 193, 0, 999, 1000, + 3, 385, 192, 0, 1000, 1001, 3, 397, 198, 0, 1001, + 1002, 3, 373, 186, 0, 1002, 210, 1, 0, 0, 0, + 1003, 1004, 3, 365, 182, 0, 1004, 1005, 3, 359, 179, + 0, 1005, 1006, 3, 407, 203, 0, 1006, 212, 1, 0, + 0, 0, 1007, 1008, 3, 373, 186, 0, 1008, 1009, 3, + 387, 193, 0, 1009, 1010, 3, 399, 199, 0, 1010, 1011, + 3, 393, 196, 0, 1011, 1012, 3, 395, 197, 0, 1012, + 214, 1, 0, 0, 0, 1013, 1014, 3, 383, 191, 0, + 1014, 1015, 3, 375, 187, 0, 1015, 1016, 3, 385, 192, + 0, 1016, 1017, 3, 399, 199, 0, 1017, 1018, 3, 397, + 198, 0, 1018, 1019, 3, 367, 183, 0, 1019, 1020, 3, + 395, 197, 0, 1020, 216, 1, 0, 0, 0, 1021, 1022, + 3, 395, 197, 0, 1022, 1023, 3, 367, 183, 0, 1023, + 1024, 3, 363, 181, 0, 1024, 1025, 3, 387, 193, 0, + 1025, 1026, 3, 385, 192, 0, 1026, 1027, 3, 365, 182, + 0, 1027, 1028, 3, 395, 197, 0, 1028, 218, 1, 0, + 0, 0, 1029, 1030, 3, 397, 198, 0, 1030, 1031, 3, + 375, 187, 0, 1031, 1032, 3, 383, 191, 0, 1032, 1033, + 3, 367, 183, 0, 1033, 1034, 3, 409, 204, 0, 1034, + 1035, 3, 387, 193, 0, 1035, 1036, 3, 385, 192, 0, + 1036, 1037, 3, 367, 183, 0, 1037, 220, 1, 0, 0, + 0, 1038, 1039, 3, 397, 198, 0, 1039, 1040, 3, 409, + 204, 0, 1040, 222, 1, 0, 0, 0, 1041, 1042, 3, + 385, 192, 0, 1042, 1043, 3, 387, 193, 0, 1043, 1044, + 3, 403, 201, 0, 1044, 224, 1, 0, 0, 0, 1045, + 1046, 3, 399, 199, 0, 1046, 1047, 3, 399, 199, 0, + 1047, 1048, 3, 375, 187, 0, 1048, 1049, 3, 365, 182, + 0, 1049, 226, 1, 0, 0, 0, 1050, 1051, 3, 395, + 197, 0, 1051, 1052, 3, 397, 198, 0, 1052, 1053, 3, + 393, 196, 0, 1053, 1054, 3, 399, 199, 0, 1054, 1055, + 3, 399, 199, 0, 1055, 1056, 3, 375, 187, 0, 1056, + 1057, 3, 365, 182, 0, 1057, 228, 1, 0, 0, 0, + 1058, 1059, 3, 395, 197, 0, 1059, 1060, 3, 373, 186, + 0, 1060, 1061, 3, 359, 179, 0, 1061, 1062, 5, 49, + 0, 0, 1062, 230, 1, 0, 0, 0, 1063, 1064, 3, + 395, 197, 0, 1064, 1065, 3, 373, 186, 0, 1065, 1066, + 3, 359, 179, 0, 1066, 1067, 5, 50, 0, 0, 1067, + 1068, 5, 53, 0, 0, 1068, 1069, 5, 54, 0, 0, + 1069, 232, 1, 0, 0, 0, 1070, 1071, 3, 395, 197, + 0, 1071, 1072, 3, 373, 186, 0, 1072, 1073, 3, 359, + 179, 0, 1073, 1074, 5, 51, 0, 0, 1074, 1075, 5, + 56, 0, 0, 1075, 1076, 5, 52, 0, 0, 1076, 234, + 1, 0, 0, 0, 1077, 1078, 3, 395, 197, 0, 1078, + 1079, 3, 373, 186, 0, 1079, 1080, 3, 359, 179, 0, + 1080, 1081, 5, 53, 0, 0, 1081, 1082, 5, 49, 0, + 0, 1082, 1083, 5, 50, 0, 0, 1083, 236, 1, 0, + 0, 0, 1084, 1085, 3, 383, 191, 0, 1085, 1086, 3, + 365, 182, 0, 1086, 1087, 5, 53, 0, 0, 1087, 238, + 1, 0, 0, 0, 1088, 1089, 3, 363, 181, 0, 1089, + 1090, 3, 387, 193, 0, 1090, 1091, 3, 359, 179, 0, + 1091, 1092, 3, 381, 190, 0, 1092, 1093, 3, 367, 183, + 0, 1093, 1094, 3, 395, 197, 0, 1094, 1095, 3, 363, + 181, 0, 1095, 1096, 3, 367, 183, 0, 1096, 240, 1, + 0, 0, 0, 1097, 1098, 3, 375, 187, 0, 1098, 1099, + 3, 369, 184, 0, 1099, 242, 1, 0, 0, 0, 1100, + 1101, 3, 395, 197, 0, 1101, 1102, 3, 397, 198, 0, + 1102, 1103, 3, 393, 196, 0, 1103, 1104, 3, 381, 190, + 0, 1104, 1105, 3, 359, 179, 0, 1105, 1106, 3, 385, + 192, 0, 1106, 1107, 3, 371, 185, 0, 1107, 244, 1, + 0, 0, 0, 1108, 1109, 3, 395, 197, 0, 1109, 1110, + 3, 397, 198, 0, 1110, 1111, 3, 393, 196, 0, 1111, + 1112, 3, 365, 182, 0, 1112, 1113, 3, 397, 198, 0, + 1113, 246, 1, 0, 0, 0, 1114, 1115, 3, 395, 197, + 0, 1115, 1116, 3, 359, 179, 0, 1116, 1117, 3, 383, + 191, 0, 1117, 1118, 3, 367, 183, 0, 1118, 1119, 3, + 397, 198, 0, 1119, 1120, 3, 367, 183, 0, 1120, 1121, + 3, 393, 196, 0, 1121, 1122, 3, 383, 191, 0, 1122, + 248, 1, 0, 0, 0, 1123, 1124, 3, 375, 187, 0, + 1124, 1125, 3, 395, 197, 0, 1125, 1126, 3, 375, 187, + 0, 1126, 1127, 3, 393, 196, 0, 1127, 1128, 3, 375, + 187, 0, 1128, 250, 1, 0, 0, 0, 1129, 1130, 3, + 375, 187, 0, 1130, 1131, 3, 395, 197, 0, 1131, 1132, + 3, 399, 199, 0, 1132, 1133, 3, 393, 196, 0, 1133, + 1134, 3, 375, 187, 0, 1134, 252, 1, 0, 0, 0, + 1135, 1136, 3, 375, 187, 0, 1136, 1137, 3, 395, 197, + 0, 1137, 1138, 3, 361, 180, 0, 1138, 1139, 3, 381, + 190, 0, 1139, 1140, 3, 359, 179, 0, 1140, 1141, 3, + 385, 192, 0, 1141, 1142, 3, 379, 189, 0, 1142, 254, + 1, 0, 0, 0, 1143, 1144, 3, 375, 187, 0, 1144, + 1145, 3, 395, 197, 0, 1145, 1146, 3, 381, 190, 0, + 1146, 1147, 3, 375, 187, 0, 1147, 1148, 3, 397, 198, + 0, 1148, 1149, 3, 367, 183, 0, 1149, 1150, 3, 393, + 196, 0, 1150, 1151, 3, 359, 179, 0, 1151, 1152, 3, + 381, 190, 0, 1152, 256, 1, 0, 0, 0, 1153, 1154, + 3, 375, 187, 0, 1154, 1155, 3, 395, 197, 0, 1155, + 1156, 3, 385, 192, 0, 1156, 1157, 3, 399, 199, 0, + 1157, 1158, 3, 383, 191, 0, 1158, 1159, 3, 367, 183, + 0, 1159, 1160, 3, 393, 196, 0, 1160, 1161, 3, 375, + 187, 0, 1161, 1162, 3, 363, 181, 0, 1162, 258, 1, + 0, 0, 0, 1163, 1164, 3, 393, 196, 0, 1164, 1165, + 3, 367, 183, 0, 1165, 1166, 3, 371, 185, 0, 1166, + 1167, 3, 367, 183, 0, 1167, 1168, 3, 405, 202, 0, + 1168, 260, 1, 0, 0, 0, 1169, 1170, 3, 395, 197, + 0, 1170, 1171, 3, 399, 199, 0, 1171, 1172, 3, 361, + 180, 0, 1172, 1173, 3, 395, 197, 0, 1173, 1174, 3, + 397, 198, 0, 1174, 1175, 3, 393, 196, 0, 1175, 262, + 1, 0, 0, 0, 1176, 1177, 3, 393, 196, 0, 1177, + 1178, 3, 367, 183, 0, 1178, 1179, 3, 389, 194, 0, + 1179, 1180, 3, 381, 190, 0, 1180, 1181, 3, 359, 179, + 0, 1181, 1182, 3, 363, 181, 0, 1182, 1183, 3, 367, + 183, 0, 1183, 264, 1, 0, 0, 0, 1184, 1185, 3, + 367, 183, 0, 1185, 1186, 3, 405, 202, 0, 1186, 1187, + 3, 375, 187, 0, 1187, 1188, 3, 395, 197, 0, 1188, + 1189, 3, 397, 198, 0, 1189, 1190, 3, 395, 197, 0, + 1190, 266, 1, 0, 0, 0, 1191, 1192, 3, 363, 181, + 0, 1192, 1193, 3, 387, 193, 0, 1193, 1194, 3, 399, + 199, 0, 1194, 1195, 3, 385, 192, 0, 1195, 1196, 3, + 397, 198, 0, 1196, 268, 1, 0, 0, 0, 1197, 1198, + 3, 395, 197, 0, 1198, 1199, 3, 399, 199, 0, 1199, + 1200, 3, 383, 191, 0, 1200, 270, 1, 0, 0, 0, + 1201, 1202, 3, 383, 191, 0, 1202, 1203, 3, 375, 187, + 0, 1203, 1204, 3, 385, 192, 0, 1204, 272, 1, 0, + 0, 0, 1205, 1206, 3, 383, 191, 0, 1206, 1207, 3, + 359, 179, 0, 1207, 1208, 3, 405, 202, 0, 1208, 274, + 1, 0, 0, 0, 1209, 1210, 3, 359, 179, 0, 1210, + 1211, 3, 401, 200, 0, 1211, 1212, 3, 371, 185, 0, + 1212, 276, 1, 0, 0, 0, 1213, 1214, 3, 395, 197, + 0, 1214, 1215, 3, 397, 198, 0, 1215, 1216, 3, 365, + 182, 0, 1216, 1217, 3, 367, 183, 0, 1217, 1218, 3, + 401, 200, 0, 1218, 278, 1, 0, 0, 0, 1219, 1220, + 3, 395, 197, 0, 1220, 1221, 3, 359, 179, 0, 1221, + 1222, 3, 383, 191, 0, 1222, 1223, 3, 389, 194, 0, + 1223, 1224, 3, 381, 190, 0, 1224, 1225, 3, 367, 183, + 0, 1225, 280, 1, 0, 0, 0, 1226, 1227, 3, 395, + 197, 0, 1227, 1228, 3, 367, 183, 0, 1228, 1229, 3, + 389, 194, 0, 1229, 1230, 3, 359, 179, 0, 1230, 1231, + 3, 393, 196, 0, 1231, 1232, 3, 359, 179, 0, 1232, + 1233, 3, 397, 198, 0, 1233, 1234, 3, 387, 193, 0, + 1234, 1235, 3, 393, 196, 0, 1235, 282, 1, 0, 0, + 0, 1236, 1240, 5, 60, 0, 0, 1237, 1239, 8, 0, + 0, 0, 1238, 1237, 1, 0, 0, 0, 1239, 1242, 1, + 0, 0, 0, 1240, 1238, 1, 0, 0, 0, 1240, 1241, + 1, 0, 0, 0, 1241, 1243, 1, 0, 0, 0, 1242, + 1240, 1, 0, 0, 0, 1243, 1244, 5, 62, 0, 0, + 1244, 284, 1, 0, 0, 0, 1245, 1247, 3, 339, 169, + 0, 1246, 1245, 1, 0, 0, 0, 1246, 1247, 1, 0, + 0, 0, 1247, 1248, 1, 0, 0, 0, 1248, 1249, 5, + 58, 0, 0, 1249, 286, 1, 0, 0, 0, 1250, 1251, + 3, 285, 142, 0, 1251, 1252, 3, 341, 170, 0, 1252, + 288, 1, 0, 0, 0, 1253, 1254, 5, 95, 0, 0, + 1254, 1255, 5, 58, 0, 0, 1255, 1258, 1, 0, 0, + 0, 1256, 1259, 3, 333, 166, 0, 1257, 1259, 3, 353, + 176, 0, 1258, 1256, 1, 0, 0, 0, 1258, 1257, 1, + 0, 0, 0, 1259, 1268, 1, 0, 0, 0, 1260, 1263, + 3, 337, 168, 0, 1261, 1263, 5, 46, 0, 0, 1262, + 1260, 1, 0, 0, 0, 1262, 1261, 1, 0, 0, 0, + 1263, 1266, 1, 0, 0, 0, 1264, 1262, 1, 0, 0, + 0, 1264, 1265, 1, 0, 0, 0, 1265, 1267, 1, 0, + 0, 0, 1266, 1264, 1, 0, 0, 0, 1267, 1269, 3, + 337, 168, 0, 1268, 1264, 1, 0, 0, 0, 1268, 1269, + 1, 0, 0, 0, 1269, 290, 1, 0, 0, 0, 1270, + 1271, 5, 63, 0, 0, 1271, 1272, 3, 335, 167, 0, + 1272, 292, 1, 0, 0, 0, 1273, 1274, 5, 36, 0, + 0, 1274, 1275, 3, 335, 167, 0, 1275, 294, 1, 0, + 0, 0, 1276, 1278, 5, 64, 0, 0, 1277, 1279, 7, + 1, 0, 0, 1278, 1277, 1, 0, 0, 0, 1279, 1280, + 1, 0, 0, 0, 1280, 1278, 1, 0, 0, 0, 1280, + 1281, 1, 0, 0, 0, 1281, 1291, 1, 0, 0, 0, + 1282, 1285, 5, 45, 0, 0, 1283, 1286, 7, 1, 0, + 0, 1284, 1286, 3, 353, 176, 0, 1285, 1283, 1, 0, + 0, 0, 1285, 1284, 1, 0, 0, 0, 1286, 1287, 1, + 0, 0, 0, 1287, 1285, 1, 0, 0, 0, 1287, 1288, + 1, 0, 0, 0, 1288, 1290, 1, 0, 0, 0, 1289, + 1282, 1, 0, 0, 0, 1290, 1293, 1, 0, 0, 0, + 1291, 1289, 1, 0, 0, 0, 1291, 1292, 1, 0, 0, + 0, 1292, 296, 1, 0, 0, 0, 1293, 1291, 1, 0, + 0, 0, 1294, 1295, 3, 295, 147, 0, 1295, 1296, 5, + 64, 0, 0, 1296, 298, 1, 0, 0, 0, 1297, 1299, + 3, 353, 176, 0, 1298, 1297, 1, 0, 0, 0, 1299, + 1300, 1, 0, 0, 0, 1300, 1298, 1, 0, 0, 0, + 1300, 1301, 1, 0, 0, 0, 1301, 300, 1, 0, 0, + 0, 1302, 1304, 3, 353, 176, 0, 1303, 1302, 1, 0, + 0, 0, 1304, 1307, 1, 0, 0, 0, 1305, 1303, 1, + 0, 0, 0, 1305, 1306, 1, 0, 0, 0, 1306, 1308, + 1, 0, 0, 0, 1307, 1305, 1, 0, 0, 0, 1308, + 1310, 5, 46, 0, 0, 1309, 1311, 3, 353, 176, 0, + 1310, 1309, 1, 0, 0, 0, 1311, 1312, 1, 0, 0, + 0, 1312, 1310, 1, 0, 0, 0, 1312, 1313, 1, 0, + 0, 0, 1313, 302, 1, 0, 0, 0, 1314, 1316, 3, + 353, 176, 0, 1315, 1314, 1, 0, 0, 0, 1316, 1317, + 1, 0, 0, 0, 1317, 1315, 1, 0, 0, 0, 1317, + 1318, 1, 0, 0, 0, 1318, 1319, 1, 0, 0, 0, + 1319, 1323, 5, 46, 0, 0, 1320, 1322, 3, 353, 176, + 0, 1321, 1320, 1, 0, 0, 0, 1322, 1325, 1, 0, + 0, 0, 1323, 1321, 1, 0, 0, 0, 1323, 1324, 1, + 0, 0, 0, 1324, 1326, 1, 0, 0, 0, 1325, 1323, + 1, 0, 0, 0, 1326, 1327, 3, 317, 158, 0, 1327, + 1344, 1, 0, 0, 0, 1328, 1330, 5, 46, 0, 0, + 1329, 1331, 3, 353, 176, 0, 1330, 1329, 1, 0, 0, + 0, 1331, 1332, 1, 0, 0, 0, 1332, 1330, 1, 0, + 0, 0, 1332, 1333, 1, 0, 0, 0, 1333, 1334, 1, + 0, 0, 0, 1334, 1335, 3, 317, 158, 0, 1335, 1344, + 1, 0, 0, 0, 1336, 1338, 3, 353, 176, 0, 1337, + 1336, 1, 0, 0, 0, 1338, 1339, 1, 0, 0, 0, + 1339, 1337, 1, 0, 0, 0, 1339, 1340, 1, 0, 0, + 0, 1340, 1341, 1, 0, 0, 0, 1341, 1342, 3, 317, + 158, 0, 1342, 1344, 1, 0, 0, 0, 1343, 1315, 1, + 0, 0, 0, 1343, 1328, 1, 0, 0, 0, 1343, 1337, + 1, 0, 0, 0, 1344, 304, 1, 0, 0, 0, 1345, + 1346, 5, 43, 0, 0, 1346, 1347, 3, 299, 149, 0, + 1347, 306, 1, 0, 0, 0, 1348, 1349, 5, 43, 0, + 0, 1349, 1350, 3, 301, 150, 0, 1350, 308, 1, 0, + 0, 0, 1351, 1352, 5, 43, 0, 0, 1352, 1353, 3, + 303, 151, 0, 1353, 310, 1, 0, 0, 0, 1354, 1355, + 5, 45, 0, 0, 1355, 1356, 3, 299, 149, 0, 1356, + 312, 1, 0, 0, 0, 1357, 1358, 5, 45, 0, 0, + 1358, 1359, 3, 301, 150, 0, 1359, 314, 1, 0, 0, + 0, 1360, 1361, 5, 45, 0, 0, 1361, 1362, 3, 303, + 151, 0, 1362, 316, 1, 0, 0, 0, 1363, 1365, 7, + 2, 0, 0, 1364, 1366, 7, 3, 0, 0, 1365, 1364, + 1, 0, 0, 0, 1365, 1366, 1, 0, 0, 0, 1366, + 1368, 1, 0, 0, 0, 1367, 1369, 3, 353, 176, 0, + 1368, 1367, 1, 0, 0, 0, 1369, 1370, 1, 0, 0, + 0, 1370, 1368, 1, 0, 0, 0, 1370, 1371, 1, 0, + 0, 0, 1371, 318, 1, 0, 0, 0, 1372, 1377, 5, + 39, 0, 0, 1373, 1376, 8, 4, 0, 0, 1374, 1376, + 3, 327, 163, 0, 1375, 1373, 1, 0, 0, 0, 1375, + 1374, 1, 0, 0, 0, 1376, 1379, 1, 0, 0, 0, + 1377, 1375, 1, 0, 0, 0, 1377, 1378, 1, 0, 0, + 0, 1378, 1380, 1, 0, 0, 0, 1379, 1377, 1, 0, + 0, 0, 1380, 1381, 5, 39, 0, 0, 1381, 320, 1, + 0, 0, 0, 1382, 1387, 5, 34, 0, 0, 1383, 1386, + 8, 5, 0, 0, 1384, 1386, 3, 327, 163, 0, 1385, + 1383, 1, 0, 0, 0, 1385, 1384, 1, 0, 0, 0, + 1386, 1389, 1, 0, 0, 0, 1387, 1385, 1, 0, 0, + 0, 1387, 1388, 1, 0, 0, 0, 1388, 1390, 1, 0, + 0, 0, 1389, 1387, 1, 0, 0, 0, 1390, 1391, 5, + 34, 0, 0, 1391, 322, 1, 0, 0, 0, 1392, 1393, + 5, 39, 0, 0, 1393, 1394, 5, 39, 0, 0, 1394, + 1395, 5, 39, 0, 0, 1395, 1407, 1, 0, 0, 0, + 1396, 1400, 5, 39, 0, 0, 1397, 1398, 5, 39, 0, + 0, 1398, 1400, 5, 39, 0, 0, 1399, 1396, 1, 0, + 0, 0, 1399, 1397, 1, 0, 0, 0, 1399, 1400, 1, + 0, 0, 0, 1400, 1403, 1, 0, 0, 0, 1401, 1404, + 8, 6, 0, 0, 1402, 1404, 3, 327, 163, 0, 1403, + 1401, 1, 0, 0, 0, 1403, 1402, 1, 0, 0, 0, + 1404, 1406, 1, 0, 0, 0, 1405, 1399, 1, 0, 0, + 0, 1406, 1409, 1, 0, 0, 0, 1407, 1405, 1, 0, + 0, 0, 1407, 1408, 1, 0, 0, 0, 1408, 1410, 1, + 0, 0, 0, 1409, 1407, 1, 0, 0, 0, 1410, 1411, + 5, 39, 0, 0, 1411, 1412, 5, 39, 0, 0, 1412, + 1413, 5, 39, 0, 0, 1413, 324, 1, 0, 0, 0, + 1414, 1415, 5, 34, 0, 0, 1415, 1416, 5, 34, 0, + 0, 1416, 1417, 5, 34, 0, 0, 1417, 1429, 1, 0, + 0, 0, 1418, 1422, 5, 34, 0, 0, 1419, 1420, 5, + 34, 0, 0, 1420, 1422, 5, 34, 0, 0, 1421, 1418, + 1, 0, 0, 0, 1421, 1419, 1, 0, 0, 0, 1421, + 1422, 1, 0, 0, 0, 1422, 1425, 1, 0, 0, 0, + 1423, 1426, 8, 7, 0, 0, 1424, 1426, 3, 327, 163, + 0, 1425, 1423, 1, 0, 0, 0, 1425, 1424, 1, 0, + 0, 0, 1426, 1428, 1, 0, 0, 0, 1427, 1421, 1, + 0, 0, 0, 1428, 1431, 1, 0, 0, 0, 1429, 1427, + 1, 0, 0, 0, 1429, 1430, 1, 0, 0, 0, 1430, + 1432, 1, 0, 0, 0, 1431, 1429, 1, 0, 0, 0, + 1432, 1433, 5, 34, 0, 0, 1433, 1434, 5, 34, 0, + 0, 1434, 1435, 5, 34, 0, 0, 1435, 326, 1, 0, + 0, 0, 1436, 1437, 5, 92, 0, 0, 1437, 1438, 7, + 8, 0, 0, 1438, 328, 1, 0, 0, 0, 1439, 1443, + 5, 40, 0, 0, 1440, 1442, 3, 355, 177, 0, 1441, + 1440, 1, 0, 0, 0, 1442, 1445, 1, 0, 0, 0, + 1443, 1441, 1, 0, 0, 0, 1443, 1444, 1, 0, 0, + 0, 1444, 1446, 1, 0, 0, 0, 1445, 1443, 1, 0, + 0, 0, 1446, 1447, 5, 41, 0, 0, 1447, 330, 1, + 0, 0, 0, 1448, 1452, 5, 91, 0, 0, 1449, 1451, + 3, 355, 177, 0, 1450, 1449, 1, 0, 0, 0, 1451, + 1454, 1, 0, 0, 0, 1452, 1450, 1, 0, 0, 0, + 1452, 1453, 1, 0, 0, 0, 1453, 1455, 1, 0, 0, + 0, 1454, 1452, 1, 0, 0, 0, 1455, 1456, 5, 93, + 0, 0, 1456, 332, 1, 0, 0, 0, 1457, 1460, 3, + 351, 175, 0, 1458, 1460, 5, 95, 0, 0, 1459, 1457, + 1, 0, 0, 0, 1459, 1458, 1, 0, 0, 0, 1460, + 334, 1, 0, 0, 0, 1461, 1464, 3, 333, 166, 0, + 1462, 1464, 3, 353, 176, 0, 1463, 1461, 1, 0, 0, + 0, 1463, 1462, 1, 0, 0, 0, 1464, 1470, 1, 0, + 0, 0, 1465, 1469, 3, 333, 166, 0, 1466, 1469, 3, + 353, 176, 0, 1467, 1469, 7, 9, 0, 0, 1468, 1465, + 1, 0, 0, 0, 1468, 1466, 1, 0, 0, 0, 1468, + 1467, 1, 0, 0, 0, 1469, 1472, 1, 0, 0, 0, + 1470, 1468, 1, 0, 0, 0, 1470, 1471, 1, 0, 0, + 0, 1471, 336, 1, 0, 0, 0, 1472, 1470, 1, 0, + 0, 0, 1473, 1478, 3, 333, 166, 0, 1474, 1478, 5, + 45, 0, 0, 1475, 1478, 3, 353, 176, 0, 1476, 1478, + 7, 9, 0, 0, 1477, 1473, 1, 0, 0, 0, 1477, + 1474, 1, 0, 0, 0, 1477, 1475, 1, 0, 0, 0, + 1477, 1476, 1, 0, 0, 0, 1478, 338, 1, 0, 0, + 0, 1479, 1488, 3, 351, 175, 0, 1480, 1483, 3, 337, + 168, 0, 1481, 1483, 5, 46, 0, 0, 1482, 1480, 1, + 0, 0, 0, 1482, 1481, 1, 0, 0, 0, 1483, 1486, + 1, 0, 0, 0, 1484, 1482, 1, 0, 0, 0, 1484, + 1485, 1, 0, 0, 0, 1485, 1487, 1, 0, 0, 0, + 1486, 1484, 1, 0, 0, 0, 1487, 1489, 3, 337, 168, + 0, 1488, 1484, 1, 0, 0, 0, 1488, 1489, 1, 0, + 0, 0, 1489, 340, 1, 0, 0, 0, 1490, 1495, 3, + 333, 166, 0, 1491, 1495, 5, 58, 0, 0, 1492, 1495, + 3, 353, 176, 0, 1493, 1495, 3, 343, 171, 0, 1494, + 1490, 1, 0, 0, 0, 1494, 1491, 1, 0, 0, 0, + 1494, 1492, 1, 0, 0, 0, 1494, 1493, 1, 0, 0, + 0, 1495, 1509, 1, 0, 0, 0, 1496, 1500, 3, 337, + 168, 0, 1497, 1500, 7, 10, 0, 0, 1498, 1500, 3, + 343, 171, 0, 1499, 1496, 1, 0, 0, 0, 1499, 1497, + 1, 0, 0, 0, 1499, 1498, 1, 0, 0, 0, 1500, + 1503, 1, 0, 0, 0, 1501, 1499, 1, 0, 0, 0, + 1501, 1502, 1, 0, 0, 0, 1502, 1507, 1, 0, 0, + 0, 1503, 1501, 1, 0, 0, 0, 1504, 1508, 3, 337, + 168, 0, 1505, 1508, 5, 58, 0, 0, 1506, 1508, 3, + 343, 171, 0, 1507, 1504, 1, 0, 0, 0, 1507, 1505, + 1, 0, 0, 0, 1507, 1506, 1, 0, 0, 0, 1508, + 1510, 1, 0, 0, 0, 1509, 1501, 1, 0, 0, 0, + 1509, 1510, 1, 0, 0, 0, 1510, 342, 1, 0, 0, + 0, 1511, 1514, 3, 345, 172, 0, 1512, 1514, 3, 349, + 174, 0, 1513, 1511, 1, 0, 0, 0, 1513, 1512, 1, + 0, 0, 0, 1514, 344, 1, 0, 0, 0, 1515, 1516, + 5, 37, 0, 0, 1516, 1517, 3, 347, 173, 0, 1517, + 1518, 3, 347, 173, 0, 1518, 346, 1, 0, 0, 0, + 1519, 1522, 3, 353, 176, 0, 1520, 1522, 7, 11, 0, + 0, 1521, 1519, 1, 0, 0, 0, 1521, 1520, 1, 0, + 0, 0, 1522, 348, 1, 0, 0, 0, 1523, 1524, 5, + 92, 0, 0, 1524, 1525, 7, 12, 0, 0, 1525, 350, + 1, 0, 0, 0, 1526, 1527, 7, 13, 0, 0, 1527, + 352, 1, 0, 0, 0, 1528, 1529, 2, 48, 57, 0, + 1529, 354, 1, 0, 0, 0, 1530, 1532, 7, 14, 0, + 0, 1531, 1530, 1, 0, 0, 0, 1532, 1533, 1, 0, + 0, 0, 1533, 1531, 1, 0, 0, 0, 1533, 1534, 1, + 0, 0, 0, 1534, 1535, 1, 0, 0, 0, 1535, 1536, + 6, 177, 0, 0, 1536, 356, 1, 0, 0, 0, 1537, + 1541, 5, 35, 0, 0, 1538, 1540, 8, 15, 0, 0, + 1539, 1538, 1, 0, 0, 0, 1540, 1543, 1, 0, 0, + 0, 1541, 1539, 1, 0, 0, 0, 1541, 1542, 1, 0, + 0, 0, 1542, 1544, 1, 0, 0, 0, 1543, 1541, 1, + 0, 0, 0, 1544, 1545, 6, 178, 0, 0, 1545, 358, + 1, 0, 0, 0, 1546, 1547, 7, 16, 0, 0, 1547, + 360, 1, 0, 0, 0, 1548, 1549, 7, 17, 0, 0, + 1549, 362, 1, 0, 0, 0, 1550, 1551, 7, 18, 0, + 0, 1551, 364, 1, 0, 0, 0, 1552, 1553, 7, 19, + 0, 0, 1553, 366, 1, 0, 0, 0, 1554, 1555, 7, + 2, 0, 0, 1555, 368, 1, 0, 0, 0, 1556, 1557, + 7, 20, 0, 0, 1557, 370, 1, 0, 0, 0, 1558, + 1559, 7, 21, 0, 0, 1559, 372, 1, 0, 0, 0, + 1560, 1561, 7, 22, 0, 0, 1561, 374, 1, 0, 0, + 0, 1562, 1563, 7, 23, 0, 0, 1563, 376, 1, 0, + 0, 0, 1564, 1565, 7, 24, 0, 0, 1565, 378, 1, + 0, 0, 0, 1566, 1567, 7, 25, 0, 0, 1567, 380, + 1, 0, 0, 0, 1568, 1569, 7, 26, 0, 0, 1569, + 382, 1, 0, 0, 0, 1570, 1571, 7, 27, 0, 0, + 1571, 384, 1, 0, 0, 0, 1572, 1573, 7, 28, 0, + 0, 1573, 386, 1, 0, 0, 0, 1574, 1575, 7, 29, + 0, 0, 1575, 388, 1, 0, 0, 0, 1576, 1577, 7, + 30, 0, 0, 1577, 390, 1, 0, 0, 0, 1578, 1579, + 7, 31, 0, 0, 1579, 392, 1, 0, 0, 0, 1580, + 1581, 7, 32, 0, 0, 1581, 394, 1, 0, 0, 0, + 1582, 1583, 7, 33, 0, 0, 1583, 396, 1, 0, 0, + 0, 1584, 1585, 7, 34, 0, 0, 1585, 398, 1, 0, + 0, 0, 1586, 1587, 7, 35, 0, 0, 1587, 400, 1, + 0, 0, 0, 1588, 1589, 7, 36, 0, 0, 1589, 402, + 1, 0, 0, 0, 1590, 1591, 7, 37, 0, 0, 1591, + 404, 1, 0, 0, 0, 1592, 1593, 7, 38, 0, 0, + 1593, 406, 1, 0, 0, 0, 1594, 1595, 7, 39, 0, + 0, 1595, 408, 1, 0, 0, 0, 1596, 1597, 7, 40, + 0, 0, 1597, 410, 1, 0, 0, 0, 54, 0, 569, + 602, 618, 627, 1240, 1246, 1258, 1262, 1264, 1268, 1280, 1285, + 1287, 1291, 1300, 1305, 1312, 1317, 1323, 1332, 1339, 1343, 1365, + 1370, 1375, 1377, 1385, 1387, 1399, 1403, 1407, 1421, 1425, 1429, + 1443, 1452, 1459, 1463, 1468, 1470, 1477, 1482, 1484, 1488, 1494, + 1499, 1501, 1507, 1509, 1513, 1521, 1533, 1541, 1, 6, 0, 0}; staticData->serializedATN = antlr4::atn::SerializedATNView( serializedATNSegment, diff --git a/src/parser/sparqlParser/generated/SparqlAutomaticLexer.h b/src/parser/sparqlParser/generated/SparqlAutomaticLexer.h index b4e3b1a738..c482fc873a 100644 --- a/src/parser/sparqlParser/generated/SparqlAutomaticLexer.h +++ b/src/parser/sparqlParser/generated/SparqlAutomaticLexer.h @@ -146,43 +146,44 @@ class SparqlAutomaticLexer : public antlr4::Lexer { MIN = 136, MAX = 137, AVG = 138, - SAMPLE = 139, - SEPARATOR = 140, - IRI_REF = 141, - PNAME_NS = 142, - PNAME_LN = 143, - BLANK_NODE_LABEL = 144, - VAR1 = 145, - VAR2 = 146, - LANGTAG = 147, - PREFIX_LANGTAG = 148, - INTEGER = 149, - DECIMAL = 150, - DOUBLE = 151, - INTEGER_POSITIVE = 152, - DECIMAL_POSITIVE = 153, - DOUBLE_POSITIVE = 154, - INTEGER_NEGATIVE = 155, - DECIMAL_NEGATIVE = 156, - DOUBLE_NEGATIVE = 157, - EXPONENT = 158, - STRING_LITERAL1 = 159, - STRING_LITERAL2 = 160, - STRING_LITERAL_LONG1 = 161, - STRING_LITERAL_LONG2 = 162, - ECHAR = 163, - NIL = 164, - ANON = 165, - PN_CHARS_U = 166, - VARNAME = 167, - PN_PREFIX = 168, - PN_LOCAL = 169, - PLX = 170, - PERCENT = 171, - HEX = 172, - PN_LOCAL_ESC = 173, - WS = 174, - COMMENTS = 175 + STDEV = 139, + SAMPLE = 140, + SEPARATOR = 141, + IRI_REF = 142, + PNAME_NS = 143, + PNAME_LN = 144, + BLANK_NODE_LABEL = 145, + VAR1 = 146, + VAR2 = 147, + LANGTAG = 148, + PREFIX_LANGTAG = 149, + INTEGER = 150, + DECIMAL = 151, + DOUBLE = 152, + INTEGER_POSITIVE = 153, + DECIMAL_POSITIVE = 154, + DOUBLE_POSITIVE = 155, + INTEGER_NEGATIVE = 156, + DECIMAL_NEGATIVE = 157, + DOUBLE_NEGATIVE = 158, + EXPONENT = 159, + STRING_LITERAL1 = 160, + STRING_LITERAL2 = 161, + STRING_LITERAL_LONG1 = 162, + STRING_LITERAL_LONG2 = 163, + ECHAR = 164, + NIL = 165, + ANON = 166, + PN_CHARS_U = 167, + VARNAME = 168, + PN_PREFIX = 169, + PN_LOCAL = 170, + PLX = 171, + PERCENT = 172, + HEX = 173, + PN_LOCAL_ESC = 174, + WS = 175, + COMMENTS = 176 }; explicit SparqlAutomaticLexer(antlr4::CharStream* input); diff --git a/src/parser/sparqlParser/generated/SparqlAutomaticLexer.interp b/src/parser/sparqlParser/generated/SparqlAutomaticLexer.interp index 24332ac764..c278c76942 100644 --- a/src/parser/sparqlParser/generated/SparqlAutomaticLexer.interp +++ b/src/parser/sparqlParser/generated/SparqlAutomaticLexer.interp @@ -175,6 +175,7 @@ null null null null +null token symbolic names: null @@ -316,6 +317,7 @@ SUM MIN MAX AVG +STDEV SAMPLE SEPARATOR IRI_REF @@ -493,6 +495,7 @@ SUM MIN MAX AVG +STDEV SAMPLE SEPARATOR IRI_REF @@ -568,4 +571,4 @@ mode names: DEFAULT_MODE atn: -[4, 0, 175, 1590, 6, -1, 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, 2, 157, 7, 157, 2, 158, 7, 158, 2, 159, 7, 159, 2, 160, 7, 160, 2, 161, 7, 161, 2, 162, 7, 162, 2, 163, 7, 163, 2, 164, 7, 164, 2, 165, 7, 165, 2, 166, 7, 166, 2, 167, 7, 167, 2, 168, 7, 168, 2, 169, 7, 169, 2, 170, 7, 170, 2, 171, 7, 171, 2, 172, 7, 172, 2, 173, 7, 173, 2, 174, 7, 174, 2, 175, 7, 175, 2, 176, 7, 176, 2, 177, 7, 177, 2, 178, 7, 178, 2, 179, 7, 179, 2, 180, 7, 180, 2, 181, 7, 181, 2, 182, 7, 182, 2, 183, 7, 183, 2, 184, 7, 184, 2, 185, 7, 185, 2, 186, 7, 186, 2, 187, 7, 187, 2, 188, 7, 188, 2, 189, 7, 189, 2, 190, 7, 190, 2, 191, 7, 191, 2, 192, 7, 192, 2, 193, 7, 193, 2, 194, 7, 194, 2, 195, 7, 195, 2, 196, 7, 196, 2, 197, 7, 197, 2, 198, 7, 198, 2, 199, 7, 199, 2, 200, 7, 200, 2, 201, 7, 201, 2, 202, 7, 202, 2, 203, 7, 203, 1, 0, 1, 0, 1, 1, 1, 1, 1, 2, 1, 2, 1, 3, 1, 3, 1, 4, 1, 4, 1, 5, 1, 5, 1, 6, 1, 6, 1, 7, 1, 7, 1, 8, 1, 8, 1, 9, 1, 9, 1, 10, 1, 10, 1, 11, 1, 11, 1, 12, 1, 12, 1, 13, 1, 13, 1, 14, 1, 14, 1, 15, 1, 15, 1, 16, 1, 16, 1, 17, 1, 17, 1, 17, 1, 18, 1, 18, 1, 18, 1, 19, 1, 19, 1, 20, 1, 20, 1, 20, 1, 21, 1, 21, 1, 22, 1, 22, 1, 23, 1, 23, 1, 23, 1, 24, 1, 24, 1, 24, 1, 25, 1, 25, 1, 26, 1, 26, 1, 26, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 1, 29, 1, 29, 1, 29, 1, 29, 1, 29, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 34, 1, 34, 1, 34, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 1, 36, 1, 36, 1, 36, 1, 36, 1, 36, 1, 36, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 38, 1, 38, 1, 38, 1, 38, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 1, 41, 1, 41, 1, 41, 1, 41, 1, 41, 1, 41, 4, 41, 566, 8, 41, 11, 41, 12, 41, 567, 1, 41, 1, 41, 1, 41, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 4, 44, 599, 8, 44, 11, 44, 12, 44, 600, 1, 44, 1, 44, 1, 44, 1, 45, 1, 45, 1, 45, 1, 45, 1, 45, 1, 45, 1, 45, 1, 45, 1, 45, 4, 45, 615, 8, 45, 11, 45, 12, 45, 616, 1, 45, 1, 45, 1, 45, 1, 45, 1, 45, 4, 45, 624, 8, 45, 11, 45, 12, 45, 625, 1, 45, 1, 45, 1, 45, 1, 46, 1, 46, 1, 46, 1, 46, 1, 47, 1, 47, 1, 47, 1, 47, 1, 47, 1, 48, 1, 48, 1, 48, 1, 48, 1, 48, 1, 48, 1, 49, 1, 49, 1, 49, 1, 49, 1, 49, 1, 49, 1, 49, 1, 50, 1, 50, 1, 50, 1, 50, 1, 50, 1, 50, 1, 50, 1, 50, 1, 50, 1, 50, 1, 51, 1, 51, 1, 51, 1, 51, 1, 51, 1, 51, 1, 51, 1, 52, 1, 52, 1, 52, 1, 52, 1, 52, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 56, 1, 56, 1, 56, 1, 56, 1, 56, 1, 57, 1, 57, 1, 57, 1, 57, 1, 57, 1, 57, 1, 57, 1, 58, 1, 58, 1, 58, 1, 58, 1, 59, 1, 59, 1, 59, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 61, 1, 61, 1, 61, 1, 61, 1, 61, 1, 62, 1, 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, 64, 1, 64, 1, 64, 1, 64, 1, 65, 1, 65, 1, 65, 1, 65, 1, 65, 1, 66, 1, 66, 1, 66, 1, 66, 1, 66, 1, 66, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 68, 1, 68, 1, 68, 1, 68, 1, 68, 1, 68, 1, 69, 1, 69, 1, 69, 1, 69, 1, 70, 1, 70, 1, 70, 1, 70, 1, 70, 1, 70, 1, 70, 1, 70, 1, 70, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 74, 1, 74, 1, 74, 1, 74, 1, 74, 1, 74, 1, 75, 1, 75, 1, 75, 1, 75, 1, 75, 1, 75, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 77, 1, 77, 1, 77, 1, 77, 1, 78, 1, 78, 1, 78, 1, 79, 1, 79, 1, 79, 1, 79, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 81, 1, 81, 1, 81, 1, 81, 1, 81, 1, 81, 1, 81, 1, 81, 1, 81, 1, 81, 1, 81, 1, 81, 1, 82, 1, 82, 1, 82, 1, 82, 1, 82, 1, 82, 1, 82, 1, 82, 1, 82, 1, 83, 1, 83, 1, 83, 1, 83, 1, 83, 1, 83, 1, 84, 1, 84, 1, 84, 1, 84, 1, 85, 1, 85, 1, 85, 1, 85, 1, 86, 1, 86, 1, 86, 1, 86, 1, 86, 1, 86, 1, 87, 1, 87, 1, 87, 1, 87, 1, 87, 1, 88, 1, 88, 1, 88, 1, 88, 1, 89, 1, 89, 1, 89, 1, 89, 1, 89, 1, 90, 1, 90, 1, 90, 1, 90, 1, 90, 1, 90, 1, 91, 1, 91, 1, 91, 1, 91, 1, 91, 1, 91, 1, 92, 1, 92, 1, 92, 1, 92, 1, 92, 1, 92, 1, 92, 1, 93, 1, 93, 1, 93, 1, 93, 1, 93, 1, 93, 1, 93, 1, 94, 1, 94, 1, 94, 1, 94, 1, 94, 1, 94, 1, 95, 1, 95, 1, 95, 1, 95, 1, 95, 1, 95, 1, 96, 1, 96, 1, 96, 1, 96, 1, 96, 1, 96, 1, 96, 1, 96, 1, 96, 1, 96, 1, 96, 1, 96, 1, 96, 1, 96, 1, 96, 1, 97, 1, 97, 1, 97, 1, 97, 1, 98, 1, 98, 1, 98, 1, 98, 1, 98, 1, 98, 1, 98, 1, 98, 1, 98, 1, 99, 1, 99, 1, 99, 1, 99, 1, 99, 1, 99, 1, 99, 1, 99, 1, 99, 1, 99, 1, 100, 1, 100, 1, 100, 1, 100, 1, 100, 1, 100, 1, 100, 1, 100, 1, 101, 1, 101, 1, 101, 1, 101, 1, 101, 1, 101, 1, 101, 1, 101, 1, 101, 1, 101, 1, 102, 1, 102, 1, 102, 1, 102, 1, 102, 1, 102, 1, 102, 1, 102, 1, 102, 1, 103, 1, 103, 1, 103, 1, 103, 1, 103, 1, 104, 1, 104, 1, 104, 1, 104, 1, 104, 1, 104, 1, 105, 1, 105, 1, 105, 1, 105, 1, 106, 1, 106, 1, 106, 1, 106, 1, 106, 1, 106, 1, 107, 1, 107, 1, 107, 1, 107, 1, 107, 1, 107, 1, 107, 1, 107, 1, 108, 1, 108, 1, 108, 1, 108, 1, 108, 1, 108, 1, 108, 1, 108, 1, 109, 1, 109, 1, 109, 1, 109, 1, 109, 1, 109, 1, 109, 1, 109, 1, 109, 1, 110, 1, 110, 1, 110, 1, 111, 1, 111, 1, 111, 1, 111, 1, 112, 1, 112, 1, 112, 1, 112, 1, 112, 1, 113, 1, 113, 1, 113, 1, 113, 1, 113, 1, 113, 1, 113, 1, 113, 1, 114, 1, 114, 1, 114, 1, 114, 1, 114, 1, 115, 1, 115, 1, 115, 1, 115, 1, 115, 1, 115, 1, 115, 1, 116, 1, 116, 1, 116, 1, 116, 1, 116, 1, 116, 1, 116, 1, 117, 1, 117, 1, 117, 1, 117, 1, 117, 1, 117, 1, 117, 1, 118, 1, 118, 1, 118, 1, 118, 1, 119, 1, 119, 1, 119, 1, 119, 1, 119, 1, 119, 1, 119, 1, 119, 1, 119, 1, 120, 1, 120, 1, 120, 1, 121, 1, 121, 1, 121, 1, 121, 1, 121, 1, 121, 1, 121, 1, 121, 1, 122, 1, 122, 1, 122, 1, 122, 1, 122, 1, 122, 1, 123, 1, 123, 1, 123, 1, 123, 1, 123, 1, 123, 1, 123, 1, 123, 1, 123, 1, 124, 1, 124, 1, 124, 1, 124, 1, 124, 1, 124, 1, 125, 1, 125, 1, 125, 1, 125, 1, 125, 1, 125, 1, 126, 1, 126, 1, 126, 1, 126, 1, 126, 1, 126, 1, 126, 1, 126, 1, 127, 1, 127, 1, 127, 1, 127, 1, 127, 1, 127, 1, 127, 1, 127, 1, 127, 1, 127, 1, 128, 1, 128, 1, 128, 1, 128, 1, 128, 1, 128, 1, 128, 1, 128, 1, 128, 1, 128, 1, 129, 1, 129, 1, 129, 1, 129, 1, 129, 1, 129, 1, 130, 1, 130, 1, 130, 1, 130, 1, 130, 1, 130, 1, 130, 1, 131, 1, 131, 1, 131, 1, 131, 1, 131, 1, 131, 1, 131, 1, 131, 1, 132, 1, 132, 1, 132, 1, 132, 1, 132, 1, 132, 1, 132, 1, 133, 1, 133, 1, 133, 1, 133, 1, 133, 1, 133, 1, 134, 1, 134, 1, 134, 1, 134, 1, 135, 1, 135, 1, 135, 1, 135, 1, 136, 1, 136, 1, 136, 1, 136, 1, 137, 1, 137, 1, 137, 1, 137, 1, 138, 1, 138, 1, 138, 1, 138, 1, 138, 1, 138, 1, 138, 1, 139, 1, 139, 1, 139, 1, 139, 1, 139, 1, 139, 1, 139, 1, 139, 1, 139, 1, 139, 1, 140, 1, 140, 5, 140, 1231, 8, 140, 10, 140, 12, 140, 1234, 9, 140, 1, 140, 1, 140, 1, 141, 3, 141, 1239, 8, 141, 1, 141, 1, 141, 1, 142, 1, 142, 1, 142, 1, 143, 1, 143, 1, 143, 1, 143, 1, 143, 3, 143, 1251, 8, 143, 1, 143, 1, 143, 5, 143, 1255, 8, 143, 10, 143, 12, 143, 1258, 9, 143, 1, 143, 3, 143, 1261, 8, 143, 1, 144, 1, 144, 1, 144, 1, 145, 1, 145, 1, 145, 1, 146, 1, 146, 4, 146, 1271, 8, 146, 11, 146, 12, 146, 1272, 1, 146, 1, 146, 1, 146, 4, 146, 1278, 8, 146, 11, 146, 12, 146, 1279, 5, 146, 1282, 8, 146, 10, 146, 12, 146, 1285, 9, 146, 1, 147, 1, 147, 1, 147, 1, 148, 4, 148, 1291, 8, 148, 11, 148, 12, 148, 1292, 1, 149, 5, 149, 1296, 8, 149, 10, 149, 12, 149, 1299, 9, 149, 1, 149, 1, 149, 4, 149, 1303, 8, 149, 11, 149, 12, 149, 1304, 1, 150, 4, 150, 1308, 8, 150, 11, 150, 12, 150, 1309, 1, 150, 1, 150, 5, 150, 1314, 8, 150, 10, 150, 12, 150, 1317, 9, 150, 1, 150, 1, 150, 1, 150, 1, 150, 4, 150, 1323, 8, 150, 11, 150, 12, 150, 1324, 1, 150, 1, 150, 1, 150, 4, 150, 1330, 8, 150, 11, 150, 12, 150, 1331, 1, 150, 1, 150, 3, 150, 1336, 8, 150, 1, 151, 1, 151, 1, 151, 1, 152, 1, 152, 1, 152, 1, 153, 1, 153, 1, 153, 1, 154, 1, 154, 1, 154, 1, 155, 1, 155, 1, 155, 1, 156, 1, 156, 1, 156, 1, 157, 1, 157, 3, 157, 1358, 8, 157, 1, 157, 4, 157, 1361, 8, 157, 11, 157, 12, 157, 1362, 1, 158, 1, 158, 1, 158, 5, 158, 1368, 8, 158, 10, 158, 12, 158, 1371, 9, 158, 1, 158, 1, 158, 1, 159, 1, 159, 1, 159, 5, 159, 1378, 8, 159, 10, 159, 12, 159, 1381, 9, 159, 1, 159, 1, 159, 1, 160, 1, 160, 1, 160, 1, 160, 1, 160, 1, 160, 1, 160, 3, 160, 1392, 8, 160, 1, 160, 1, 160, 3, 160, 1396, 8, 160, 5, 160, 1398, 8, 160, 10, 160, 12, 160, 1401, 9, 160, 1, 160, 1, 160, 1, 160, 1, 160, 1, 161, 1, 161, 1, 161, 1, 161, 1, 161, 1, 161, 1, 161, 3, 161, 1414, 8, 161, 1, 161, 1, 161, 3, 161, 1418, 8, 161, 5, 161, 1420, 8, 161, 10, 161, 12, 161, 1423, 9, 161, 1, 161, 1, 161, 1, 161, 1, 161, 1, 162, 1, 162, 1, 162, 1, 163, 1, 163, 5, 163, 1434, 8, 163, 10, 163, 12, 163, 1437, 9, 163, 1, 163, 1, 163, 1, 164, 1, 164, 5, 164, 1443, 8, 164, 10, 164, 12, 164, 1446, 9, 164, 1, 164, 1, 164, 1, 165, 1, 165, 3, 165, 1452, 8, 165, 1, 166, 1, 166, 3, 166, 1456, 8, 166, 1, 166, 1, 166, 1, 166, 5, 166, 1461, 8, 166, 10, 166, 12, 166, 1464, 9, 166, 1, 167, 1, 167, 1, 167, 1, 167, 3, 167, 1470, 8, 167, 1, 168, 1, 168, 1, 168, 5, 168, 1475, 8, 168, 10, 168, 12, 168, 1478, 9, 168, 1, 168, 3, 168, 1481, 8, 168, 1, 169, 1, 169, 1, 169, 1, 169, 3, 169, 1487, 8, 169, 1, 169, 1, 169, 1, 169, 5, 169, 1492, 8, 169, 10, 169, 12, 169, 1495, 9, 169, 1, 169, 1, 169, 1, 169, 3, 169, 1500, 8, 169, 3, 169, 1502, 8, 169, 1, 170, 1, 170, 3, 170, 1506, 8, 170, 1, 171, 1, 171, 1, 171, 1, 171, 1, 172, 1, 172, 3, 172, 1514, 8, 172, 1, 173, 1, 173, 1, 173, 1, 174, 1, 174, 1, 175, 1, 175, 1, 176, 4, 176, 1524, 8, 176, 11, 176, 12, 176, 1525, 1, 176, 1, 176, 1, 177, 1, 177, 5, 177, 1532, 8, 177, 10, 177, 12, 177, 1535, 9, 177, 1, 177, 1, 177, 1, 178, 1, 178, 1, 179, 1, 179, 1, 180, 1, 180, 1, 181, 1, 181, 1, 182, 1, 182, 1, 183, 1, 183, 1, 184, 1, 184, 1, 185, 1, 185, 1, 186, 1, 186, 1, 187, 1, 187, 1, 188, 1, 188, 1, 189, 1, 189, 1, 190, 1, 190, 1, 191, 1, 191, 1, 192, 1, 192, 1, 193, 1, 193, 1, 194, 1, 194, 1, 195, 1, 195, 1, 196, 1, 196, 1, 197, 1, 197, 1, 198, 1, 198, 1, 199, 1, 199, 1, 200, 1, 200, 1, 201, 1, 201, 1, 202, 1, 202, 1, 203, 1, 203, 0, 0, 204, 1, 1, 3, 2, 5, 3, 7, 4, 9, 5, 11, 6, 13, 7, 15, 8, 17, 9, 19, 10, 21, 11, 23, 12, 25, 13, 27, 14, 29, 15, 31, 16, 33, 17, 35, 18, 37, 19, 39, 20, 41, 21, 43, 22, 45, 23, 47, 24, 49, 25, 51, 26, 53, 27, 55, 28, 57, 29, 59, 30, 61, 31, 63, 32, 65, 33, 67, 34, 69, 35, 71, 36, 73, 37, 75, 38, 77, 39, 79, 40, 81, 41, 83, 42, 85, 43, 87, 44, 89, 45, 91, 46, 93, 47, 95, 48, 97, 49, 99, 50, 101, 51, 103, 52, 105, 53, 107, 54, 109, 55, 111, 56, 113, 57, 115, 58, 117, 59, 119, 60, 121, 61, 123, 62, 125, 63, 127, 64, 129, 65, 131, 66, 133, 67, 135, 68, 137, 69, 139, 70, 141, 71, 143, 72, 145, 73, 147, 74, 149, 75, 151, 76, 153, 77, 155, 78, 157, 79, 159, 80, 161, 81, 163, 82, 165, 83, 167, 84, 169, 85, 171, 86, 173, 87, 175, 88, 177, 89, 179, 90, 181, 91, 183, 92, 185, 93, 187, 94, 189, 95, 191, 96, 193, 97, 195, 98, 197, 99, 199, 100, 201, 101, 203, 102, 205, 103, 207, 104, 209, 105, 211, 106, 213, 107, 215, 108, 217, 109, 219, 110, 221, 111, 223, 112, 225, 113, 227, 114, 229, 115, 231, 116, 233, 117, 235, 118, 237, 119, 239, 120, 241, 121, 243, 122, 245, 123, 247, 124, 249, 125, 251, 126, 253, 127, 255, 128, 257, 129, 259, 130, 261, 131, 263, 132, 265, 133, 267, 134, 269, 135, 271, 136, 273, 137, 275, 138, 277, 139, 279, 140, 281, 141, 283, 142, 285, 143, 287, 144, 289, 145, 291, 146, 293, 147, 295, 148, 297, 149, 299, 150, 301, 151, 303, 152, 305, 153, 307, 154, 309, 155, 311, 156, 313, 157, 315, 158, 317, 159, 319, 160, 321, 161, 323, 162, 325, 163, 327, 164, 329, 165, 331, 166, 333, 167, 335, 0, 337, 168, 339, 169, 341, 170, 343, 171, 345, 172, 347, 173, 349, 0, 351, 0, 353, 174, 355, 175, 357, 0, 359, 0, 361, 0, 363, 0, 365, 0, 367, 0, 369, 0, 371, 0, 373, 0, 375, 0, 377, 0, 379, 0, 381, 0, 383, 0, 385, 0, 387, 0, 389, 0, 391, 0, 393, 0, 395, 0, 397, 0, 399, 0, 401, 0, 403, 0, 405, 0, 407, 0, 1, 0, 41, 8, 0, 0, 32, 34, 34, 60, 60, 62, 62, 92, 92, 94, 94, 96, 96, 123, 125, 2, 0, 65, 90, 97, 122, 2, 0, 69, 69, 101, 101, 2, 0, 43, 43, 45, 45, 4, 0, 10, 10, 13, 13, 39, 39, 92, 92, 4, 0, 10, 10, 13, 13, 34, 34, 92, 92, 2, 0, 39, 39, 92, 92, 2, 0, 34, 34, 92, 92, 8, 0, 34, 34, 39, 39, 92, 92, 98, 98, 102, 102, 110, 110, 114, 114, 116, 116, 3, 0, 183, 183, 768, 879, 8255, 8256, 2, 0, 46, 46, 58, 58, 2, 0, 65, 70, 97, 102, 7, 0, 33, 33, 35, 47, 59, 59, 61, 61, 63, 64, 95, 95, 126, 126, 13, 0, 65, 90, 97, 122, 192, 214, 216, 246, 248, 767, 880, 893, 895, 8191, 8204, 8205, 8304, 8591, 11264, 12271, 12289, 55295, 63744, 64975, 65008, 65533, 3, 0, 9, 10, 13, 13, 32, 32, 2, 0, 10, 10, 13, 13, 2, 0, 65, 65, 97, 97, 2, 0, 66, 66, 98, 98, 2, 0, 67, 67, 99, 99, 2, 0, 68, 68, 100, 100, 2, 0, 70, 70, 102, 102, 2, 0, 71, 71, 103, 103, 2, 0, 72, 72, 104, 104, 2, 0, 73, 73, 105, 105, 2, 0, 74, 74, 106, 106, 2, 0, 75, 75, 107, 107, 2, 0, 76, 76, 108, 108, 2, 0, 77, 77, 109, 109, 2, 0, 78, 78, 110, 110, 2, 0, 79, 79, 111, 111, 2, 0, 80, 80, 112, 112, 2, 0, 81, 81, 113, 113, 2, 0, 82, 82, 114, 114, 2, 0, 83, 83, 115, 115, 2, 0, 84, 84, 116, 116, 2, 0, 85, 85, 117, 117, 2, 0, 86, 86, 118, 118, 2, 0, 87, 87, 119, 119, 2, 0, 88, 88, 120, 120, 2, 0, 89, 89, 121, 121, 2, 0, 90, 90, 122, 122, 1623, 0, 1, 1, 0, 0, 0, 0, 3, 1, 0, 0, 0, 0, 5, 1, 0, 0, 0, 0, 7, 1, 0, 0, 0, 0, 9, 1, 0, 0, 0, 0, 11, 1, 0, 0, 0, 0, 13, 1, 0, 0, 0, 0, 15, 1, 0, 0, 0, 0, 17, 1, 0, 0, 0, 0, 19, 1, 0, 0, 0, 0, 21, 1, 0, 0, 0, 0, 23, 1, 0, 0, 0, 0, 25, 1, 0, 0, 0, 0, 27, 1, 0, 0, 0, 0, 29, 1, 0, 0, 0, 0, 31, 1, 0, 0, 0, 0, 33, 1, 0, 0, 0, 0, 35, 1, 0, 0, 0, 0, 37, 1, 0, 0, 0, 0, 39, 1, 0, 0, 0, 0, 41, 1, 0, 0, 0, 0, 43, 1, 0, 0, 0, 0, 45, 1, 0, 0, 0, 0, 47, 1, 0, 0, 0, 0, 49, 1, 0, 0, 0, 0, 51, 1, 0, 0, 0, 0, 53, 1, 0, 0, 0, 0, 55, 1, 0, 0, 0, 0, 57, 1, 0, 0, 0, 0, 59, 1, 0, 0, 0, 0, 61, 1, 0, 0, 0, 0, 63, 1, 0, 0, 0, 0, 65, 1, 0, 0, 0, 0, 67, 1, 0, 0, 0, 0, 69, 1, 0, 0, 0, 0, 71, 1, 0, 0, 0, 0, 73, 1, 0, 0, 0, 0, 75, 1, 0, 0, 0, 0, 77, 1, 0, 0, 0, 0, 79, 1, 0, 0, 0, 0, 81, 1, 0, 0, 0, 0, 83, 1, 0, 0, 0, 0, 85, 1, 0, 0, 0, 0, 87, 1, 0, 0, 0, 0, 89, 1, 0, 0, 0, 0, 91, 1, 0, 0, 0, 0, 93, 1, 0, 0, 0, 0, 95, 1, 0, 0, 0, 0, 97, 1, 0, 0, 0, 0, 99, 1, 0, 0, 0, 0, 101, 1, 0, 0, 0, 0, 103, 1, 0, 0, 0, 0, 105, 1, 0, 0, 0, 0, 107, 1, 0, 0, 0, 0, 109, 1, 0, 0, 0, 0, 111, 1, 0, 0, 0, 0, 113, 1, 0, 0, 0, 0, 115, 1, 0, 0, 0, 0, 117, 1, 0, 0, 0, 0, 119, 1, 0, 0, 0, 0, 121, 1, 0, 0, 0, 0, 123, 1, 0, 0, 0, 0, 125, 1, 0, 0, 0, 0, 127, 1, 0, 0, 0, 0, 129, 1, 0, 0, 0, 0, 131, 1, 0, 0, 0, 0, 133, 1, 0, 0, 0, 0, 135, 1, 0, 0, 0, 0, 137, 1, 0, 0, 0, 0, 139, 1, 0, 0, 0, 0, 141, 1, 0, 0, 0, 0, 143, 1, 0, 0, 0, 0, 145, 1, 0, 0, 0, 0, 147, 1, 0, 0, 0, 0, 149, 1, 0, 0, 0, 0, 151, 1, 0, 0, 0, 0, 153, 1, 0, 0, 0, 0, 155, 1, 0, 0, 0, 0, 157, 1, 0, 0, 0, 0, 159, 1, 0, 0, 0, 0, 161, 1, 0, 0, 0, 0, 163, 1, 0, 0, 0, 0, 165, 1, 0, 0, 0, 0, 167, 1, 0, 0, 0, 0, 169, 1, 0, 0, 0, 0, 171, 1, 0, 0, 0, 0, 173, 1, 0, 0, 0, 0, 175, 1, 0, 0, 0, 0, 177, 1, 0, 0, 0, 0, 179, 1, 0, 0, 0, 0, 181, 1, 0, 0, 0, 0, 183, 1, 0, 0, 0, 0, 185, 1, 0, 0, 0, 0, 187, 1, 0, 0, 0, 0, 189, 1, 0, 0, 0, 0, 191, 1, 0, 0, 0, 0, 193, 1, 0, 0, 0, 0, 195, 1, 0, 0, 0, 0, 197, 1, 0, 0, 0, 0, 199, 1, 0, 0, 0, 0, 201, 1, 0, 0, 0, 0, 203, 1, 0, 0, 0, 0, 205, 1, 0, 0, 0, 0, 207, 1, 0, 0, 0, 0, 209, 1, 0, 0, 0, 0, 211, 1, 0, 0, 0, 0, 213, 1, 0, 0, 0, 0, 215, 1, 0, 0, 0, 0, 217, 1, 0, 0, 0, 0, 219, 1, 0, 0, 0, 0, 221, 1, 0, 0, 0, 0, 223, 1, 0, 0, 0, 0, 225, 1, 0, 0, 0, 0, 227, 1, 0, 0, 0, 0, 229, 1, 0, 0, 0, 0, 231, 1, 0, 0, 0, 0, 233, 1, 0, 0, 0, 0, 235, 1, 0, 0, 0, 0, 237, 1, 0, 0, 0, 0, 239, 1, 0, 0, 0, 0, 241, 1, 0, 0, 0, 0, 243, 1, 0, 0, 0, 0, 245, 1, 0, 0, 0, 0, 247, 1, 0, 0, 0, 0, 249, 1, 0, 0, 0, 0, 251, 1, 0, 0, 0, 0, 253, 1, 0, 0, 0, 0, 255, 1, 0, 0, 0, 0, 257, 1, 0, 0, 0, 0, 259, 1, 0, 0, 0, 0, 261, 1, 0, 0, 0, 0, 263, 1, 0, 0, 0, 0, 265, 1, 0, 0, 0, 0, 267, 1, 0, 0, 0, 0, 269, 1, 0, 0, 0, 0, 271, 1, 0, 0, 0, 0, 273, 1, 0, 0, 0, 0, 275, 1, 0, 0, 0, 0, 277, 1, 0, 0, 0, 0, 279, 1, 0, 0, 0, 0, 281, 1, 0, 0, 0, 0, 283, 1, 0, 0, 0, 0, 285, 1, 0, 0, 0, 0, 287, 1, 0, 0, 0, 0, 289, 1, 0, 0, 0, 0, 291, 1, 0, 0, 0, 0, 293, 1, 0, 0, 0, 0, 295, 1, 0, 0, 0, 0, 297, 1, 0, 0, 0, 0, 299, 1, 0, 0, 0, 0, 301, 1, 0, 0, 0, 0, 303, 1, 0, 0, 0, 0, 305, 1, 0, 0, 0, 0, 307, 1, 0, 0, 0, 0, 309, 1, 0, 0, 0, 0, 311, 1, 0, 0, 0, 0, 313, 1, 0, 0, 0, 0, 315, 1, 0, 0, 0, 0, 317, 1, 0, 0, 0, 0, 319, 1, 0, 0, 0, 0, 321, 1, 0, 0, 0, 0, 323, 1, 0, 0, 0, 0, 325, 1, 0, 0, 0, 0, 327, 1, 0, 0, 0, 0, 329, 1, 0, 0, 0, 0, 331, 1, 0, 0, 0, 0, 333, 1, 0, 0, 0, 0, 337, 1, 0, 0, 0, 0, 339, 1, 0, 0, 0, 0, 341, 1, 0, 0, 0, 0, 343, 1, 0, 0, 0, 0, 345, 1, 0, 0, 0, 0, 347, 1, 0, 0, 0, 0, 353, 1, 0, 0, 0, 0, 355, 1, 0, 0, 0, 1, 409, 1, 0, 0, 0, 3, 411, 1, 0, 0, 0, 5, 413, 1, 0, 0, 0, 7, 415, 1, 0, 0, 0, 9, 417, 1, 0, 0, 0, 11, 419, 1, 0, 0, 0, 13, 421, 1, 0, 0, 0, 15, 423, 1, 0, 0, 0, 17, 425, 1, 0, 0, 0, 19, 427, 1, 0, 0, 0, 21, 429, 1, 0, 0, 0, 23, 431, 1, 0, 0, 0, 25, 433, 1, 0, 0, 0, 27, 435, 1, 0, 0, 0, 29, 437, 1, 0, 0, 0, 31, 439, 1, 0, 0, 0, 33, 441, 1, 0, 0, 0, 35, 443, 1, 0, 0, 0, 37, 446, 1, 0, 0, 0, 39, 449, 1, 0, 0, 0, 41, 451, 1, 0, 0, 0, 43, 454, 1, 0, 0, 0, 45, 456, 1, 0, 0, 0, 47, 458, 1, 0, 0, 0, 49, 461, 1, 0, 0, 0, 51, 464, 1, 0, 0, 0, 53, 466, 1, 0, 0, 0, 55, 469, 1, 0, 0, 0, 57, 474, 1, 0, 0, 0, 59, 480, 1, 0, 0, 0, 61, 485, 1, 0, 0, 0, 63, 492, 1, 0, 0, 0, 65, 499, 1, 0, 0, 0, 67, 508, 1, 0, 0, 0, 69, 516, 1, 0, 0, 0, 71, 519, 1, 0, 0, 0, 73, 529, 1, 0, 0, 0, 75, 535, 1, 0, 0, 0, 77, 544, 1, 0, 0, 0, 79, 548, 1, 0, 0, 0, 81, 553, 1, 0, 0, 0, 83, 559, 1, 0, 0, 0, 85, 572, 1, 0, 0, 0, 87, 585, 1, 0, 0, 0, 89, 592, 1, 0, 0, 0, 91, 605, 1, 0, 0, 0, 93, 630, 1, 0, 0, 0, 95, 634, 1, 0, 0, 0, 97, 639, 1, 0, 0, 0, 99, 645, 1, 0, 0, 0, 101, 652, 1, 0, 0, 0, 103, 662, 1, 0, 0, 0, 105, 669, 1, 0, 0, 0, 107, 674, 1, 0, 0, 0, 109, 681, 1, 0, 0, 0, 111, 686, 1, 0, 0, 0, 113, 692, 1, 0, 0, 0, 115, 697, 1, 0, 0, 0, 117, 704, 1, 0, 0, 0, 119, 708, 1, 0, 0, 0, 121, 711, 1, 0, 0, 0, 123, 716, 1, 0, 0, 0, 125, 721, 1, 0, 0, 0, 127, 726, 1, 0, 0, 0, 129, 733, 1, 0, 0, 0, 131, 740, 1, 0, 0, 0, 133, 745, 1, 0, 0, 0, 135, 751, 1, 0, 0, 0, 137, 759, 1, 0, 0, 0, 139, 765, 1, 0, 0, 0, 141, 769, 1, 0, 0, 0, 143, 778, 1, 0, 0, 0, 145, 786, 1, 0, 0, 0, 147, 791, 1, 0, 0, 0, 149, 797, 1, 0, 0, 0, 151, 803, 1, 0, 0, 0, 153, 809, 1, 0, 0, 0, 155, 816, 1, 0, 0, 0, 157, 820, 1, 0, 0, 0, 159, 823, 1, 0, 0, 0, 161, 827, 1, 0, 0, 0, 163, 832, 1, 0, 0, 0, 165, 844, 1, 0, 0, 0, 167, 853, 1, 0, 0, 0, 169, 859, 1, 0, 0, 0, 171, 863, 1, 0, 0, 0, 173, 867, 1, 0, 0, 0, 175, 873, 1, 0, 0, 0, 177, 878, 1, 0, 0, 0, 179, 882, 1, 0, 0, 0, 181, 887, 1, 0, 0, 0, 183, 893, 1, 0, 0, 0, 185, 899, 1, 0, 0, 0, 187, 906, 1, 0, 0, 0, 189, 913, 1, 0, 0, 0, 191, 919, 1, 0, 0, 0, 193, 925, 1, 0, 0, 0, 195, 940, 1, 0, 0, 0, 197, 944, 1, 0, 0, 0, 199, 953, 1, 0, 0, 0, 201, 963, 1, 0, 0, 0, 203, 971, 1, 0, 0, 0, 205, 981, 1, 0, 0, 0, 207, 990, 1, 0, 0, 0, 209, 995, 1, 0, 0, 0, 211, 1001, 1, 0, 0, 0, 213, 1005, 1, 0, 0, 0, 215, 1011, 1, 0, 0, 0, 217, 1019, 1, 0, 0, 0, 219, 1027, 1, 0, 0, 0, 221, 1036, 1, 0, 0, 0, 223, 1039, 1, 0, 0, 0, 225, 1043, 1, 0, 0, 0, 227, 1048, 1, 0, 0, 0, 229, 1056, 1, 0, 0, 0, 231, 1061, 1, 0, 0, 0, 233, 1068, 1, 0, 0, 0, 235, 1075, 1, 0, 0, 0, 237, 1082, 1, 0, 0, 0, 239, 1086, 1, 0, 0, 0, 241, 1095, 1, 0, 0, 0, 243, 1098, 1, 0, 0, 0, 245, 1106, 1, 0, 0, 0, 247, 1112, 1, 0, 0, 0, 249, 1121, 1, 0, 0, 0, 251, 1127, 1, 0, 0, 0, 253, 1133, 1, 0, 0, 0, 255, 1141, 1, 0, 0, 0, 257, 1151, 1, 0, 0, 0, 259, 1161, 1, 0, 0, 0, 261, 1167, 1, 0, 0, 0, 263, 1174, 1, 0, 0, 0, 265, 1182, 1, 0, 0, 0, 267, 1189, 1, 0, 0, 0, 269, 1195, 1, 0, 0, 0, 271, 1199, 1, 0, 0, 0, 273, 1203, 1, 0, 0, 0, 275, 1207, 1, 0, 0, 0, 277, 1211, 1, 0, 0, 0, 279, 1218, 1, 0, 0, 0, 281, 1228, 1, 0, 0, 0, 283, 1238, 1, 0, 0, 0, 285, 1242, 1, 0, 0, 0, 287, 1245, 1, 0, 0, 0, 289, 1262, 1, 0, 0, 0, 291, 1265, 1, 0, 0, 0, 293, 1268, 1, 0, 0, 0, 295, 1286, 1, 0, 0, 0, 297, 1290, 1, 0, 0, 0, 299, 1297, 1, 0, 0, 0, 301, 1335, 1, 0, 0, 0, 303, 1337, 1, 0, 0, 0, 305, 1340, 1, 0, 0, 0, 307, 1343, 1, 0, 0, 0, 309, 1346, 1, 0, 0, 0, 311, 1349, 1, 0, 0, 0, 313, 1352, 1, 0, 0, 0, 315, 1355, 1, 0, 0, 0, 317, 1364, 1, 0, 0, 0, 319, 1374, 1, 0, 0, 0, 321, 1384, 1, 0, 0, 0, 323, 1406, 1, 0, 0, 0, 325, 1428, 1, 0, 0, 0, 327, 1431, 1, 0, 0, 0, 329, 1440, 1, 0, 0, 0, 331, 1451, 1, 0, 0, 0, 333, 1455, 1, 0, 0, 0, 335, 1469, 1, 0, 0, 0, 337, 1471, 1, 0, 0, 0, 339, 1486, 1, 0, 0, 0, 341, 1505, 1, 0, 0, 0, 343, 1507, 1, 0, 0, 0, 345, 1513, 1, 0, 0, 0, 347, 1515, 1, 0, 0, 0, 349, 1518, 1, 0, 0, 0, 351, 1520, 1, 0, 0, 0, 353, 1523, 1, 0, 0, 0, 355, 1529, 1, 0, 0, 0, 357, 1538, 1, 0, 0, 0, 359, 1540, 1, 0, 0, 0, 361, 1542, 1, 0, 0, 0, 363, 1544, 1, 0, 0, 0, 365, 1546, 1, 0, 0, 0, 367, 1548, 1, 0, 0, 0, 369, 1550, 1, 0, 0, 0, 371, 1552, 1, 0, 0, 0, 373, 1554, 1, 0, 0, 0, 375, 1556, 1, 0, 0, 0, 377, 1558, 1, 0, 0, 0, 379, 1560, 1, 0, 0, 0, 381, 1562, 1, 0, 0, 0, 383, 1564, 1, 0, 0, 0, 385, 1566, 1, 0, 0, 0, 387, 1568, 1, 0, 0, 0, 389, 1570, 1, 0, 0, 0, 391, 1572, 1, 0, 0, 0, 393, 1574, 1, 0, 0, 0, 395, 1576, 1, 0, 0, 0, 397, 1578, 1, 0, 0, 0, 399, 1580, 1, 0, 0, 0, 401, 1582, 1, 0, 0, 0, 403, 1584, 1, 0, 0, 0, 405, 1586, 1, 0, 0, 0, 407, 1588, 1, 0, 0, 0, 409, 410, 5, 42, 0, 0, 410, 2, 1, 0, 0, 0, 411, 412, 5, 40, 0, 0, 412, 4, 1, 0, 0, 0, 413, 414, 5, 41, 0, 0, 414, 6, 1, 0, 0, 0, 415, 416, 5, 123, 0, 0, 416, 8, 1, 0, 0, 0, 417, 418, 5, 125, 0, 0, 418, 10, 1, 0, 0, 0, 419, 420, 5, 59, 0, 0, 420, 12, 1, 0, 0, 0, 421, 422, 5, 46, 0, 0, 422, 14, 1, 0, 0, 0, 423, 424, 5, 44, 0, 0, 424, 16, 1, 0, 0, 0, 425, 426, 5, 97, 0, 0, 426, 18, 1, 0, 0, 0, 427, 428, 5, 124, 0, 0, 428, 20, 1, 0, 0, 0, 429, 430, 5, 47, 0, 0, 430, 22, 1, 0, 0, 0, 431, 432, 5, 94, 0, 0, 432, 24, 1, 0, 0, 0, 433, 434, 5, 43, 0, 0, 434, 26, 1, 0, 0, 0, 435, 436, 5, 63, 0, 0, 436, 28, 1, 0, 0, 0, 437, 438, 5, 33, 0, 0, 438, 30, 1, 0, 0, 0, 439, 440, 5, 91, 0, 0, 440, 32, 1, 0, 0, 0, 441, 442, 5, 93, 0, 0, 442, 34, 1, 0, 0, 0, 443, 444, 5, 124, 0, 0, 444, 445, 5, 124, 0, 0, 445, 36, 1, 0, 0, 0, 446, 447, 5, 38, 0, 0, 447, 448, 5, 38, 0, 0, 448, 38, 1, 0, 0, 0, 449, 450, 5, 61, 0, 0, 450, 40, 1, 0, 0, 0, 451, 452, 5, 33, 0, 0, 452, 453, 5, 61, 0, 0, 453, 42, 1, 0, 0, 0, 454, 455, 5, 60, 0, 0, 455, 44, 1, 0, 0, 0, 456, 457, 5, 62, 0, 0, 457, 46, 1, 0, 0, 0, 458, 459, 5, 60, 0, 0, 459, 460, 5, 61, 0, 0, 460, 48, 1, 0, 0, 0, 461, 462, 5, 62, 0, 0, 462, 463, 5, 61, 0, 0, 463, 50, 1, 0, 0, 0, 464, 465, 5, 45, 0, 0, 465, 52, 1, 0, 0, 0, 466, 467, 5, 94, 0, 0, 467, 468, 5, 94, 0, 0, 468, 54, 1, 0, 0, 0, 469, 470, 5, 116, 0, 0, 470, 471, 5, 114, 0, 0, 471, 472, 5, 117, 0, 0, 472, 473, 5, 101, 0, 0, 473, 56, 1, 0, 0, 0, 474, 475, 5, 102, 0, 0, 475, 476, 5, 97, 0, 0, 476, 477, 5, 108, 0, 0, 477, 478, 5, 115, 0, 0, 478, 479, 5, 101, 0, 0, 479, 58, 1, 0, 0, 0, 480, 481, 3, 359, 179, 0, 481, 482, 3, 357, 178, 0, 482, 483, 3, 393, 196, 0, 483, 484, 3, 365, 182, 0, 484, 60, 1, 0, 0, 0, 485, 486, 3, 387, 193, 0, 486, 487, 3, 391, 195, 0, 487, 488, 3, 365, 182, 0, 488, 489, 3, 367, 183, 0, 489, 490, 3, 373, 186, 0, 490, 491, 3, 403, 201, 0, 491, 62, 1, 0, 0, 0, 492, 493, 3, 393, 196, 0, 493, 494, 3, 365, 182, 0, 494, 495, 3, 379, 189, 0, 495, 496, 3, 365, 182, 0, 496, 497, 3, 361, 180, 0, 497, 498, 3, 395, 197, 0, 498, 64, 1, 0, 0, 0, 499, 500, 3, 363, 181, 0, 500, 501, 3, 373, 186, 0, 501, 502, 3, 393, 196, 0, 502, 503, 3, 395, 197, 0, 503, 504, 3, 373, 186, 0, 504, 505, 3, 383, 191, 0, 505, 506, 3, 361, 180, 0, 506, 507, 3, 395, 197, 0, 507, 66, 1, 0, 0, 0, 508, 509, 3, 391, 195, 0, 509, 510, 3, 365, 182, 0, 510, 511, 3, 363, 181, 0, 511, 512, 3, 397, 198, 0, 512, 513, 3, 361, 180, 0, 513, 514, 3, 365, 182, 0, 514, 515, 3, 363, 181, 0, 515, 68, 1, 0, 0, 0, 516, 517, 3, 357, 178, 0, 517, 518, 3, 393, 196, 0, 518, 70, 1, 0, 0, 0, 519, 520, 3, 361, 180, 0, 520, 521, 3, 385, 192, 0, 521, 522, 3, 383, 191, 0, 522, 523, 3, 393, 196, 0, 523, 524, 3, 395, 197, 0, 524, 525, 3, 391, 195, 0, 525, 526, 3, 397, 198, 0, 526, 527, 3, 361, 180, 0, 527, 528, 3, 395, 197, 0, 528, 72, 1, 0, 0, 0, 529, 530, 3, 401, 200, 0, 530, 531, 3, 371, 185, 0, 531, 532, 3, 365, 182, 0, 532, 533, 3, 391, 195, 0, 533, 534, 3, 365, 182, 0, 534, 74, 1, 0, 0, 0, 535, 536, 3, 363, 181, 0, 536, 537, 3, 365, 182, 0, 537, 538, 3, 393, 196, 0, 538, 539, 3, 361, 180, 0, 539, 540, 3, 391, 195, 0, 540, 541, 3, 373, 186, 0, 541, 542, 3, 359, 179, 0, 542, 543, 3, 365, 182, 0, 543, 76, 1, 0, 0, 0, 544, 545, 3, 357, 178, 0, 545, 546, 3, 393, 196, 0, 546, 547, 3, 377, 188, 0, 547, 78, 1, 0, 0, 0, 548, 549, 3, 367, 183, 0, 549, 550, 3, 391, 195, 0, 550, 551, 3, 385, 192, 0, 551, 552, 3, 381, 190, 0, 552, 80, 1, 0, 0, 0, 553, 554, 3, 383, 191, 0, 554, 555, 3, 357, 178, 0, 555, 556, 3, 381, 190, 0, 556, 557, 3, 365, 182, 0, 557, 558, 3, 363, 181, 0, 558, 82, 1, 0, 0, 0, 559, 560, 3, 369, 184, 0, 560, 561, 3, 391, 195, 0, 561, 562, 3, 385, 192, 0, 562, 563, 3, 397, 198, 0, 563, 565, 3, 387, 193, 0, 564, 566, 3, 353, 176, 0, 565, 564, 1, 0, 0, 0, 566, 567, 1, 0, 0, 0, 567, 565, 1, 0, 0, 0, 567, 568, 1, 0, 0, 0, 568, 569, 1, 0, 0, 0, 569, 570, 3, 359, 179, 0, 570, 571, 3, 405, 202, 0, 571, 84, 1, 0, 0, 0, 572, 573, 3, 369, 184, 0, 573, 574, 3, 391, 195, 0, 574, 575, 3, 385, 192, 0, 575, 576, 3, 397, 198, 0, 576, 577, 3, 387, 193, 0, 577, 578, 5, 95, 0, 0, 578, 579, 3, 361, 180, 0, 579, 580, 3, 385, 192, 0, 580, 581, 3, 383, 191, 0, 581, 582, 3, 361, 180, 0, 582, 583, 3, 357, 178, 0, 583, 584, 3, 395, 197, 0, 584, 86, 1, 0, 0, 0, 585, 586, 3, 371, 185, 0, 586, 587, 3, 357, 178, 0, 587, 588, 3, 399, 199, 0, 588, 589, 3, 373, 186, 0, 589, 590, 3, 383, 191, 0, 590, 591, 3, 369, 184, 0, 591, 88, 1, 0, 0, 0, 592, 593, 3, 385, 192, 0, 593, 594, 3, 391, 195, 0, 594, 595, 3, 363, 181, 0, 595, 596, 3, 365, 182, 0, 596, 598, 3, 391, 195, 0, 597, 599, 3, 353, 176, 0, 598, 597, 1, 0, 0, 0, 599, 600, 1, 0, 0, 0, 600, 598, 1, 0, 0, 0, 600, 601, 1, 0, 0, 0, 601, 602, 1, 0, 0, 0, 602, 603, 3, 359, 179, 0, 603, 604, 3, 405, 202, 0, 604, 90, 1, 0, 0, 0, 605, 606, 3, 373, 186, 0, 606, 607, 3, 383, 191, 0, 607, 608, 3, 395, 197, 0, 608, 609, 3, 365, 182, 0, 609, 610, 3, 391, 195, 0, 610, 611, 3, 383, 191, 0, 611, 612, 3, 357, 178, 0, 612, 614, 3, 379, 189, 0, 613, 615, 3, 353, 176, 0, 614, 613, 1, 0, 0, 0, 615, 616, 1, 0, 0, 0, 616, 614, 1, 0, 0, 0, 616, 617, 1, 0, 0, 0, 617, 618, 1, 0, 0, 0, 618, 619, 3, 393, 196, 0, 619, 620, 3, 385, 192, 0, 620, 621, 3, 391, 195, 0, 621, 623, 3, 395, 197, 0, 622, 624, 3, 353, 176, 0, 623, 622, 1, 0, 0, 0, 624, 625, 1, 0, 0, 0, 625, 623, 1, 0, 0, 0, 625, 626, 1, 0, 0, 0, 626, 627, 1, 0, 0, 0, 627, 628, 3, 359, 179, 0, 628, 629, 3, 405, 202, 0, 629, 92, 1, 0, 0, 0, 630, 631, 3, 357, 178, 0, 631, 632, 3, 393, 196, 0, 632, 633, 3, 361, 180, 0, 633, 94, 1, 0, 0, 0, 634, 635, 3, 363, 181, 0, 635, 636, 3, 365, 182, 0, 636, 637, 3, 393, 196, 0, 637, 638, 3, 361, 180, 0, 638, 96, 1, 0, 0, 0, 639, 640, 3, 379, 189, 0, 640, 641, 3, 373, 186, 0, 641, 642, 3, 381, 190, 0, 642, 643, 3, 373, 186, 0, 643, 644, 3, 395, 197, 0, 644, 98, 1, 0, 0, 0, 645, 646, 3, 385, 192, 0, 646, 647, 3, 367, 183, 0, 647, 648, 3, 367, 183, 0, 648, 649, 3, 393, 196, 0, 649, 650, 3, 365, 182, 0, 650, 651, 3, 395, 197, 0, 651, 100, 1, 0, 0, 0, 652, 653, 3, 395, 197, 0, 653, 654, 3, 365, 182, 0, 654, 655, 3, 403, 201, 0, 655, 656, 3, 395, 197, 0, 656, 657, 3, 379, 189, 0, 657, 658, 3, 373, 186, 0, 658, 659, 3, 381, 190, 0, 659, 660, 3, 373, 186, 0, 660, 661, 3, 395, 197, 0, 661, 102, 1, 0, 0, 0, 662, 663, 3, 399, 199, 0, 663, 664, 3, 357, 178, 0, 664, 665, 3, 379, 189, 0, 665, 666, 3, 397, 198, 0, 666, 667, 3, 365, 182, 0, 667, 668, 3, 393, 196, 0, 668, 104, 1, 0, 0, 0, 669, 670, 3, 379, 189, 0, 670, 671, 3, 385, 192, 0, 671, 672, 3, 357, 178, 0, 672, 673, 3, 363, 181, 0, 673, 106, 1, 0, 0, 0, 674, 675, 3, 393, 196, 0, 675, 676, 3, 373, 186, 0, 676, 677, 3, 379, 189, 0, 677, 678, 3, 365, 182, 0, 678, 679, 3, 383, 191, 0, 679, 680, 3, 395, 197, 0, 680, 108, 1, 0, 0, 0, 681, 682, 3, 373, 186, 0, 682, 683, 3, 383, 191, 0, 683, 684, 3, 395, 197, 0, 684, 685, 3, 385, 192, 0, 685, 110, 1, 0, 0, 0, 686, 687, 3, 361, 180, 0, 687, 688, 3, 379, 189, 0, 688, 689, 3, 365, 182, 0, 689, 690, 3, 357, 178, 0, 690, 691, 3, 391, 195, 0, 691, 112, 1, 0, 0, 0, 692, 693, 3, 363, 181, 0, 693, 694, 3, 391, 195, 0, 694, 695, 3, 385, 192, 0, 695, 696, 3, 387, 193, 0, 696, 114, 1, 0, 0, 0, 697, 698, 3, 361, 180, 0, 698, 699, 3, 391, 195, 0, 699, 700, 3, 365, 182, 0, 700, 701, 3, 357, 178, 0, 701, 702, 3, 395, 197, 0, 702, 703, 3, 365, 182, 0, 703, 116, 1, 0, 0, 0, 704, 705, 3, 357, 178, 0, 705, 706, 3, 363, 181, 0, 706, 707, 3, 363, 181, 0, 707, 118, 1, 0, 0, 0, 708, 709, 3, 395, 197, 0, 709, 710, 3, 385, 192, 0, 710, 120, 1, 0, 0, 0, 711, 712, 3, 363, 181, 0, 712, 713, 3, 357, 178, 0, 713, 714, 3, 395, 197, 0, 714, 715, 3, 357, 178, 0, 715, 122, 1, 0, 0, 0, 716, 717, 3, 381, 190, 0, 717, 718, 3, 385, 192, 0, 718, 719, 3, 399, 199, 0, 719, 720, 3, 365, 182, 0, 720, 124, 1, 0, 0, 0, 721, 722, 3, 361, 180, 0, 722, 723, 3, 385, 192, 0, 723, 724, 3, 387, 193, 0, 724, 725, 3, 405, 202, 0, 725, 126, 1, 0, 0, 0, 726, 727, 3, 373, 186, 0, 727, 728, 3, 383, 191, 0, 728, 729, 3, 393, 196, 0, 729, 730, 3, 365, 182, 0, 730, 731, 3, 391, 195, 0, 731, 732, 3, 395, 197, 0, 732, 128, 1, 0, 0, 0, 733, 734, 3, 363, 181, 0, 734, 735, 3, 365, 182, 0, 735, 736, 3, 379, 189, 0, 736, 737, 3, 365, 182, 0, 737, 738, 3, 395, 197, 0, 738, 739, 3, 365, 182, 0, 739, 130, 1, 0, 0, 0, 740, 741, 3, 401, 200, 0, 741, 742, 3, 373, 186, 0, 742, 743, 3, 395, 197, 0, 743, 744, 3, 371, 185, 0, 744, 132, 1, 0, 0, 0, 745, 746, 3, 397, 198, 0, 746, 747, 3, 393, 196, 0, 747, 748, 3, 373, 186, 0, 748, 749, 3, 383, 191, 0, 749, 750, 3, 369, 184, 0, 750, 134, 1, 0, 0, 0, 751, 752, 3, 363, 181, 0, 752, 753, 3, 365, 182, 0, 753, 754, 3, 367, 183, 0, 754, 755, 3, 357, 178, 0, 755, 756, 3, 397, 198, 0, 756, 757, 3, 379, 189, 0, 757, 758, 3, 395, 197, 0, 758, 136, 1, 0, 0, 0, 759, 760, 3, 369, 184, 0, 760, 761, 3, 391, 195, 0, 761, 762, 3, 357, 178, 0, 762, 763, 3, 387, 193, 0, 763, 764, 3, 371, 185, 0, 764, 138, 1, 0, 0, 0, 765, 766, 3, 357, 178, 0, 766, 767, 3, 379, 189, 0, 767, 768, 3, 379, 189, 0, 768, 140, 1, 0, 0, 0, 769, 770, 3, 385, 192, 0, 770, 771, 3, 387, 193, 0, 771, 772, 3, 395, 197, 0, 772, 773, 3, 373, 186, 0, 773, 774, 3, 385, 192, 0, 774, 775, 3, 383, 191, 0, 775, 776, 3, 357, 178, 0, 776, 777, 3, 379, 189, 0, 777, 142, 1, 0, 0, 0, 778, 779, 3, 393, 196, 0, 779, 780, 3, 365, 182, 0, 780, 781, 3, 391, 195, 0, 781, 782, 3, 399, 199, 0, 782, 783, 3, 373, 186, 0, 783, 784, 3, 361, 180, 0, 784, 785, 3, 365, 182, 0, 785, 144, 1, 0, 0, 0, 786, 787, 3, 359, 179, 0, 787, 788, 3, 373, 186, 0, 788, 789, 3, 383, 191, 0, 789, 790, 3, 363, 181, 0, 790, 146, 1, 0, 0, 0, 791, 792, 3, 397, 198, 0, 792, 793, 3, 383, 191, 0, 793, 794, 3, 363, 181, 0, 794, 795, 3, 365, 182, 0, 795, 796, 3, 367, 183, 0, 796, 148, 1, 0, 0, 0, 797, 798, 3, 381, 190, 0, 798, 799, 3, 373, 186, 0, 799, 800, 3, 383, 191, 0, 800, 801, 3, 397, 198, 0, 801, 802, 3, 393, 196, 0, 802, 150, 1, 0, 0, 0, 803, 804, 3, 397, 198, 0, 804, 805, 3, 383, 191, 0, 805, 806, 3, 373, 186, 0, 806, 807, 3, 385, 192, 0, 807, 808, 3, 383, 191, 0, 808, 152, 1, 0, 0, 0, 809, 810, 3, 367, 183, 0, 810, 811, 3, 373, 186, 0, 811, 812, 3, 379, 189, 0, 812, 813, 3, 395, 197, 0, 813, 814, 3, 365, 182, 0, 814, 815, 3, 391, 195, 0, 815, 154, 1, 0, 0, 0, 816, 817, 3, 383, 191, 0, 817, 818, 3, 385, 192, 0, 818, 819, 3, 395, 197, 0, 819, 156, 1, 0, 0, 0, 820, 821, 3, 373, 186, 0, 821, 822, 3, 383, 191, 0, 822, 158, 1, 0, 0, 0, 823, 824, 3, 393, 196, 0, 824, 825, 3, 395, 197, 0, 825, 826, 3, 391, 195, 0, 826, 160, 1, 0, 0, 0, 827, 828, 3, 379, 189, 0, 828, 829, 3, 357, 178, 0, 829, 830, 3, 383, 191, 0, 830, 831, 3, 369, 184, 0, 831, 162, 1, 0, 0, 0, 832, 833, 3, 379, 189, 0, 833, 834, 3, 357, 178, 0, 834, 835, 3, 383, 191, 0, 835, 836, 3, 369, 184, 0, 836, 837, 3, 381, 190, 0, 837, 838, 3, 357, 178, 0, 838, 839, 3, 395, 197, 0, 839, 840, 3, 361, 180, 0, 840, 841, 3, 371, 185, 0, 841, 842, 3, 365, 182, 0, 842, 843, 3, 393, 196, 0, 843, 164, 1, 0, 0, 0, 844, 845, 3, 363, 181, 0, 845, 846, 3, 357, 178, 0, 846, 847, 3, 395, 197, 0, 847, 848, 3, 357, 178, 0, 848, 849, 3, 395, 197, 0, 849, 850, 3, 405, 202, 0, 850, 851, 3, 387, 193, 0, 851, 852, 3, 365, 182, 0, 852, 166, 1, 0, 0, 0, 853, 854, 3, 359, 179, 0, 854, 855, 3, 385, 192, 0, 855, 856, 3, 397, 198, 0, 856, 857, 3, 383, 191, 0, 857, 858, 3, 363, 181, 0, 858, 168, 1, 0, 0, 0, 859, 860, 3, 373, 186, 0, 860, 861, 3, 391, 195, 0, 861, 862, 3, 373, 186, 0, 862, 170, 1, 0, 0, 0, 863, 864, 3, 397, 198, 0, 864, 865, 3, 391, 195, 0, 865, 866, 3, 373, 186, 0, 866, 172, 1, 0, 0, 0, 867, 868, 3, 359, 179, 0, 868, 869, 3, 383, 191, 0, 869, 870, 3, 385, 192, 0, 870, 871, 3, 363, 181, 0, 871, 872, 3, 365, 182, 0, 872, 174, 1, 0, 0, 0, 873, 874, 3, 391, 195, 0, 874, 875, 3, 357, 178, 0, 875, 876, 3, 383, 191, 0, 876, 877, 3, 363, 181, 0, 877, 176, 1, 0, 0, 0, 878, 879, 3, 357, 178, 0, 879, 880, 3, 359, 179, 0, 880, 881, 3, 393, 196, 0, 881, 178, 1, 0, 0, 0, 882, 883, 3, 361, 180, 0, 883, 884, 3, 365, 182, 0, 884, 885, 3, 373, 186, 0, 885, 886, 3, 379, 189, 0, 886, 180, 1, 0, 0, 0, 887, 888, 3, 367, 183, 0, 888, 889, 3, 379, 189, 0, 889, 890, 3, 385, 192, 0, 890, 891, 3, 385, 192, 0, 891, 892, 3, 391, 195, 0, 892, 182, 1, 0, 0, 0, 893, 894, 3, 391, 195, 0, 894, 895, 3, 385, 192, 0, 895, 896, 3, 397, 198, 0, 896, 897, 3, 383, 191, 0, 897, 898, 3, 363, 181, 0, 898, 184, 1, 0, 0, 0, 899, 900, 3, 361, 180, 0, 900, 901, 3, 385, 192, 0, 901, 902, 3, 383, 191, 0, 902, 903, 3, 361, 180, 0, 903, 904, 3, 357, 178, 0, 904, 905, 3, 395, 197, 0, 905, 186, 1, 0, 0, 0, 906, 907, 3, 393, 196, 0, 907, 908, 3, 395, 197, 0, 908, 909, 3, 391, 195, 0, 909, 910, 3, 379, 189, 0, 910, 911, 3, 365, 182, 0, 911, 912, 3, 383, 191, 0, 912, 188, 1, 0, 0, 0, 913, 914, 3, 397, 198, 0, 914, 915, 3, 361, 180, 0, 915, 916, 3, 357, 178, 0, 916, 917, 3, 393, 196, 0, 917, 918, 3, 365, 182, 0, 918, 190, 1, 0, 0, 0, 919, 920, 3, 379, 189, 0, 920, 921, 3, 361, 180, 0, 921, 922, 3, 357, 178, 0, 922, 923, 3, 393, 196, 0, 923, 924, 3, 365, 182, 0, 924, 192, 1, 0, 0, 0, 925, 926, 3, 365, 182, 0, 926, 927, 3, 383, 191, 0, 927, 928, 3, 361, 180, 0, 928, 929, 3, 385, 192, 0, 929, 930, 3, 363, 181, 0, 930, 931, 3, 365, 182, 0, 931, 932, 5, 95, 0, 0, 932, 933, 3, 367, 183, 0, 933, 934, 3, 385, 192, 0, 934, 935, 3, 391, 195, 0, 935, 936, 5, 95, 0, 0, 936, 937, 3, 397, 198, 0, 937, 938, 3, 391, 195, 0, 938, 939, 3, 373, 186, 0, 939, 194, 1, 0, 0, 0, 940, 941, 3, 367, 183, 0, 941, 942, 3, 385, 192, 0, 942, 943, 3, 391, 195, 0, 943, 196, 1, 0, 0, 0, 944, 945, 3, 361, 180, 0, 945, 946, 3, 385, 192, 0, 946, 947, 3, 383, 191, 0, 947, 948, 3, 395, 197, 0, 948, 949, 3, 357, 178, 0, 949, 950, 3, 373, 186, 0, 950, 951, 3, 383, 191, 0, 951, 952, 3, 393, 196, 0, 952, 198, 1, 0, 0, 0, 953, 954, 3, 393, 196, 0, 954, 955, 3, 395, 197, 0, 955, 956, 3, 391, 195, 0, 956, 957, 3, 393, 196, 0, 957, 958, 3, 395, 197, 0, 958, 959, 3, 357, 178, 0, 959, 960, 3, 391, 195, 0, 960, 961, 3, 395, 197, 0, 961, 962, 3, 393, 196, 0, 962, 200, 1, 0, 0, 0, 963, 964, 3, 393, 196, 0, 964, 965, 3, 395, 197, 0, 965, 966, 3, 391, 195, 0, 966, 967, 3, 365, 182, 0, 967, 968, 3, 383, 191, 0, 968, 969, 3, 363, 181, 0, 969, 970, 3, 393, 196, 0, 970, 202, 1, 0, 0, 0, 971, 972, 3, 393, 196, 0, 972, 973, 3, 395, 197, 0, 973, 974, 3, 391, 195, 0, 974, 975, 3, 359, 179, 0, 975, 976, 3, 365, 182, 0, 976, 977, 3, 367, 183, 0, 977, 978, 3, 385, 192, 0, 978, 979, 3, 391, 195, 0, 979, 980, 3, 365, 182, 0, 980, 204, 1, 0, 0, 0, 981, 982, 3, 393, 196, 0, 982, 983, 3, 395, 197, 0, 983, 984, 3, 391, 195, 0, 984, 985, 3, 357, 178, 0, 985, 986, 3, 367, 183, 0, 986, 987, 3, 395, 197, 0, 987, 988, 3, 365, 182, 0, 988, 989, 3, 391, 195, 0, 989, 206, 1, 0, 0, 0, 990, 991, 3, 405, 202, 0, 991, 992, 3, 365, 182, 0, 992, 993, 3, 357, 178, 0, 993, 994, 3, 391, 195, 0, 994, 208, 1, 0, 0, 0, 995, 996, 3, 381, 190, 0, 996, 997, 3, 385, 192, 0, 997, 998, 3, 383, 191, 0, 998, 999, 3, 395, 197, 0, 999, 1000, 3, 371, 185, 0, 1000, 210, 1, 0, 0, 0, 1001, 1002, 3, 363, 181, 0, 1002, 1003, 3, 357, 178, 0, 1003, 1004, 3, 405, 202, 0, 1004, 212, 1, 0, 0, 0, 1005, 1006, 3, 371, 185, 0, 1006, 1007, 3, 385, 192, 0, 1007, 1008, 3, 397, 198, 0, 1008, 1009, 3, 391, 195, 0, 1009, 1010, 3, 393, 196, 0, 1010, 214, 1, 0, 0, 0, 1011, 1012, 3, 381, 190, 0, 1012, 1013, 3, 373, 186, 0, 1013, 1014, 3, 383, 191, 0, 1014, 1015, 3, 397, 198, 0, 1015, 1016, 3, 395, 197, 0, 1016, 1017, 3, 365, 182, 0, 1017, 1018, 3, 393, 196, 0, 1018, 216, 1, 0, 0, 0, 1019, 1020, 3, 393, 196, 0, 1020, 1021, 3, 365, 182, 0, 1021, 1022, 3, 361, 180, 0, 1022, 1023, 3, 385, 192, 0, 1023, 1024, 3, 383, 191, 0, 1024, 1025, 3, 363, 181, 0, 1025, 1026, 3, 393, 196, 0, 1026, 218, 1, 0, 0, 0, 1027, 1028, 3, 395, 197, 0, 1028, 1029, 3, 373, 186, 0, 1029, 1030, 3, 381, 190, 0, 1030, 1031, 3, 365, 182, 0, 1031, 1032, 3, 407, 203, 0, 1032, 1033, 3, 385, 192, 0, 1033, 1034, 3, 383, 191, 0, 1034, 1035, 3, 365, 182, 0, 1035, 220, 1, 0, 0, 0, 1036, 1037, 3, 395, 197, 0, 1037, 1038, 3, 407, 203, 0, 1038, 222, 1, 0, 0, 0, 1039, 1040, 3, 383, 191, 0, 1040, 1041, 3, 385, 192, 0, 1041, 1042, 3, 401, 200, 0, 1042, 224, 1, 0, 0, 0, 1043, 1044, 3, 397, 198, 0, 1044, 1045, 3, 397, 198, 0, 1045, 1046, 3, 373, 186, 0, 1046, 1047, 3, 363, 181, 0, 1047, 226, 1, 0, 0, 0, 1048, 1049, 3, 393, 196, 0, 1049, 1050, 3, 395, 197, 0, 1050, 1051, 3, 391, 195, 0, 1051, 1052, 3, 397, 198, 0, 1052, 1053, 3, 397, 198, 0, 1053, 1054, 3, 373, 186, 0, 1054, 1055, 3, 363, 181, 0, 1055, 228, 1, 0, 0, 0, 1056, 1057, 3, 393, 196, 0, 1057, 1058, 3, 371, 185, 0, 1058, 1059, 3, 357, 178, 0, 1059, 1060, 5, 49, 0, 0, 1060, 230, 1, 0, 0, 0, 1061, 1062, 3, 393, 196, 0, 1062, 1063, 3, 371, 185, 0, 1063, 1064, 3, 357, 178, 0, 1064, 1065, 5, 50, 0, 0, 1065, 1066, 5, 53, 0, 0, 1066, 1067, 5, 54, 0, 0, 1067, 232, 1, 0, 0, 0, 1068, 1069, 3, 393, 196, 0, 1069, 1070, 3, 371, 185, 0, 1070, 1071, 3, 357, 178, 0, 1071, 1072, 5, 51, 0, 0, 1072, 1073, 5, 56, 0, 0, 1073, 1074, 5, 52, 0, 0, 1074, 234, 1, 0, 0, 0, 1075, 1076, 3, 393, 196, 0, 1076, 1077, 3, 371, 185, 0, 1077, 1078, 3, 357, 178, 0, 1078, 1079, 5, 53, 0, 0, 1079, 1080, 5, 49, 0, 0, 1080, 1081, 5, 50, 0, 0, 1081, 236, 1, 0, 0, 0, 1082, 1083, 3, 381, 190, 0, 1083, 1084, 3, 363, 181, 0, 1084, 1085, 5, 53, 0, 0, 1085, 238, 1, 0, 0, 0, 1086, 1087, 3, 361, 180, 0, 1087, 1088, 3, 385, 192, 0, 1088, 1089, 3, 357, 178, 0, 1089, 1090, 3, 379, 189, 0, 1090, 1091, 3, 365, 182, 0, 1091, 1092, 3, 393, 196, 0, 1092, 1093, 3, 361, 180, 0, 1093, 1094, 3, 365, 182, 0, 1094, 240, 1, 0, 0, 0, 1095, 1096, 3, 373, 186, 0, 1096, 1097, 3, 367, 183, 0, 1097, 242, 1, 0, 0, 0, 1098, 1099, 3, 393, 196, 0, 1099, 1100, 3, 395, 197, 0, 1100, 1101, 3, 391, 195, 0, 1101, 1102, 3, 379, 189, 0, 1102, 1103, 3, 357, 178, 0, 1103, 1104, 3, 383, 191, 0, 1104, 1105, 3, 369, 184, 0, 1105, 244, 1, 0, 0, 0, 1106, 1107, 3, 393, 196, 0, 1107, 1108, 3, 395, 197, 0, 1108, 1109, 3, 391, 195, 0, 1109, 1110, 3, 363, 181, 0, 1110, 1111, 3, 395, 197, 0, 1111, 246, 1, 0, 0, 0, 1112, 1113, 3, 393, 196, 0, 1113, 1114, 3, 357, 178, 0, 1114, 1115, 3, 381, 190, 0, 1115, 1116, 3, 365, 182, 0, 1116, 1117, 3, 395, 197, 0, 1117, 1118, 3, 365, 182, 0, 1118, 1119, 3, 391, 195, 0, 1119, 1120, 3, 381, 190, 0, 1120, 248, 1, 0, 0, 0, 1121, 1122, 3, 373, 186, 0, 1122, 1123, 3, 393, 196, 0, 1123, 1124, 3, 373, 186, 0, 1124, 1125, 3, 391, 195, 0, 1125, 1126, 3, 373, 186, 0, 1126, 250, 1, 0, 0, 0, 1127, 1128, 3, 373, 186, 0, 1128, 1129, 3, 393, 196, 0, 1129, 1130, 3, 397, 198, 0, 1130, 1131, 3, 391, 195, 0, 1131, 1132, 3, 373, 186, 0, 1132, 252, 1, 0, 0, 0, 1133, 1134, 3, 373, 186, 0, 1134, 1135, 3, 393, 196, 0, 1135, 1136, 3, 359, 179, 0, 1136, 1137, 3, 379, 189, 0, 1137, 1138, 3, 357, 178, 0, 1138, 1139, 3, 383, 191, 0, 1139, 1140, 3, 377, 188, 0, 1140, 254, 1, 0, 0, 0, 1141, 1142, 3, 373, 186, 0, 1142, 1143, 3, 393, 196, 0, 1143, 1144, 3, 379, 189, 0, 1144, 1145, 3, 373, 186, 0, 1145, 1146, 3, 395, 197, 0, 1146, 1147, 3, 365, 182, 0, 1147, 1148, 3, 391, 195, 0, 1148, 1149, 3, 357, 178, 0, 1149, 1150, 3, 379, 189, 0, 1150, 256, 1, 0, 0, 0, 1151, 1152, 3, 373, 186, 0, 1152, 1153, 3, 393, 196, 0, 1153, 1154, 3, 383, 191, 0, 1154, 1155, 3, 397, 198, 0, 1155, 1156, 3, 381, 190, 0, 1156, 1157, 3, 365, 182, 0, 1157, 1158, 3, 391, 195, 0, 1158, 1159, 3, 373, 186, 0, 1159, 1160, 3, 361, 180, 0, 1160, 258, 1, 0, 0, 0, 1161, 1162, 3, 391, 195, 0, 1162, 1163, 3, 365, 182, 0, 1163, 1164, 3, 369, 184, 0, 1164, 1165, 3, 365, 182, 0, 1165, 1166, 3, 403, 201, 0, 1166, 260, 1, 0, 0, 0, 1167, 1168, 3, 393, 196, 0, 1168, 1169, 3, 397, 198, 0, 1169, 1170, 3, 359, 179, 0, 1170, 1171, 3, 393, 196, 0, 1171, 1172, 3, 395, 197, 0, 1172, 1173, 3, 391, 195, 0, 1173, 262, 1, 0, 0, 0, 1174, 1175, 3, 391, 195, 0, 1175, 1176, 3, 365, 182, 0, 1176, 1177, 3, 387, 193, 0, 1177, 1178, 3, 379, 189, 0, 1178, 1179, 3, 357, 178, 0, 1179, 1180, 3, 361, 180, 0, 1180, 1181, 3, 365, 182, 0, 1181, 264, 1, 0, 0, 0, 1182, 1183, 3, 365, 182, 0, 1183, 1184, 3, 403, 201, 0, 1184, 1185, 3, 373, 186, 0, 1185, 1186, 3, 393, 196, 0, 1186, 1187, 3, 395, 197, 0, 1187, 1188, 3, 393, 196, 0, 1188, 266, 1, 0, 0, 0, 1189, 1190, 3, 361, 180, 0, 1190, 1191, 3, 385, 192, 0, 1191, 1192, 3, 397, 198, 0, 1192, 1193, 3, 383, 191, 0, 1193, 1194, 3, 395, 197, 0, 1194, 268, 1, 0, 0, 0, 1195, 1196, 3, 393, 196, 0, 1196, 1197, 3, 397, 198, 0, 1197, 1198, 3, 381, 190, 0, 1198, 270, 1, 0, 0, 0, 1199, 1200, 3, 381, 190, 0, 1200, 1201, 3, 373, 186, 0, 1201, 1202, 3, 383, 191, 0, 1202, 272, 1, 0, 0, 0, 1203, 1204, 3, 381, 190, 0, 1204, 1205, 3, 357, 178, 0, 1205, 1206, 3, 403, 201, 0, 1206, 274, 1, 0, 0, 0, 1207, 1208, 3, 357, 178, 0, 1208, 1209, 3, 399, 199, 0, 1209, 1210, 3, 369, 184, 0, 1210, 276, 1, 0, 0, 0, 1211, 1212, 3, 393, 196, 0, 1212, 1213, 3, 357, 178, 0, 1213, 1214, 3, 381, 190, 0, 1214, 1215, 3, 387, 193, 0, 1215, 1216, 3, 379, 189, 0, 1216, 1217, 3, 365, 182, 0, 1217, 278, 1, 0, 0, 0, 1218, 1219, 3, 393, 196, 0, 1219, 1220, 3, 365, 182, 0, 1220, 1221, 3, 387, 193, 0, 1221, 1222, 3, 357, 178, 0, 1222, 1223, 3, 391, 195, 0, 1223, 1224, 3, 357, 178, 0, 1224, 1225, 3, 395, 197, 0, 1225, 1226, 3, 385, 192, 0, 1226, 1227, 3, 391, 195, 0, 1227, 280, 1, 0, 0, 0, 1228, 1232, 5, 60, 0, 0, 1229, 1231, 8, 0, 0, 0, 1230, 1229, 1, 0, 0, 0, 1231, 1234, 1, 0, 0, 0, 1232, 1230, 1, 0, 0, 0, 1232, 1233, 1, 0, 0, 0, 1233, 1235, 1, 0, 0, 0, 1234, 1232, 1, 0, 0, 0, 1235, 1236, 5, 62, 0, 0, 1236, 282, 1, 0, 0, 0, 1237, 1239, 3, 337, 168, 0, 1238, 1237, 1, 0, 0, 0, 1238, 1239, 1, 0, 0, 0, 1239, 1240, 1, 0, 0, 0, 1240, 1241, 5, 58, 0, 0, 1241, 284, 1, 0, 0, 0, 1242, 1243, 3, 283, 141, 0, 1243, 1244, 3, 339, 169, 0, 1244, 286, 1, 0, 0, 0, 1245, 1246, 5, 95, 0, 0, 1246, 1247, 5, 58, 0, 0, 1247, 1250, 1, 0, 0, 0, 1248, 1251, 3, 331, 165, 0, 1249, 1251, 3, 351, 175, 0, 1250, 1248, 1, 0, 0, 0, 1250, 1249, 1, 0, 0, 0, 1251, 1260, 1, 0, 0, 0, 1252, 1255, 3, 335, 167, 0, 1253, 1255, 5, 46, 0, 0, 1254, 1252, 1, 0, 0, 0, 1254, 1253, 1, 0, 0, 0, 1255, 1258, 1, 0, 0, 0, 1256, 1254, 1, 0, 0, 0, 1256, 1257, 1, 0, 0, 0, 1257, 1259, 1, 0, 0, 0, 1258, 1256, 1, 0, 0, 0, 1259, 1261, 3, 335, 167, 0, 1260, 1256, 1, 0, 0, 0, 1260, 1261, 1, 0, 0, 0, 1261, 288, 1, 0, 0, 0, 1262, 1263, 5, 63, 0, 0, 1263, 1264, 3, 333, 166, 0, 1264, 290, 1, 0, 0, 0, 1265, 1266, 5, 36, 0, 0, 1266, 1267, 3, 333, 166, 0, 1267, 292, 1, 0, 0, 0, 1268, 1270, 5, 64, 0, 0, 1269, 1271, 7, 1, 0, 0, 1270, 1269, 1, 0, 0, 0, 1271, 1272, 1, 0, 0, 0, 1272, 1270, 1, 0, 0, 0, 1272, 1273, 1, 0, 0, 0, 1273, 1283, 1, 0, 0, 0, 1274, 1277, 5, 45, 0, 0, 1275, 1278, 7, 1, 0, 0, 1276, 1278, 3, 351, 175, 0, 1277, 1275, 1, 0, 0, 0, 1277, 1276, 1, 0, 0, 0, 1278, 1279, 1, 0, 0, 0, 1279, 1277, 1, 0, 0, 0, 1279, 1280, 1, 0, 0, 0, 1280, 1282, 1, 0, 0, 0, 1281, 1274, 1, 0, 0, 0, 1282, 1285, 1, 0, 0, 0, 1283, 1281, 1, 0, 0, 0, 1283, 1284, 1, 0, 0, 0, 1284, 294, 1, 0, 0, 0, 1285, 1283, 1, 0, 0, 0, 1286, 1287, 3, 293, 146, 0, 1287, 1288, 5, 64, 0, 0, 1288, 296, 1, 0, 0, 0, 1289, 1291, 3, 351, 175, 0, 1290, 1289, 1, 0, 0, 0, 1291, 1292, 1, 0, 0, 0, 1292, 1290, 1, 0, 0, 0, 1292, 1293, 1, 0, 0, 0, 1293, 298, 1, 0, 0, 0, 1294, 1296, 3, 351, 175, 0, 1295, 1294, 1, 0, 0, 0, 1296, 1299, 1, 0, 0, 0, 1297, 1295, 1, 0, 0, 0, 1297, 1298, 1, 0, 0, 0, 1298, 1300, 1, 0, 0, 0, 1299, 1297, 1, 0, 0, 0, 1300, 1302, 5, 46, 0, 0, 1301, 1303, 3, 351, 175, 0, 1302, 1301, 1, 0, 0, 0, 1303, 1304, 1, 0, 0, 0, 1304, 1302, 1, 0, 0, 0, 1304, 1305, 1, 0, 0, 0, 1305, 300, 1, 0, 0, 0, 1306, 1308, 3, 351, 175, 0, 1307, 1306, 1, 0, 0, 0, 1308, 1309, 1, 0, 0, 0, 1309, 1307, 1, 0, 0, 0, 1309, 1310, 1, 0, 0, 0, 1310, 1311, 1, 0, 0, 0, 1311, 1315, 5, 46, 0, 0, 1312, 1314, 3, 351, 175, 0, 1313, 1312, 1, 0, 0, 0, 1314, 1317, 1, 0, 0, 0, 1315, 1313, 1, 0, 0, 0, 1315, 1316, 1, 0, 0, 0, 1316, 1318, 1, 0, 0, 0, 1317, 1315, 1, 0, 0, 0, 1318, 1319, 3, 315, 157, 0, 1319, 1336, 1, 0, 0, 0, 1320, 1322, 5, 46, 0, 0, 1321, 1323, 3, 351, 175, 0, 1322, 1321, 1, 0, 0, 0, 1323, 1324, 1, 0, 0, 0, 1324, 1322, 1, 0, 0, 0, 1324, 1325, 1, 0, 0, 0, 1325, 1326, 1, 0, 0, 0, 1326, 1327, 3, 315, 157, 0, 1327, 1336, 1, 0, 0, 0, 1328, 1330, 3, 351, 175, 0, 1329, 1328, 1, 0, 0, 0, 1330, 1331, 1, 0, 0, 0, 1331, 1329, 1, 0, 0, 0, 1331, 1332, 1, 0, 0, 0, 1332, 1333, 1, 0, 0, 0, 1333, 1334, 3, 315, 157, 0, 1334, 1336, 1, 0, 0, 0, 1335, 1307, 1, 0, 0, 0, 1335, 1320, 1, 0, 0, 0, 1335, 1329, 1, 0, 0, 0, 1336, 302, 1, 0, 0, 0, 1337, 1338, 5, 43, 0, 0, 1338, 1339, 3, 297, 148, 0, 1339, 304, 1, 0, 0, 0, 1340, 1341, 5, 43, 0, 0, 1341, 1342, 3, 299, 149, 0, 1342, 306, 1, 0, 0, 0, 1343, 1344, 5, 43, 0, 0, 1344, 1345, 3, 301, 150, 0, 1345, 308, 1, 0, 0, 0, 1346, 1347, 5, 45, 0, 0, 1347, 1348, 3, 297, 148, 0, 1348, 310, 1, 0, 0, 0, 1349, 1350, 5, 45, 0, 0, 1350, 1351, 3, 299, 149, 0, 1351, 312, 1, 0, 0, 0, 1352, 1353, 5, 45, 0, 0, 1353, 1354, 3, 301, 150, 0, 1354, 314, 1, 0, 0, 0, 1355, 1357, 7, 2, 0, 0, 1356, 1358, 7, 3, 0, 0, 1357, 1356, 1, 0, 0, 0, 1357, 1358, 1, 0, 0, 0, 1358, 1360, 1, 0, 0, 0, 1359, 1361, 3, 351, 175, 0, 1360, 1359, 1, 0, 0, 0, 1361, 1362, 1, 0, 0, 0, 1362, 1360, 1, 0, 0, 0, 1362, 1363, 1, 0, 0, 0, 1363, 316, 1, 0, 0, 0, 1364, 1369, 5, 39, 0, 0, 1365, 1368, 8, 4, 0, 0, 1366, 1368, 3, 325, 162, 0, 1367, 1365, 1, 0, 0, 0, 1367, 1366, 1, 0, 0, 0, 1368, 1371, 1, 0, 0, 0, 1369, 1367, 1, 0, 0, 0, 1369, 1370, 1, 0, 0, 0, 1370, 1372, 1, 0, 0, 0, 1371, 1369, 1, 0, 0, 0, 1372, 1373, 5, 39, 0, 0, 1373, 318, 1, 0, 0, 0, 1374, 1379, 5, 34, 0, 0, 1375, 1378, 8, 5, 0, 0, 1376, 1378, 3, 325, 162, 0, 1377, 1375, 1, 0, 0, 0, 1377, 1376, 1, 0, 0, 0, 1378, 1381, 1, 0, 0, 0, 1379, 1377, 1, 0, 0, 0, 1379, 1380, 1, 0, 0, 0, 1380, 1382, 1, 0, 0, 0, 1381, 1379, 1, 0, 0, 0, 1382, 1383, 5, 34, 0, 0, 1383, 320, 1, 0, 0, 0, 1384, 1385, 5, 39, 0, 0, 1385, 1386, 5, 39, 0, 0, 1386, 1387, 5, 39, 0, 0, 1387, 1399, 1, 0, 0, 0, 1388, 1392, 5, 39, 0, 0, 1389, 1390, 5, 39, 0, 0, 1390, 1392, 5, 39, 0, 0, 1391, 1388, 1, 0, 0, 0, 1391, 1389, 1, 0, 0, 0, 1391, 1392, 1, 0, 0, 0, 1392, 1395, 1, 0, 0, 0, 1393, 1396, 8, 6, 0, 0, 1394, 1396, 3, 325, 162, 0, 1395, 1393, 1, 0, 0, 0, 1395, 1394, 1, 0, 0, 0, 1396, 1398, 1, 0, 0, 0, 1397, 1391, 1, 0, 0, 0, 1398, 1401, 1, 0, 0, 0, 1399, 1397, 1, 0, 0, 0, 1399, 1400, 1, 0, 0, 0, 1400, 1402, 1, 0, 0, 0, 1401, 1399, 1, 0, 0, 0, 1402, 1403, 5, 39, 0, 0, 1403, 1404, 5, 39, 0, 0, 1404, 1405, 5, 39, 0, 0, 1405, 322, 1, 0, 0, 0, 1406, 1407, 5, 34, 0, 0, 1407, 1408, 5, 34, 0, 0, 1408, 1409, 5, 34, 0, 0, 1409, 1421, 1, 0, 0, 0, 1410, 1414, 5, 34, 0, 0, 1411, 1412, 5, 34, 0, 0, 1412, 1414, 5, 34, 0, 0, 1413, 1410, 1, 0, 0, 0, 1413, 1411, 1, 0, 0, 0, 1413, 1414, 1, 0, 0, 0, 1414, 1417, 1, 0, 0, 0, 1415, 1418, 8, 7, 0, 0, 1416, 1418, 3, 325, 162, 0, 1417, 1415, 1, 0, 0, 0, 1417, 1416, 1, 0, 0, 0, 1418, 1420, 1, 0, 0, 0, 1419, 1413, 1, 0, 0, 0, 1420, 1423, 1, 0, 0, 0, 1421, 1419, 1, 0, 0, 0, 1421, 1422, 1, 0, 0, 0, 1422, 1424, 1, 0, 0, 0, 1423, 1421, 1, 0, 0, 0, 1424, 1425, 5, 34, 0, 0, 1425, 1426, 5, 34, 0, 0, 1426, 1427, 5, 34, 0, 0, 1427, 324, 1, 0, 0, 0, 1428, 1429, 5, 92, 0, 0, 1429, 1430, 7, 8, 0, 0, 1430, 326, 1, 0, 0, 0, 1431, 1435, 5, 40, 0, 0, 1432, 1434, 3, 353, 176, 0, 1433, 1432, 1, 0, 0, 0, 1434, 1437, 1, 0, 0, 0, 1435, 1433, 1, 0, 0, 0, 1435, 1436, 1, 0, 0, 0, 1436, 1438, 1, 0, 0, 0, 1437, 1435, 1, 0, 0, 0, 1438, 1439, 5, 41, 0, 0, 1439, 328, 1, 0, 0, 0, 1440, 1444, 5, 91, 0, 0, 1441, 1443, 3, 353, 176, 0, 1442, 1441, 1, 0, 0, 0, 1443, 1446, 1, 0, 0, 0, 1444, 1442, 1, 0, 0, 0, 1444, 1445, 1, 0, 0, 0, 1445, 1447, 1, 0, 0, 0, 1446, 1444, 1, 0, 0, 0, 1447, 1448, 5, 93, 0, 0, 1448, 330, 1, 0, 0, 0, 1449, 1452, 3, 349, 174, 0, 1450, 1452, 5, 95, 0, 0, 1451, 1449, 1, 0, 0, 0, 1451, 1450, 1, 0, 0, 0, 1452, 332, 1, 0, 0, 0, 1453, 1456, 3, 331, 165, 0, 1454, 1456, 3, 351, 175, 0, 1455, 1453, 1, 0, 0, 0, 1455, 1454, 1, 0, 0, 0, 1456, 1462, 1, 0, 0, 0, 1457, 1461, 3, 331, 165, 0, 1458, 1461, 3, 351, 175, 0, 1459, 1461, 7, 9, 0, 0, 1460, 1457, 1, 0, 0, 0, 1460, 1458, 1, 0, 0, 0, 1460, 1459, 1, 0, 0, 0, 1461, 1464, 1, 0, 0, 0, 1462, 1460, 1, 0, 0, 0, 1462, 1463, 1, 0, 0, 0, 1463, 334, 1, 0, 0, 0, 1464, 1462, 1, 0, 0, 0, 1465, 1470, 3, 331, 165, 0, 1466, 1470, 5, 45, 0, 0, 1467, 1470, 3, 351, 175, 0, 1468, 1470, 7, 9, 0, 0, 1469, 1465, 1, 0, 0, 0, 1469, 1466, 1, 0, 0, 0, 1469, 1467, 1, 0, 0, 0, 1469, 1468, 1, 0, 0, 0, 1470, 336, 1, 0, 0, 0, 1471, 1480, 3, 349, 174, 0, 1472, 1475, 3, 335, 167, 0, 1473, 1475, 5, 46, 0, 0, 1474, 1472, 1, 0, 0, 0, 1474, 1473, 1, 0, 0, 0, 1475, 1478, 1, 0, 0, 0, 1476, 1474, 1, 0, 0, 0, 1476, 1477, 1, 0, 0, 0, 1477, 1479, 1, 0, 0, 0, 1478, 1476, 1, 0, 0, 0, 1479, 1481, 3, 335, 167, 0, 1480, 1476, 1, 0, 0, 0, 1480, 1481, 1, 0, 0, 0, 1481, 338, 1, 0, 0, 0, 1482, 1487, 3, 331, 165, 0, 1483, 1487, 5, 58, 0, 0, 1484, 1487, 3, 351, 175, 0, 1485, 1487, 3, 341, 170, 0, 1486, 1482, 1, 0, 0, 0, 1486, 1483, 1, 0, 0, 0, 1486, 1484, 1, 0, 0, 0, 1486, 1485, 1, 0, 0, 0, 1487, 1501, 1, 0, 0, 0, 1488, 1492, 3, 335, 167, 0, 1489, 1492, 7, 10, 0, 0, 1490, 1492, 3, 341, 170, 0, 1491, 1488, 1, 0, 0, 0, 1491, 1489, 1, 0, 0, 0, 1491, 1490, 1, 0, 0, 0, 1492, 1495, 1, 0, 0, 0, 1493, 1491, 1, 0, 0, 0, 1493, 1494, 1, 0, 0, 0, 1494, 1499, 1, 0, 0, 0, 1495, 1493, 1, 0, 0, 0, 1496, 1500, 3, 335, 167, 0, 1497, 1500, 5, 58, 0, 0, 1498, 1500, 3, 341, 170, 0, 1499, 1496, 1, 0, 0, 0, 1499, 1497, 1, 0, 0, 0, 1499, 1498, 1, 0, 0, 0, 1500, 1502, 1, 0, 0, 0, 1501, 1493, 1, 0, 0, 0, 1501, 1502, 1, 0, 0, 0, 1502, 340, 1, 0, 0, 0, 1503, 1506, 3, 343, 171, 0, 1504, 1506, 3, 347, 173, 0, 1505, 1503, 1, 0, 0, 0, 1505, 1504, 1, 0, 0, 0, 1506, 342, 1, 0, 0, 0, 1507, 1508, 5, 37, 0, 0, 1508, 1509, 3, 345, 172, 0, 1509, 1510, 3, 345, 172, 0, 1510, 344, 1, 0, 0, 0, 1511, 1514, 3, 351, 175, 0, 1512, 1514, 7, 11, 0, 0, 1513, 1511, 1, 0, 0, 0, 1513, 1512, 1, 0, 0, 0, 1514, 346, 1, 0, 0, 0, 1515, 1516, 5, 92, 0, 0, 1516, 1517, 7, 12, 0, 0, 1517, 348, 1, 0, 0, 0, 1518, 1519, 7, 13, 0, 0, 1519, 350, 1, 0, 0, 0, 1520, 1521, 2, 48, 57, 0, 1521, 352, 1, 0, 0, 0, 1522, 1524, 7, 14, 0, 0, 1523, 1522, 1, 0, 0, 0, 1524, 1525, 1, 0, 0, 0, 1525, 1523, 1, 0, 0, 0, 1525, 1526, 1, 0, 0, 0, 1526, 1527, 1, 0, 0, 0, 1527, 1528, 6, 176, 0, 0, 1528, 354, 1, 0, 0, 0, 1529, 1533, 5, 35, 0, 0, 1530, 1532, 8, 15, 0, 0, 1531, 1530, 1, 0, 0, 0, 1532, 1535, 1, 0, 0, 0, 1533, 1531, 1, 0, 0, 0, 1533, 1534, 1, 0, 0, 0, 1534, 1536, 1, 0, 0, 0, 1535, 1533, 1, 0, 0, 0, 1536, 1537, 6, 177, 0, 0, 1537, 356, 1, 0, 0, 0, 1538, 1539, 7, 16, 0, 0, 1539, 358, 1, 0, 0, 0, 1540, 1541, 7, 17, 0, 0, 1541, 360, 1, 0, 0, 0, 1542, 1543, 7, 18, 0, 0, 1543, 362, 1, 0, 0, 0, 1544, 1545, 7, 19, 0, 0, 1545, 364, 1, 0, 0, 0, 1546, 1547, 7, 2, 0, 0, 1547, 366, 1, 0, 0, 0, 1548, 1549, 7, 20, 0, 0, 1549, 368, 1, 0, 0, 0, 1550, 1551, 7, 21, 0, 0, 1551, 370, 1, 0, 0, 0, 1552, 1553, 7, 22, 0, 0, 1553, 372, 1, 0, 0, 0, 1554, 1555, 7, 23, 0, 0, 1555, 374, 1, 0, 0, 0, 1556, 1557, 7, 24, 0, 0, 1557, 376, 1, 0, 0, 0, 1558, 1559, 7, 25, 0, 0, 1559, 378, 1, 0, 0, 0, 1560, 1561, 7, 26, 0, 0, 1561, 380, 1, 0, 0, 0, 1562, 1563, 7, 27, 0, 0, 1563, 382, 1, 0, 0, 0, 1564, 1565, 7, 28, 0, 0, 1565, 384, 1, 0, 0, 0, 1566, 1567, 7, 29, 0, 0, 1567, 386, 1, 0, 0, 0, 1568, 1569, 7, 30, 0, 0, 1569, 388, 1, 0, 0, 0, 1570, 1571, 7, 31, 0, 0, 1571, 390, 1, 0, 0, 0, 1572, 1573, 7, 32, 0, 0, 1573, 392, 1, 0, 0, 0, 1574, 1575, 7, 33, 0, 0, 1575, 394, 1, 0, 0, 0, 1576, 1577, 7, 34, 0, 0, 1577, 396, 1, 0, 0, 0, 1578, 1579, 7, 35, 0, 0, 1579, 398, 1, 0, 0, 0, 1580, 1581, 7, 36, 0, 0, 1581, 400, 1, 0, 0, 0, 1582, 1583, 7, 37, 0, 0, 1583, 402, 1, 0, 0, 0, 1584, 1585, 7, 38, 0, 0, 1585, 404, 1, 0, 0, 0, 1586, 1587, 7, 39, 0, 0, 1587, 406, 1, 0, 0, 0, 1588, 1589, 7, 40, 0, 0, 1589, 408, 1, 0, 0, 0, 54, 0, 567, 600, 616, 625, 1232, 1238, 1250, 1254, 1256, 1260, 1272, 1277, 1279, 1283, 1292, 1297, 1304, 1309, 1315, 1324, 1331, 1335, 1357, 1362, 1367, 1369, 1377, 1379, 1391, 1395, 1399, 1413, 1417, 1421, 1435, 1444, 1451, 1455, 1460, 1462, 1469, 1474, 1476, 1480, 1486, 1491, 1493, 1499, 1501, 1505, 1513, 1525, 1533, 1, 6, 0, 0] \ No newline at end of file +[4, 0, 176, 1598, 6, -1, 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, 2, 157, 7, 157, 2, 158, 7, 158, 2, 159, 7, 159, 2, 160, 7, 160, 2, 161, 7, 161, 2, 162, 7, 162, 2, 163, 7, 163, 2, 164, 7, 164, 2, 165, 7, 165, 2, 166, 7, 166, 2, 167, 7, 167, 2, 168, 7, 168, 2, 169, 7, 169, 2, 170, 7, 170, 2, 171, 7, 171, 2, 172, 7, 172, 2, 173, 7, 173, 2, 174, 7, 174, 2, 175, 7, 175, 2, 176, 7, 176, 2, 177, 7, 177, 2, 178, 7, 178, 2, 179, 7, 179, 2, 180, 7, 180, 2, 181, 7, 181, 2, 182, 7, 182, 2, 183, 7, 183, 2, 184, 7, 184, 2, 185, 7, 185, 2, 186, 7, 186, 2, 187, 7, 187, 2, 188, 7, 188, 2, 189, 7, 189, 2, 190, 7, 190, 2, 191, 7, 191, 2, 192, 7, 192, 2, 193, 7, 193, 2, 194, 7, 194, 2, 195, 7, 195, 2, 196, 7, 196, 2, 197, 7, 197, 2, 198, 7, 198, 2, 199, 7, 199, 2, 200, 7, 200, 2, 201, 7, 201, 2, 202, 7, 202, 2, 203, 7, 203, 2, 204, 7, 204, 1, 0, 1, 0, 1, 1, 1, 1, 1, 2, 1, 2, 1, 3, 1, 3, 1, 4, 1, 4, 1, 5, 1, 5, 1, 6, 1, 6, 1, 7, 1, 7, 1, 8, 1, 8, 1, 9, 1, 9, 1, 10, 1, 10, 1, 11, 1, 11, 1, 12, 1, 12, 1, 13, 1, 13, 1, 14, 1, 14, 1, 15, 1, 15, 1, 16, 1, 16, 1, 17, 1, 17, 1, 17, 1, 18, 1, 18, 1, 18, 1, 19, 1, 19, 1, 20, 1, 20, 1, 20, 1, 21, 1, 21, 1, 22, 1, 22, 1, 23, 1, 23, 1, 23, 1, 24, 1, 24, 1, 24, 1, 25, 1, 25, 1, 26, 1, 26, 1, 26, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 1, 28, 1, 29, 1, 29, 1, 29, 1, 29, 1, 29, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 34, 1, 34, 1, 34, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 1, 35, 1, 36, 1, 36, 1, 36, 1, 36, 1, 36, 1, 36, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 38, 1, 38, 1, 38, 1, 38, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 1, 40, 1, 41, 1, 41, 1, 41, 1, 41, 1, 41, 1, 41, 4, 41, 568, 8, 41, 11, 41, 12, 41, 569, 1, 41, 1, 41, 1, 41, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 4, 44, 601, 8, 44, 11, 44, 12, 44, 602, 1, 44, 1, 44, 1, 44, 1, 45, 1, 45, 1, 45, 1, 45, 1, 45, 1, 45, 1, 45, 1, 45, 1, 45, 4, 45, 617, 8, 45, 11, 45, 12, 45, 618, 1, 45, 1, 45, 1, 45, 1, 45, 1, 45, 4, 45, 626, 8, 45, 11, 45, 12, 45, 627, 1, 45, 1, 45, 1, 45, 1, 46, 1, 46, 1, 46, 1, 46, 1, 47, 1, 47, 1, 47, 1, 47, 1, 47, 1, 48, 1, 48, 1, 48, 1, 48, 1, 48, 1, 48, 1, 49, 1, 49, 1, 49, 1, 49, 1, 49, 1, 49, 1, 49, 1, 50, 1, 50, 1, 50, 1, 50, 1, 50, 1, 50, 1, 50, 1, 50, 1, 50, 1, 50, 1, 51, 1, 51, 1, 51, 1, 51, 1, 51, 1, 51, 1, 51, 1, 52, 1, 52, 1, 52, 1, 52, 1, 52, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 54, 1, 54, 1, 54, 1, 54, 1, 54, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 55, 1, 56, 1, 56, 1, 56, 1, 56, 1, 56, 1, 57, 1, 57, 1, 57, 1, 57, 1, 57, 1, 57, 1, 57, 1, 58, 1, 58, 1, 58, 1, 58, 1, 59, 1, 59, 1, 59, 1, 60, 1, 60, 1, 60, 1, 60, 1, 60, 1, 61, 1, 61, 1, 61, 1, 61, 1, 61, 1, 62, 1, 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, 64, 1, 64, 1, 64, 1, 64, 1, 65, 1, 65, 1, 65, 1, 65, 1, 65, 1, 66, 1, 66, 1, 66, 1, 66, 1, 66, 1, 66, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 67, 1, 68, 1, 68, 1, 68, 1, 68, 1, 68, 1, 68, 1, 69, 1, 69, 1, 69, 1, 69, 1, 70, 1, 70, 1, 70, 1, 70, 1, 70, 1, 70, 1, 70, 1, 70, 1, 70, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 71, 1, 72, 1, 72, 1, 72, 1, 72, 1, 72, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 73, 1, 74, 1, 74, 1, 74, 1, 74, 1, 74, 1, 74, 1, 75, 1, 75, 1, 75, 1, 75, 1, 75, 1, 75, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 76, 1, 77, 1, 77, 1, 77, 1, 77, 1, 78, 1, 78, 1, 78, 1, 79, 1, 79, 1, 79, 1, 79, 1, 80, 1, 80, 1, 80, 1, 80, 1, 80, 1, 81, 1, 81, 1, 81, 1, 81, 1, 81, 1, 81, 1, 81, 1, 81, 1, 81, 1, 81, 1, 81, 1, 81, 1, 82, 1, 82, 1, 82, 1, 82, 1, 82, 1, 82, 1, 82, 1, 82, 1, 82, 1, 83, 1, 83, 1, 83, 1, 83, 1, 83, 1, 83, 1, 84, 1, 84, 1, 84, 1, 84, 1, 85, 1, 85, 1, 85, 1, 85, 1, 86, 1, 86, 1, 86, 1, 86, 1, 86, 1, 86, 1, 87, 1, 87, 1, 87, 1, 87, 1, 87, 1, 88, 1, 88, 1, 88, 1, 88, 1, 89, 1, 89, 1, 89, 1, 89, 1, 89, 1, 90, 1, 90, 1, 90, 1, 90, 1, 90, 1, 90, 1, 91, 1, 91, 1, 91, 1, 91, 1, 91, 1, 91, 1, 92, 1, 92, 1, 92, 1, 92, 1, 92, 1, 92, 1, 92, 1, 93, 1, 93, 1, 93, 1, 93, 1, 93, 1, 93, 1, 93, 1, 94, 1, 94, 1, 94, 1, 94, 1, 94, 1, 94, 1, 95, 1, 95, 1, 95, 1, 95, 1, 95, 1, 95, 1, 96, 1, 96, 1, 96, 1, 96, 1, 96, 1, 96, 1, 96, 1, 96, 1, 96, 1, 96, 1, 96, 1, 96, 1, 96, 1, 96, 1, 96, 1, 97, 1, 97, 1, 97, 1, 97, 1, 98, 1, 98, 1, 98, 1, 98, 1, 98, 1, 98, 1, 98, 1, 98, 1, 98, 1, 99, 1, 99, 1, 99, 1, 99, 1, 99, 1, 99, 1, 99, 1, 99, 1, 99, 1, 99, 1, 100, 1, 100, 1, 100, 1, 100, 1, 100, 1, 100, 1, 100, 1, 100, 1, 101, 1, 101, 1, 101, 1, 101, 1, 101, 1, 101, 1, 101, 1, 101, 1, 101, 1, 101, 1, 102, 1, 102, 1, 102, 1, 102, 1, 102, 1, 102, 1, 102, 1, 102, 1, 102, 1, 103, 1, 103, 1, 103, 1, 103, 1, 103, 1, 104, 1, 104, 1, 104, 1, 104, 1, 104, 1, 104, 1, 105, 1, 105, 1, 105, 1, 105, 1, 106, 1, 106, 1, 106, 1, 106, 1, 106, 1, 106, 1, 107, 1, 107, 1, 107, 1, 107, 1, 107, 1, 107, 1, 107, 1, 107, 1, 108, 1, 108, 1, 108, 1, 108, 1, 108, 1, 108, 1, 108, 1, 108, 1, 109, 1, 109, 1, 109, 1, 109, 1, 109, 1, 109, 1, 109, 1, 109, 1, 109, 1, 110, 1, 110, 1, 110, 1, 111, 1, 111, 1, 111, 1, 111, 1, 112, 1, 112, 1, 112, 1, 112, 1, 112, 1, 113, 1, 113, 1, 113, 1, 113, 1, 113, 1, 113, 1, 113, 1, 113, 1, 114, 1, 114, 1, 114, 1, 114, 1, 114, 1, 115, 1, 115, 1, 115, 1, 115, 1, 115, 1, 115, 1, 115, 1, 116, 1, 116, 1, 116, 1, 116, 1, 116, 1, 116, 1, 116, 1, 117, 1, 117, 1, 117, 1, 117, 1, 117, 1, 117, 1, 117, 1, 118, 1, 118, 1, 118, 1, 118, 1, 119, 1, 119, 1, 119, 1, 119, 1, 119, 1, 119, 1, 119, 1, 119, 1, 119, 1, 120, 1, 120, 1, 120, 1, 121, 1, 121, 1, 121, 1, 121, 1, 121, 1, 121, 1, 121, 1, 121, 1, 122, 1, 122, 1, 122, 1, 122, 1, 122, 1, 122, 1, 123, 1, 123, 1, 123, 1, 123, 1, 123, 1, 123, 1, 123, 1, 123, 1, 123, 1, 124, 1, 124, 1, 124, 1, 124, 1, 124, 1, 124, 1, 125, 1, 125, 1, 125, 1, 125, 1, 125, 1, 125, 1, 126, 1, 126, 1, 126, 1, 126, 1, 126, 1, 126, 1, 126, 1, 126, 1, 127, 1, 127, 1, 127, 1, 127, 1, 127, 1, 127, 1, 127, 1, 127, 1, 127, 1, 127, 1, 128, 1, 128, 1, 128, 1, 128, 1, 128, 1, 128, 1, 128, 1, 128, 1, 128, 1, 128, 1, 129, 1, 129, 1, 129, 1, 129, 1, 129, 1, 129, 1, 130, 1, 130, 1, 130, 1, 130, 1, 130, 1, 130, 1, 130, 1, 131, 1, 131, 1, 131, 1, 131, 1, 131, 1, 131, 1, 131, 1, 131, 1, 132, 1, 132, 1, 132, 1, 132, 1, 132, 1, 132, 1, 132, 1, 133, 1, 133, 1, 133, 1, 133, 1, 133, 1, 133, 1, 134, 1, 134, 1, 134, 1, 134, 1, 135, 1, 135, 1, 135, 1, 135, 1, 136, 1, 136, 1, 136, 1, 136, 1, 137, 1, 137, 1, 137, 1, 137, 1, 138, 1, 138, 1, 138, 1, 138, 1, 138, 1, 138, 1, 139, 1, 139, 1, 139, 1, 139, 1, 139, 1, 139, 1, 139, 1, 140, 1, 140, 1, 140, 1, 140, 1, 140, 1, 140, 1, 140, 1, 140, 1, 140, 1, 140, 1, 141, 1, 141, 5, 141, 1239, 8, 141, 10, 141, 12, 141, 1242, 9, 141, 1, 141, 1, 141, 1, 142, 3, 142, 1247, 8, 142, 1, 142, 1, 142, 1, 143, 1, 143, 1, 143, 1, 144, 1, 144, 1, 144, 1, 144, 1, 144, 3, 144, 1259, 8, 144, 1, 144, 1, 144, 5, 144, 1263, 8, 144, 10, 144, 12, 144, 1266, 9, 144, 1, 144, 3, 144, 1269, 8, 144, 1, 145, 1, 145, 1, 145, 1, 146, 1, 146, 1, 146, 1, 147, 1, 147, 4, 147, 1279, 8, 147, 11, 147, 12, 147, 1280, 1, 147, 1, 147, 1, 147, 4, 147, 1286, 8, 147, 11, 147, 12, 147, 1287, 5, 147, 1290, 8, 147, 10, 147, 12, 147, 1293, 9, 147, 1, 148, 1, 148, 1, 148, 1, 149, 4, 149, 1299, 8, 149, 11, 149, 12, 149, 1300, 1, 150, 5, 150, 1304, 8, 150, 10, 150, 12, 150, 1307, 9, 150, 1, 150, 1, 150, 4, 150, 1311, 8, 150, 11, 150, 12, 150, 1312, 1, 151, 4, 151, 1316, 8, 151, 11, 151, 12, 151, 1317, 1, 151, 1, 151, 5, 151, 1322, 8, 151, 10, 151, 12, 151, 1325, 9, 151, 1, 151, 1, 151, 1, 151, 1, 151, 4, 151, 1331, 8, 151, 11, 151, 12, 151, 1332, 1, 151, 1, 151, 1, 151, 4, 151, 1338, 8, 151, 11, 151, 12, 151, 1339, 1, 151, 1, 151, 3, 151, 1344, 8, 151, 1, 152, 1, 152, 1, 152, 1, 153, 1, 153, 1, 153, 1, 154, 1, 154, 1, 154, 1, 155, 1, 155, 1, 155, 1, 156, 1, 156, 1, 156, 1, 157, 1, 157, 1, 157, 1, 158, 1, 158, 3, 158, 1366, 8, 158, 1, 158, 4, 158, 1369, 8, 158, 11, 158, 12, 158, 1370, 1, 159, 1, 159, 1, 159, 5, 159, 1376, 8, 159, 10, 159, 12, 159, 1379, 9, 159, 1, 159, 1, 159, 1, 160, 1, 160, 1, 160, 5, 160, 1386, 8, 160, 10, 160, 12, 160, 1389, 9, 160, 1, 160, 1, 160, 1, 161, 1, 161, 1, 161, 1, 161, 1, 161, 1, 161, 1, 161, 3, 161, 1400, 8, 161, 1, 161, 1, 161, 3, 161, 1404, 8, 161, 5, 161, 1406, 8, 161, 10, 161, 12, 161, 1409, 9, 161, 1, 161, 1, 161, 1, 161, 1, 161, 1, 162, 1, 162, 1, 162, 1, 162, 1, 162, 1, 162, 1, 162, 3, 162, 1422, 8, 162, 1, 162, 1, 162, 3, 162, 1426, 8, 162, 5, 162, 1428, 8, 162, 10, 162, 12, 162, 1431, 9, 162, 1, 162, 1, 162, 1, 162, 1, 162, 1, 163, 1, 163, 1, 163, 1, 164, 1, 164, 5, 164, 1442, 8, 164, 10, 164, 12, 164, 1445, 9, 164, 1, 164, 1, 164, 1, 165, 1, 165, 5, 165, 1451, 8, 165, 10, 165, 12, 165, 1454, 9, 165, 1, 165, 1, 165, 1, 166, 1, 166, 3, 166, 1460, 8, 166, 1, 167, 1, 167, 3, 167, 1464, 8, 167, 1, 167, 1, 167, 1, 167, 5, 167, 1469, 8, 167, 10, 167, 12, 167, 1472, 9, 167, 1, 168, 1, 168, 1, 168, 1, 168, 3, 168, 1478, 8, 168, 1, 169, 1, 169, 1, 169, 5, 169, 1483, 8, 169, 10, 169, 12, 169, 1486, 9, 169, 1, 169, 3, 169, 1489, 8, 169, 1, 170, 1, 170, 1, 170, 1, 170, 3, 170, 1495, 8, 170, 1, 170, 1, 170, 1, 170, 5, 170, 1500, 8, 170, 10, 170, 12, 170, 1503, 9, 170, 1, 170, 1, 170, 1, 170, 3, 170, 1508, 8, 170, 3, 170, 1510, 8, 170, 1, 171, 1, 171, 3, 171, 1514, 8, 171, 1, 172, 1, 172, 1, 172, 1, 172, 1, 173, 1, 173, 3, 173, 1522, 8, 173, 1, 174, 1, 174, 1, 174, 1, 175, 1, 175, 1, 176, 1, 176, 1, 177, 4, 177, 1532, 8, 177, 11, 177, 12, 177, 1533, 1, 177, 1, 177, 1, 178, 1, 178, 5, 178, 1540, 8, 178, 10, 178, 12, 178, 1543, 9, 178, 1, 178, 1, 178, 1, 179, 1, 179, 1, 180, 1, 180, 1, 181, 1, 181, 1, 182, 1, 182, 1, 183, 1, 183, 1, 184, 1, 184, 1, 185, 1, 185, 1, 186, 1, 186, 1, 187, 1, 187, 1, 188, 1, 188, 1, 189, 1, 189, 1, 190, 1, 190, 1, 191, 1, 191, 1, 192, 1, 192, 1, 193, 1, 193, 1, 194, 1, 194, 1, 195, 1, 195, 1, 196, 1, 196, 1, 197, 1, 197, 1, 198, 1, 198, 1, 199, 1, 199, 1, 200, 1, 200, 1, 201, 1, 201, 1, 202, 1, 202, 1, 203, 1, 203, 1, 204, 1, 204, 0, 0, 205, 1, 1, 3, 2, 5, 3, 7, 4, 9, 5, 11, 6, 13, 7, 15, 8, 17, 9, 19, 10, 21, 11, 23, 12, 25, 13, 27, 14, 29, 15, 31, 16, 33, 17, 35, 18, 37, 19, 39, 20, 41, 21, 43, 22, 45, 23, 47, 24, 49, 25, 51, 26, 53, 27, 55, 28, 57, 29, 59, 30, 61, 31, 63, 32, 65, 33, 67, 34, 69, 35, 71, 36, 73, 37, 75, 38, 77, 39, 79, 40, 81, 41, 83, 42, 85, 43, 87, 44, 89, 45, 91, 46, 93, 47, 95, 48, 97, 49, 99, 50, 101, 51, 103, 52, 105, 53, 107, 54, 109, 55, 111, 56, 113, 57, 115, 58, 117, 59, 119, 60, 121, 61, 123, 62, 125, 63, 127, 64, 129, 65, 131, 66, 133, 67, 135, 68, 137, 69, 139, 70, 141, 71, 143, 72, 145, 73, 147, 74, 149, 75, 151, 76, 153, 77, 155, 78, 157, 79, 159, 80, 161, 81, 163, 82, 165, 83, 167, 84, 169, 85, 171, 86, 173, 87, 175, 88, 177, 89, 179, 90, 181, 91, 183, 92, 185, 93, 187, 94, 189, 95, 191, 96, 193, 97, 195, 98, 197, 99, 199, 100, 201, 101, 203, 102, 205, 103, 207, 104, 209, 105, 211, 106, 213, 107, 215, 108, 217, 109, 219, 110, 221, 111, 223, 112, 225, 113, 227, 114, 229, 115, 231, 116, 233, 117, 235, 118, 237, 119, 239, 120, 241, 121, 243, 122, 245, 123, 247, 124, 249, 125, 251, 126, 253, 127, 255, 128, 257, 129, 259, 130, 261, 131, 263, 132, 265, 133, 267, 134, 269, 135, 271, 136, 273, 137, 275, 138, 277, 139, 279, 140, 281, 141, 283, 142, 285, 143, 287, 144, 289, 145, 291, 146, 293, 147, 295, 148, 297, 149, 299, 150, 301, 151, 303, 152, 305, 153, 307, 154, 309, 155, 311, 156, 313, 157, 315, 158, 317, 159, 319, 160, 321, 161, 323, 162, 325, 163, 327, 164, 329, 165, 331, 166, 333, 167, 335, 168, 337, 0, 339, 169, 341, 170, 343, 171, 345, 172, 347, 173, 349, 174, 351, 0, 353, 0, 355, 175, 357, 176, 359, 0, 361, 0, 363, 0, 365, 0, 367, 0, 369, 0, 371, 0, 373, 0, 375, 0, 377, 0, 379, 0, 381, 0, 383, 0, 385, 0, 387, 0, 389, 0, 391, 0, 393, 0, 395, 0, 397, 0, 399, 0, 401, 0, 403, 0, 405, 0, 407, 0, 409, 0, 1, 0, 41, 8, 0, 0, 32, 34, 34, 60, 60, 62, 62, 92, 92, 94, 94, 96, 96, 123, 125, 2, 0, 65, 90, 97, 122, 2, 0, 69, 69, 101, 101, 2, 0, 43, 43, 45, 45, 4, 0, 10, 10, 13, 13, 39, 39, 92, 92, 4, 0, 10, 10, 13, 13, 34, 34, 92, 92, 2, 0, 39, 39, 92, 92, 2, 0, 34, 34, 92, 92, 8, 0, 34, 34, 39, 39, 92, 92, 98, 98, 102, 102, 110, 110, 114, 114, 116, 116, 3, 0, 183, 183, 768, 879, 8255, 8256, 2, 0, 46, 46, 58, 58, 2, 0, 65, 70, 97, 102, 7, 0, 33, 33, 35, 47, 59, 59, 61, 61, 63, 64, 95, 95, 126, 126, 13, 0, 65, 90, 97, 122, 192, 214, 216, 246, 248, 767, 880, 893, 895, 8191, 8204, 8205, 8304, 8591, 11264, 12271, 12289, 55295, 63744, 64975, 65008, 65533, 3, 0, 9, 10, 13, 13, 32, 32, 2, 0, 10, 10, 13, 13, 2, 0, 65, 65, 97, 97, 2, 0, 66, 66, 98, 98, 2, 0, 67, 67, 99, 99, 2, 0, 68, 68, 100, 100, 2, 0, 70, 70, 102, 102, 2, 0, 71, 71, 103, 103, 2, 0, 72, 72, 104, 104, 2, 0, 73, 73, 105, 105, 2, 0, 74, 74, 106, 106, 2, 0, 75, 75, 107, 107, 2, 0, 76, 76, 108, 108, 2, 0, 77, 77, 109, 109, 2, 0, 78, 78, 110, 110, 2, 0, 79, 79, 111, 111, 2, 0, 80, 80, 112, 112, 2, 0, 81, 81, 113, 113, 2, 0, 82, 82, 114, 114, 2, 0, 83, 83, 115, 115, 2, 0, 84, 84, 116, 116, 2, 0, 85, 85, 117, 117, 2, 0, 86, 86, 118, 118, 2, 0, 87, 87, 119, 119, 2, 0, 88, 88, 120, 120, 2, 0, 89, 89, 121, 121, 2, 0, 90, 90, 122, 122, 1631, 0, 1, 1, 0, 0, 0, 0, 3, 1, 0, 0, 0, 0, 5, 1, 0, 0, 0, 0, 7, 1, 0, 0, 0, 0, 9, 1, 0, 0, 0, 0, 11, 1, 0, 0, 0, 0, 13, 1, 0, 0, 0, 0, 15, 1, 0, 0, 0, 0, 17, 1, 0, 0, 0, 0, 19, 1, 0, 0, 0, 0, 21, 1, 0, 0, 0, 0, 23, 1, 0, 0, 0, 0, 25, 1, 0, 0, 0, 0, 27, 1, 0, 0, 0, 0, 29, 1, 0, 0, 0, 0, 31, 1, 0, 0, 0, 0, 33, 1, 0, 0, 0, 0, 35, 1, 0, 0, 0, 0, 37, 1, 0, 0, 0, 0, 39, 1, 0, 0, 0, 0, 41, 1, 0, 0, 0, 0, 43, 1, 0, 0, 0, 0, 45, 1, 0, 0, 0, 0, 47, 1, 0, 0, 0, 0, 49, 1, 0, 0, 0, 0, 51, 1, 0, 0, 0, 0, 53, 1, 0, 0, 0, 0, 55, 1, 0, 0, 0, 0, 57, 1, 0, 0, 0, 0, 59, 1, 0, 0, 0, 0, 61, 1, 0, 0, 0, 0, 63, 1, 0, 0, 0, 0, 65, 1, 0, 0, 0, 0, 67, 1, 0, 0, 0, 0, 69, 1, 0, 0, 0, 0, 71, 1, 0, 0, 0, 0, 73, 1, 0, 0, 0, 0, 75, 1, 0, 0, 0, 0, 77, 1, 0, 0, 0, 0, 79, 1, 0, 0, 0, 0, 81, 1, 0, 0, 0, 0, 83, 1, 0, 0, 0, 0, 85, 1, 0, 0, 0, 0, 87, 1, 0, 0, 0, 0, 89, 1, 0, 0, 0, 0, 91, 1, 0, 0, 0, 0, 93, 1, 0, 0, 0, 0, 95, 1, 0, 0, 0, 0, 97, 1, 0, 0, 0, 0, 99, 1, 0, 0, 0, 0, 101, 1, 0, 0, 0, 0, 103, 1, 0, 0, 0, 0, 105, 1, 0, 0, 0, 0, 107, 1, 0, 0, 0, 0, 109, 1, 0, 0, 0, 0, 111, 1, 0, 0, 0, 0, 113, 1, 0, 0, 0, 0, 115, 1, 0, 0, 0, 0, 117, 1, 0, 0, 0, 0, 119, 1, 0, 0, 0, 0, 121, 1, 0, 0, 0, 0, 123, 1, 0, 0, 0, 0, 125, 1, 0, 0, 0, 0, 127, 1, 0, 0, 0, 0, 129, 1, 0, 0, 0, 0, 131, 1, 0, 0, 0, 0, 133, 1, 0, 0, 0, 0, 135, 1, 0, 0, 0, 0, 137, 1, 0, 0, 0, 0, 139, 1, 0, 0, 0, 0, 141, 1, 0, 0, 0, 0, 143, 1, 0, 0, 0, 0, 145, 1, 0, 0, 0, 0, 147, 1, 0, 0, 0, 0, 149, 1, 0, 0, 0, 0, 151, 1, 0, 0, 0, 0, 153, 1, 0, 0, 0, 0, 155, 1, 0, 0, 0, 0, 157, 1, 0, 0, 0, 0, 159, 1, 0, 0, 0, 0, 161, 1, 0, 0, 0, 0, 163, 1, 0, 0, 0, 0, 165, 1, 0, 0, 0, 0, 167, 1, 0, 0, 0, 0, 169, 1, 0, 0, 0, 0, 171, 1, 0, 0, 0, 0, 173, 1, 0, 0, 0, 0, 175, 1, 0, 0, 0, 0, 177, 1, 0, 0, 0, 0, 179, 1, 0, 0, 0, 0, 181, 1, 0, 0, 0, 0, 183, 1, 0, 0, 0, 0, 185, 1, 0, 0, 0, 0, 187, 1, 0, 0, 0, 0, 189, 1, 0, 0, 0, 0, 191, 1, 0, 0, 0, 0, 193, 1, 0, 0, 0, 0, 195, 1, 0, 0, 0, 0, 197, 1, 0, 0, 0, 0, 199, 1, 0, 0, 0, 0, 201, 1, 0, 0, 0, 0, 203, 1, 0, 0, 0, 0, 205, 1, 0, 0, 0, 0, 207, 1, 0, 0, 0, 0, 209, 1, 0, 0, 0, 0, 211, 1, 0, 0, 0, 0, 213, 1, 0, 0, 0, 0, 215, 1, 0, 0, 0, 0, 217, 1, 0, 0, 0, 0, 219, 1, 0, 0, 0, 0, 221, 1, 0, 0, 0, 0, 223, 1, 0, 0, 0, 0, 225, 1, 0, 0, 0, 0, 227, 1, 0, 0, 0, 0, 229, 1, 0, 0, 0, 0, 231, 1, 0, 0, 0, 0, 233, 1, 0, 0, 0, 0, 235, 1, 0, 0, 0, 0, 237, 1, 0, 0, 0, 0, 239, 1, 0, 0, 0, 0, 241, 1, 0, 0, 0, 0, 243, 1, 0, 0, 0, 0, 245, 1, 0, 0, 0, 0, 247, 1, 0, 0, 0, 0, 249, 1, 0, 0, 0, 0, 251, 1, 0, 0, 0, 0, 253, 1, 0, 0, 0, 0, 255, 1, 0, 0, 0, 0, 257, 1, 0, 0, 0, 0, 259, 1, 0, 0, 0, 0, 261, 1, 0, 0, 0, 0, 263, 1, 0, 0, 0, 0, 265, 1, 0, 0, 0, 0, 267, 1, 0, 0, 0, 0, 269, 1, 0, 0, 0, 0, 271, 1, 0, 0, 0, 0, 273, 1, 0, 0, 0, 0, 275, 1, 0, 0, 0, 0, 277, 1, 0, 0, 0, 0, 279, 1, 0, 0, 0, 0, 281, 1, 0, 0, 0, 0, 283, 1, 0, 0, 0, 0, 285, 1, 0, 0, 0, 0, 287, 1, 0, 0, 0, 0, 289, 1, 0, 0, 0, 0, 291, 1, 0, 0, 0, 0, 293, 1, 0, 0, 0, 0, 295, 1, 0, 0, 0, 0, 297, 1, 0, 0, 0, 0, 299, 1, 0, 0, 0, 0, 301, 1, 0, 0, 0, 0, 303, 1, 0, 0, 0, 0, 305, 1, 0, 0, 0, 0, 307, 1, 0, 0, 0, 0, 309, 1, 0, 0, 0, 0, 311, 1, 0, 0, 0, 0, 313, 1, 0, 0, 0, 0, 315, 1, 0, 0, 0, 0, 317, 1, 0, 0, 0, 0, 319, 1, 0, 0, 0, 0, 321, 1, 0, 0, 0, 0, 323, 1, 0, 0, 0, 0, 325, 1, 0, 0, 0, 0, 327, 1, 0, 0, 0, 0, 329, 1, 0, 0, 0, 0, 331, 1, 0, 0, 0, 0, 333, 1, 0, 0, 0, 0, 335, 1, 0, 0, 0, 0, 339, 1, 0, 0, 0, 0, 341, 1, 0, 0, 0, 0, 343, 1, 0, 0, 0, 0, 345, 1, 0, 0, 0, 0, 347, 1, 0, 0, 0, 0, 349, 1, 0, 0, 0, 0, 355, 1, 0, 0, 0, 0, 357, 1, 0, 0, 0, 1, 411, 1, 0, 0, 0, 3, 413, 1, 0, 0, 0, 5, 415, 1, 0, 0, 0, 7, 417, 1, 0, 0, 0, 9, 419, 1, 0, 0, 0, 11, 421, 1, 0, 0, 0, 13, 423, 1, 0, 0, 0, 15, 425, 1, 0, 0, 0, 17, 427, 1, 0, 0, 0, 19, 429, 1, 0, 0, 0, 21, 431, 1, 0, 0, 0, 23, 433, 1, 0, 0, 0, 25, 435, 1, 0, 0, 0, 27, 437, 1, 0, 0, 0, 29, 439, 1, 0, 0, 0, 31, 441, 1, 0, 0, 0, 33, 443, 1, 0, 0, 0, 35, 445, 1, 0, 0, 0, 37, 448, 1, 0, 0, 0, 39, 451, 1, 0, 0, 0, 41, 453, 1, 0, 0, 0, 43, 456, 1, 0, 0, 0, 45, 458, 1, 0, 0, 0, 47, 460, 1, 0, 0, 0, 49, 463, 1, 0, 0, 0, 51, 466, 1, 0, 0, 0, 53, 468, 1, 0, 0, 0, 55, 471, 1, 0, 0, 0, 57, 476, 1, 0, 0, 0, 59, 482, 1, 0, 0, 0, 61, 487, 1, 0, 0, 0, 63, 494, 1, 0, 0, 0, 65, 501, 1, 0, 0, 0, 67, 510, 1, 0, 0, 0, 69, 518, 1, 0, 0, 0, 71, 521, 1, 0, 0, 0, 73, 531, 1, 0, 0, 0, 75, 537, 1, 0, 0, 0, 77, 546, 1, 0, 0, 0, 79, 550, 1, 0, 0, 0, 81, 555, 1, 0, 0, 0, 83, 561, 1, 0, 0, 0, 85, 574, 1, 0, 0, 0, 87, 587, 1, 0, 0, 0, 89, 594, 1, 0, 0, 0, 91, 607, 1, 0, 0, 0, 93, 632, 1, 0, 0, 0, 95, 636, 1, 0, 0, 0, 97, 641, 1, 0, 0, 0, 99, 647, 1, 0, 0, 0, 101, 654, 1, 0, 0, 0, 103, 664, 1, 0, 0, 0, 105, 671, 1, 0, 0, 0, 107, 676, 1, 0, 0, 0, 109, 683, 1, 0, 0, 0, 111, 688, 1, 0, 0, 0, 113, 694, 1, 0, 0, 0, 115, 699, 1, 0, 0, 0, 117, 706, 1, 0, 0, 0, 119, 710, 1, 0, 0, 0, 121, 713, 1, 0, 0, 0, 123, 718, 1, 0, 0, 0, 125, 723, 1, 0, 0, 0, 127, 728, 1, 0, 0, 0, 129, 735, 1, 0, 0, 0, 131, 742, 1, 0, 0, 0, 133, 747, 1, 0, 0, 0, 135, 753, 1, 0, 0, 0, 137, 761, 1, 0, 0, 0, 139, 767, 1, 0, 0, 0, 141, 771, 1, 0, 0, 0, 143, 780, 1, 0, 0, 0, 145, 788, 1, 0, 0, 0, 147, 793, 1, 0, 0, 0, 149, 799, 1, 0, 0, 0, 151, 805, 1, 0, 0, 0, 153, 811, 1, 0, 0, 0, 155, 818, 1, 0, 0, 0, 157, 822, 1, 0, 0, 0, 159, 825, 1, 0, 0, 0, 161, 829, 1, 0, 0, 0, 163, 834, 1, 0, 0, 0, 165, 846, 1, 0, 0, 0, 167, 855, 1, 0, 0, 0, 169, 861, 1, 0, 0, 0, 171, 865, 1, 0, 0, 0, 173, 869, 1, 0, 0, 0, 175, 875, 1, 0, 0, 0, 177, 880, 1, 0, 0, 0, 179, 884, 1, 0, 0, 0, 181, 889, 1, 0, 0, 0, 183, 895, 1, 0, 0, 0, 185, 901, 1, 0, 0, 0, 187, 908, 1, 0, 0, 0, 189, 915, 1, 0, 0, 0, 191, 921, 1, 0, 0, 0, 193, 927, 1, 0, 0, 0, 195, 942, 1, 0, 0, 0, 197, 946, 1, 0, 0, 0, 199, 955, 1, 0, 0, 0, 201, 965, 1, 0, 0, 0, 203, 973, 1, 0, 0, 0, 205, 983, 1, 0, 0, 0, 207, 992, 1, 0, 0, 0, 209, 997, 1, 0, 0, 0, 211, 1003, 1, 0, 0, 0, 213, 1007, 1, 0, 0, 0, 215, 1013, 1, 0, 0, 0, 217, 1021, 1, 0, 0, 0, 219, 1029, 1, 0, 0, 0, 221, 1038, 1, 0, 0, 0, 223, 1041, 1, 0, 0, 0, 225, 1045, 1, 0, 0, 0, 227, 1050, 1, 0, 0, 0, 229, 1058, 1, 0, 0, 0, 231, 1063, 1, 0, 0, 0, 233, 1070, 1, 0, 0, 0, 235, 1077, 1, 0, 0, 0, 237, 1084, 1, 0, 0, 0, 239, 1088, 1, 0, 0, 0, 241, 1097, 1, 0, 0, 0, 243, 1100, 1, 0, 0, 0, 245, 1108, 1, 0, 0, 0, 247, 1114, 1, 0, 0, 0, 249, 1123, 1, 0, 0, 0, 251, 1129, 1, 0, 0, 0, 253, 1135, 1, 0, 0, 0, 255, 1143, 1, 0, 0, 0, 257, 1153, 1, 0, 0, 0, 259, 1163, 1, 0, 0, 0, 261, 1169, 1, 0, 0, 0, 263, 1176, 1, 0, 0, 0, 265, 1184, 1, 0, 0, 0, 267, 1191, 1, 0, 0, 0, 269, 1197, 1, 0, 0, 0, 271, 1201, 1, 0, 0, 0, 273, 1205, 1, 0, 0, 0, 275, 1209, 1, 0, 0, 0, 277, 1213, 1, 0, 0, 0, 279, 1219, 1, 0, 0, 0, 281, 1226, 1, 0, 0, 0, 283, 1236, 1, 0, 0, 0, 285, 1246, 1, 0, 0, 0, 287, 1250, 1, 0, 0, 0, 289, 1253, 1, 0, 0, 0, 291, 1270, 1, 0, 0, 0, 293, 1273, 1, 0, 0, 0, 295, 1276, 1, 0, 0, 0, 297, 1294, 1, 0, 0, 0, 299, 1298, 1, 0, 0, 0, 301, 1305, 1, 0, 0, 0, 303, 1343, 1, 0, 0, 0, 305, 1345, 1, 0, 0, 0, 307, 1348, 1, 0, 0, 0, 309, 1351, 1, 0, 0, 0, 311, 1354, 1, 0, 0, 0, 313, 1357, 1, 0, 0, 0, 315, 1360, 1, 0, 0, 0, 317, 1363, 1, 0, 0, 0, 319, 1372, 1, 0, 0, 0, 321, 1382, 1, 0, 0, 0, 323, 1392, 1, 0, 0, 0, 325, 1414, 1, 0, 0, 0, 327, 1436, 1, 0, 0, 0, 329, 1439, 1, 0, 0, 0, 331, 1448, 1, 0, 0, 0, 333, 1459, 1, 0, 0, 0, 335, 1463, 1, 0, 0, 0, 337, 1477, 1, 0, 0, 0, 339, 1479, 1, 0, 0, 0, 341, 1494, 1, 0, 0, 0, 343, 1513, 1, 0, 0, 0, 345, 1515, 1, 0, 0, 0, 347, 1521, 1, 0, 0, 0, 349, 1523, 1, 0, 0, 0, 351, 1526, 1, 0, 0, 0, 353, 1528, 1, 0, 0, 0, 355, 1531, 1, 0, 0, 0, 357, 1537, 1, 0, 0, 0, 359, 1546, 1, 0, 0, 0, 361, 1548, 1, 0, 0, 0, 363, 1550, 1, 0, 0, 0, 365, 1552, 1, 0, 0, 0, 367, 1554, 1, 0, 0, 0, 369, 1556, 1, 0, 0, 0, 371, 1558, 1, 0, 0, 0, 373, 1560, 1, 0, 0, 0, 375, 1562, 1, 0, 0, 0, 377, 1564, 1, 0, 0, 0, 379, 1566, 1, 0, 0, 0, 381, 1568, 1, 0, 0, 0, 383, 1570, 1, 0, 0, 0, 385, 1572, 1, 0, 0, 0, 387, 1574, 1, 0, 0, 0, 389, 1576, 1, 0, 0, 0, 391, 1578, 1, 0, 0, 0, 393, 1580, 1, 0, 0, 0, 395, 1582, 1, 0, 0, 0, 397, 1584, 1, 0, 0, 0, 399, 1586, 1, 0, 0, 0, 401, 1588, 1, 0, 0, 0, 403, 1590, 1, 0, 0, 0, 405, 1592, 1, 0, 0, 0, 407, 1594, 1, 0, 0, 0, 409, 1596, 1, 0, 0, 0, 411, 412, 5, 42, 0, 0, 412, 2, 1, 0, 0, 0, 413, 414, 5, 40, 0, 0, 414, 4, 1, 0, 0, 0, 415, 416, 5, 41, 0, 0, 416, 6, 1, 0, 0, 0, 417, 418, 5, 123, 0, 0, 418, 8, 1, 0, 0, 0, 419, 420, 5, 125, 0, 0, 420, 10, 1, 0, 0, 0, 421, 422, 5, 59, 0, 0, 422, 12, 1, 0, 0, 0, 423, 424, 5, 46, 0, 0, 424, 14, 1, 0, 0, 0, 425, 426, 5, 44, 0, 0, 426, 16, 1, 0, 0, 0, 427, 428, 5, 97, 0, 0, 428, 18, 1, 0, 0, 0, 429, 430, 5, 124, 0, 0, 430, 20, 1, 0, 0, 0, 431, 432, 5, 47, 0, 0, 432, 22, 1, 0, 0, 0, 433, 434, 5, 94, 0, 0, 434, 24, 1, 0, 0, 0, 435, 436, 5, 43, 0, 0, 436, 26, 1, 0, 0, 0, 437, 438, 5, 63, 0, 0, 438, 28, 1, 0, 0, 0, 439, 440, 5, 33, 0, 0, 440, 30, 1, 0, 0, 0, 441, 442, 5, 91, 0, 0, 442, 32, 1, 0, 0, 0, 443, 444, 5, 93, 0, 0, 444, 34, 1, 0, 0, 0, 445, 446, 5, 124, 0, 0, 446, 447, 5, 124, 0, 0, 447, 36, 1, 0, 0, 0, 448, 449, 5, 38, 0, 0, 449, 450, 5, 38, 0, 0, 450, 38, 1, 0, 0, 0, 451, 452, 5, 61, 0, 0, 452, 40, 1, 0, 0, 0, 453, 454, 5, 33, 0, 0, 454, 455, 5, 61, 0, 0, 455, 42, 1, 0, 0, 0, 456, 457, 5, 60, 0, 0, 457, 44, 1, 0, 0, 0, 458, 459, 5, 62, 0, 0, 459, 46, 1, 0, 0, 0, 460, 461, 5, 60, 0, 0, 461, 462, 5, 61, 0, 0, 462, 48, 1, 0, 0, 0, 463, 464, 5, 62, 0, 0, 464, 465, 5, 61, 0, 0, 465, 50, 1, 0, 0, 0, 466, 467, 5, 45, 0, 0, 467, 52, 1, 0, 0, 0, 468, 469, 5, 94, 0, 0, 469, 470, 5, 94, 0, 0, 470, 54, 1, 0, 0, 0, 471, 472, 5, 116, 0, 0, 472, 473, 5, 114, 0, 0, 473, 474, 5, 117, 0, 0, 474, 475, 5, 101, 0, 0, 475, 56, 1, 0, 0, 0, 476, 477, 5, 102, 0, 0, 477, 478, 5, 97, 0, 0, 478, 479, 5, 108, 0, 0, 479, 480, 5, 115, 0, 0, 480, 481, 5, 101, 0, 0, 481, 58, 1, 0, 0, 0, 482, 483, 3, 361, 180, 0, 483, 484, 3, 359, 179, 0, 484, 485, 3, 395, 197, 0, 485, 486, 3, 367, 183, 0, 486, 60, 1, 0, 0, 0, 487, 488, 3, 389, 194, 0, 488, 489, 3, 393, 196, 0, 489, 490, 3, 367, 183, 0, 490, 491, 3, 369, 184, 0, 491, 492, 3, 375, 187, 0, 492, 493, 3, 405, 202, 0, 493, 62, 1, 0, 0, 0, 494, 495, 3, 395, 197, 0, 495, 496, 3, 367, 183, 0, 496, 497, 3, 381, 190, 0, 497, 498, 3, 367, 183, 0, 498, 499, 3, 363, 181, 0, 499, 500, 3, 397, 198, 0, 500, 64, 1, 0, 0, 0, 501, 502, 3, 365, 182, 0, 502, 503, 3, 375, 187, 0, 503, 504, 3, 395, 197, 0, 504, 505, 3, 397, 198, 0, 505, 506, 3, 375, 187, 0, 506, 507, 3, 385, 192, 0, 507, 508, 3, 363, 181, 0, 508, 509, 3, 397, 198, 0, 509, 66, 1, 0, 0, 0, 510, 511, 3, 393, 196, 0, 511, 512, 3, 367, 183, 0, 512, 513, 3, 365, 182, 0, 513, 514, 3, 399, 199, 0, 514, 515, 3, 363, 181, 0, 515, 516, 3, 367, 183, 0, 516, 517, 3, 365, 182, 0, 517, 68, 1, 0, 0, 0, 518, 519, 3, 359, 179, 0, 519, 520, 3, 395, 197, 0, 520, 70, 1, 0, 0, 0, 521, 522, 3, 363, 181, 0, 522, 523, 3, 387, 193, 0, 523, 524, 3, 385, 192, 0, 524, 525, 3, 395, 197, 0, 525, 526, 3, 397, 198, 0, 526, 527, 3, 393, 196, 0, 527, 528, 3, 399, 199, 0, 528, 529, 3, 363, 181, 0, 529, 530, 3, 397, 198, 0, 530, 72, 1, 0, 0, 0, 531, 532, 3, 403, 201, 0, 532, 533, 3, 373, 186, 0, 533, 534, 3, 367, 183, 0, 534, 535, 3, 393, 196, 0, 535, 536, 3, 367, 183, 0, 536, 74, 1, 0, 0, 0, 537, 538, 3, 365, 182, 0, 538, 539, 3, 367, 183, 0, 539, 540, 3, 395, 197, 0, 540, 541, 3, 363, 181, 0, 541, 542, 3, 393, 196, 0, 542, 543, 3, 375, 187, 0, 543, 544, 3, 361, 180, 0, 544, 545, 3, 367, 183, 0, 545, 76, 1, 0, 0, 0, 546, 547, 3, 359, 179, 0, 547, 548, 3, 395, 197, 0, 548, 549, 3, 379, 189, 0, 549, 78, 1, 0, 0, 0, 550, 551, 3, 369, 184, 0, 551, 552, 3, 393, 196, 0, 552, 553, 3, 387, 193, 0, 553, 554, 3, 383, 191, 0, 554, 80, 1, 0, 0, 0, 555, 556, 3, 385, 192, 0, 556, 557, 3, 359, 179, 0, 557, 558, 3, 383, 191, 0, 558, 559, 3, 367, 183, 0, 559, 560, 3, 365, 182, 0, 560, 82, 1, 0, 0, 0, 561, 562, 3, 371, 185, 0, 562, 563, 3, 393, 196, 0, 563, 564, 3, 387, 193, 0, 564, 565, 3, 399, 199, 0, 565, 567, 3, 389, 194, 0, 566, 568, 3, 355, 177, 0, 567, 566, 1, 0, 0, 0, 568, 569, 1, 0, 0, 0, 569, 567, 1, 0, 0, 0, 569, 570, 1, 0, 0, 0, 570, 571, 1, 0, 0, 0, 571, 572, 3, 361, 180, 0, 572, 573, 3, 407, 203, 0, 573, 84, 1, 0, 0, 0, 574, 575, 3, 371, 185, 0, 575, 576, 3, 393, 196, 0, 576, 577, 3, 387, 193, 0, 577, 578, 3, 399, 199, 0, 578, 579, 3, 389, 194, 0, 579, 580, 5, 95, 0, 0, 580, 581, 3, 363, 181, 0, 581, 582, 3, 387, 193, 0, 582, 583, 3, 385, 192, 0, 583, 584, 3, 363, 181, 0, 584, 585, 3, 359, 179, 0, 585, 586, 3, 397, 198, 0, 586, 86, 1, 0, 0, 0, 587, 588, 3, 373, 186, 0, 588, 589, 3, 359, 179, 0, 589, 590, 3, 401, 200, 0, 590, 591, 3, 375, 187, 0, 591, 592, 3, 385, 192, 0, 592, 593, 3, 371, 185, 0, 593, 88, 1, 0, 0, 0, 594, 595, 3, 387, 193, 0, 595, 596, 3, 393, 196, 0, 596, 597, 3, 365, 182, 0, 597, 598, 3, 367, 183, 0, 598, 600, 3, 393, 196, 0, 599, 601, 3, 355, 177, 0, 600, 599, 1, 0, 0, 0, 601, 602, 1, 0, 0, 0, 602, 600, 1, 0, 0, 0, 602, 603, 1, 0, 0, 0, 603, 604, 1, 0, 0, 0, 604, 605, 3, 361, 180, 0, 605, 606, 3, 407, 203, 0, 606, 90, 1, 0, 0, 0, 607, 608, 3, 375, 187, 0, 608, 609, 3, 385, 192, 0, 609, 610, 3, 397, 198, 0, 610, 611, 3, 367, 183, 0, 611, 612, 3, 393, 196, 0, 612, 613, 3, 385, 192, 0, 613, 614, 3, 359, 179, 0, 614, 616, 3, 381, 190, 0, 615, 617, 3, 355, 177, 0, 616, 615, 1, 0, 0, 0, 617, 618, 1, 0, 0, 0, 618, 616, 1, 0, 0, 0, 618, 619, 1, 0, 0, 0, 619, 620, 1, 0, 0, 0, 620, 621, 3, 395, 197, 0, 621, 622, 3, 387, 193, 0, 622, 623, 3, 393, 196, 0, 623, 625, 3, 397, 198, 0, 624, 626, 3, 355, 177, 0, 625, 624, 1, 0, 0, 0, 626, 627, 1, 0, 0, 0, 627, 625, 1, 0, 0, 0, 627, 628, 1, 0, 0, 0, 628, 629, 1, 0, 0, 0, 629, 630, 3, 361, 180, 0, 630, 631, 3, 407, 203, 0, 631, 92, 1, 0, 0, 0, 632, 633, 3, 359, 179, 0, 633, 634, 3, 395, 197, 0, 634, 635, 3, 363, 181, 0, 635, 94, 1, 0, 0, 0, 636, 637, 3, 365, 182, 0, 637, 638, 3, 367, 183, 0, 638, 639, 3, 395, 197, 0, 639, 640, 3, 363, 181, 0, 640, 96, 1, 0, 0, 0, 641, 642, 3, 381, 190, 0, 642, 643, 3, 375, 187, 0, 643, 644, 3, 383, 191, 0, 644, 645, 3, 375, 187, 0, 645, 646, 3, 397, 198, 0, 646, 98, 1, 0, 0, 0, 647, 648, 3, 387, 193, 0, 648, 649, 3, 369, 184, 0, 649, 650, 3, 369, 184, 0, 650, 651, 3, 395, 197, 0, 651, 652, 3, 367, 183, 0, 652, 653, 3, 397, 198, 0, 653, 100, 1, 0, 0, 0, 654, 655, 3, 397, 198, 0, 655, 656, 3, 367, 183, 0, 656, 657, 3, 405, 202, 0, 657, 658, 3, 397, 198, 0, 658, 659, 3, 381, 190, 0, 659, 660, 3, 375, 187, 0, 660, 661, 3, 383, 191, 0, 661, 662, 3, 375, 187, 0, 662, 663, 3, 397, 198, 0, 663, 102, 1, 0, 0, 0, 664, 665, 3, 401, 200, 0, 665, 666, 3, 359, 179, 0, 666, 667, 3, 381, 190, 0, 667, 668, 3, 399, 199, 0, 668, 669, 3, 367, 183, 0, 669, 670, 3, 395, 197, 0, 670, 104, 1, 0, 0, 0, 671, 672, 3, 381, 190, 0, 672, 673, 3, 387, 193, 0, 673, 674, 3, 359, 179, 0, 674, 675, 3, 365, 182, 0, 675, 106, 1, 0, 0, 0, 676, 677, 3, 395, 197, 0, 677, 678, 3, 375, 187, 0, 678, 679, 3, 381, 190, 0, 679, 680, 3, 367, 183, 0, 680, 681, 3, 385, 192, 0, 681, 682, 3, 397, 198, 0, 682, 108, 1, 0, 0, 0, 683, 684, 3, 375, 187, 0, 684, 685, 3, 385, 192, 0, 685, 686, 3, 397, 198, 0, 686, 687, 3, 387, 193, 0, 687, 110, 1, 0, 0, 0, 688, 689, 3, 363, 181, 0, 689, 690, 3, 381, 190, 0, 690, 691, 3, 367, 183, 0, 691, 692, 3, 359, 179, 0, 692, 693, 3, 393, 196, 0, 693, 112, 1, 0, 0, 0, 694, 695, 3, 365, 182, 0, 695, 696, 3, 393, 196, 0, 696, 697, 3, 387, 193, 0, 697, 698, 3, 389, 194, 0, 698, 114, 1, 0, 0, 0, 699, 700, 3, 363, 181, 0, 700, 701, 3, 393, 196, 0, 701, 702, 3, 367, 183, 0, 702, 703, 3, 359, 179, 0, 703, 704, 3, 397, 198, 0, 704, 705, 3, 367, 183, 0, 705, 116, 1, 0, 0, 0, 706, 707, 3, 359, 179, 0, 707, 708, 3, 365, 182, 0, 708, 709, 3, 365, 182, 0, 709, 118, 1, 0, 0, 0, 710, 711, 3, 397, 198, 0, 711, 712, 3, 387, 193, 0, 712, 120, 1, 0, 0, 0, 713, 714, 3, 365, 182, 0, 714, 715, 3, 359, 179, 0, 715, 716, 3, 397, 198, 0, 716, 717, 3, 359, 179, 0, 717, 122, 1, 0, 0, 0, 718, 719, 3, 383, 191, 0, 719, 720, 3, 387, 193, 0, 720, 721, 3, 401, 200, 0, 721, 722, 3, 367, 183, 0, 722, 124, 1, 0, 0, 0, 723, 724, 3, 363, 181, 0, 724, 725, 3, 387, 193, 0, 725, 726, 3, 389, 194, 0, 726, 727, 3, 407, 203, 0, 727, 126, 1, 0, 0, 0, 728, 729, 3, 375, 187, 0, 729, 730, 3, 385, 192, 0, 730, 731, 3, 395, 197, 0, 731, 732, 3, 367, 183, 0, 732, 733, 3, 393, 196, 0, 733, 734, 3, 397, 198, 0, 734, 128, 1, 0, 0, 0, 735, 736, 3, 365, 182, 0, 736, 737, 3, 367, 183, 0, 737, 738, 3, 381, 190, 0, 738, 739, 3, 367, 183, 0, 739, 740, 3, 397, 198, 0, 740, 741, 3, 367, 183, 0, 741, 130, 1, 0, 0, 0, 742, 743, 3, 403, 201, 0, 743, 744, 3, 375, 187, 0, 744, 745, 3, 397, 198, 0, 745, 746, 3, 373, 186, 0, 746, 132, 1, 0, 0, 0, 747, 748, 3, 399, 199, 0, 748, 749, 3, 395, 197, 0, 749, 750, 3, 375, 187, 0, 750, 751, 3, 385, 192, 0, 751, 752, 3, 371, 185, 0, 752, 134, 1, 0, 0, 0, 753, 754, 3, 365, 182, 0, 754, 755, 3, 367, 183, 0, 755, 756, 3, 369, 184, 0, 756, 757, 3, 359, 179, 0, 757, 758, 3, 399, 199, 0, 758, 759, 3, 381, 190, 0, 759, 760, 3, 397, 198, 0, 760, 136, 1, 0, 0, 0, 761, 762, 3, 371, 185, 0, 762, 763, 3, 393, 196, 0, 763, 764, 3, 359, 179, 0, 764, 765, 3, 389, 194, 0, 765, 766, 3, 373, 186, 0, 766, 138, 1, 0, 0, 0, 767, 768, 3, 359, 179, 0, 768, 769, 3, 381, 190, 0, 769, 770, 3, 381, 190, 0, 770, 140, 1, 0, 0, 0, 771, 772, 3, 387, 193, 0, 772, 773, 3, 389, 194, 0, 773, 774, 3, 397, 198, 0, 774, 775, 3, 375, 187, 0, 775, 776, 3, 387, 193, 0, 776, 777, 3, 385, 192, 0, 777, 778, 3, 359, 179, 0, 778, 779, 3, 381, 190, 0, 779, 142, 1, 0, 0, 0, 780, 781, 3, 395, 197, 0, 781, 782, 3, 367, 183, 0, 782, 783, 3, 393, 196, 0, 783, 784, 3, 401, 200, 0, 784, 785, 3, 375, 187, 0, 785, 786, 3, 363, 181, 0, 786, 787, 3, 367, 183, 0, 787, 144, 1, 0, 0, 0, 788, 789, 3, 361, 180, 0, 789, 790, 3, 375, 187, 0, 790, 791, 3, 385, 192, 0, 791, 792, 3, 365, 182, 0, 792, 146, 1, 0, 0, 0, 793, 794, 3, 399, 199, 0, 794, 795, 3, 385, 192, 0, 795, 796, 3, 365, 182, 0, 796, 797, 3, 367, 183, 0, 797, 798, 3, 369, 184, 0, 798, 148, 1, 0, 0, 0, 799, 800, 3, 383, 191, 0, 800, 801, 3, 375, 187, 0, 801, 802, 3, 385, 192, 0, 802, 803, 3, 399, 199, 0, 803, 804, 3, 395, 197, 0, 804, 150, 1, 0, 0, 0, 805, 806, 3, 399, 199, 0, 806, 807, 3, 385, 192, 0, 807, 808, 3, 375, 187, 0, 808, 809, 3, 387, 193, 0, 809, 810, 3, 385, 192, 0, 810, 152, 1, 0, 0, 0, 811, 812, 3, 369, 184, 0, 812, 813, 3, 375, 187, 0, 813, 814, 3, 381, 190, 0, 814, 815, 3, 397, 198, 0, 815, 816, 3, 367, 183, 0, 816, 817, 3, 393, 196, 0, 817, 154, 1, 0, 0, 0, 818, 819, 3, 385, 192, 0, 819, 820, 3, 387, 193, 0, 820, 821, 3, 397, 198, 0, 821, 156, 1, 0, 0, 0, 822, 823, 3, 375, 187, 0, 823, 824, 3, 385, 192, 0, 824, 158, 1, 0, 0, 0, 825, 826, 3, 395, 197, 0, 826, 827, 3, 397, 198, 0, 827, 828, 3, 393, 196, 0, 828, 160, 1, 0, 0, 0, 829, 830, 3, 381, 190, 0, 830, 831, 3, 359, 179, 0, 831, 832, 3, 385, 192, 0, 832, 833, 3, 371, 185, 0, 833, 162, 1, 0, 0, 0, 834, 835, 3, 381, 190, 0, 835, 836, 3, 359, 179, 0, 836, 837, 3, 385, 192, 0, 837, 838, 3, 371, 185, 0, 838, 839, 3, 383, 191, 0, 839, 840, 3, 359, 179, 0, 840, 841, 3, 397, 198, 0, 841, 842, 3, 363, 181, 0, 842, 843, 3, 373, 186, 0, 843, 844, 3, 367, 183, 0, 844, 845, 3, 395, 197, 0, 845, 164, 1, 0, 0, 0, 846, 847, 3, 365, 182, 0, 847, 848, 3, 359, 179, 0, 848, 849, 3, 397, 198, 0, 849, 850, 3, 359, 179, 0, 850, 851, 3, 397, 198, 0, 851, 852, 3, 407, 203, 0, 852, 853, 3, 389, 194, 0, 853, 854, 3, 367, 183, 0, 854, 166, 1, 0, 0, 0, 855, 856, 3, 361, 180, 0, 856, 857, 3, 387, 193, 0, 857, 858, 3, 399, 199, 0, 858, 859, 3, 385, 192, 0, 859, 860, 3, 365, 182, 0, 860, 168, 1, 0, 0, 0, 861, 862, 3, 375, 187, 0, 862, 863, 3, 393, 196, 0, 863, 864, 3, 375, 187, 0, 864, 170, 1, 0, 0, 0, 865, 866, 3, 399, 199, 0, 866, 867, 3, 393, 196, 0, 867, 868, 3, 375, 187, 0, 868, 172, 1, 0, 0, 0, 869, 870, 3, 361, 180, 0, 870, 871, 3, 385, 192, 0, 871, 872, 3, 387, 193, 0, 872, 873, 3, 365, 182, 0, 873, 874, 3, 367, 183, 0, 874, 174, 1, 0, 0, 0, 875, 876, 3, 393, 196, 0, 876, 877, 3, 359, 179, 0, 877, 878, 3, 385, 192, 0, 878, 879, 3, 365, 182, 0, 879, 176, 1, 0, 0, 0, 880, 881, 3, 359, 179, 0, 881, 882, 3, 361, 180, 0, 882, 883, 3, 395, 197, 0, 883, 178, 1, 0, 0, 0, 884, 885, 3, 363, 181, 0, 885, 886, 3, 367, 183, 0, 886, 887, 3, 375, 187, 0, 887, 888, 3, 381, 190, 0, 888, 180, 1, 0, 0, 0, 889, 890, 3, 369, 184, 0, 890, 891, 3, 381, 190, 0, 891, 892, 3, 387, 193, 0, 892, 893, 3, 387, 193, 0, 893, 894, 3, 393, 196, 0, 894, 182, 1, 0, 0, 0, 895, 896, 3, 393, 196, 0, 896, 897, 3, 387, 193, 0, 897, 898, 3, 399, 199, 0, 898, 899, 3, 385, 192, 0, 899, 900, 3, 365, 182, 0, 900, 184, 1, 0, 0, 0, 901, 902, 3, 363, 181, 0, 902, 903, 3, 387, 193, 0, 903, 904, 3, 385, 192, 0, 904, 905, 3, 363, 181, 0, 905, 906, 3, 359, 179, 0, 906, 907, 3, 397, 198, 0, 907, 186, 1, 0, 0, 0, 908, 909, 3, 395, 197, 0, 909, 910, 3, 397, 198, 0, 910, 911, 3, 393, 196, 0, 911, 912, 3, 381, 190, 0, 912, 913, 3, 367, 183, 0, 913, 914, 3, 385, 192, 0, 914, 188, 1, 0, 0, 0, 915, 916, 3, 399, 199, 0, 916, 917, 3, 363, 181, 0, 917, 918, 3, 359, 179, 0, 918, 919, 3, 395, 197, 0, 919, 920, 3, 367, 183, 0, 920, 190, 1, 0, 0, 0, 921, 922, 3, 381, 190, 0, 922, 923, 3, 363, 181, 0, 923, 924, 3, 359, 179, 0, 924, 925, 3, 395, 197, 0, 925, 926, 3, 367, 183, 0, 926, 192, 1, 0, 0, 0, 927, 928, 3, 367, 183, 0, 928, 929, 3, 385, 192, 0, 929, 930, 3, 363, 181, 0, 930, 931, 3, 387, 193, 0, 931, 932, 3, 365, 182, 0, 932, 933, 3, 367, 183, 0, 933, 934, 5, 95, 0, 0, 934, 935, 3, 369, 184, 0, 935, 936, 3, 387, 193, 0, 936, 937, 3, 393, 196, 0, 937, 938, 5, 95, 0, 0, 938, 939, 3, 399, 199, 0, 939, 940, 3, 393, 196, 0, 940, 941, 3, 375, 187, 0, 941, 194, 1, 0, 0, 0, 942, 943, 3, 369, 184, 0, 943, 944, 3, 387, 193, 0, 944, 945, 3, 393, 196, 0, 945, 196, 1, 0, 0, 0, 946, 947, 3, 363, 181, 0, 947, 948, 3, 387, 193, 0, 948, 949, 3, 385, 192, 0, 949, 950, 3, 397, 198, 0, 950, 951, 3, 359, 179, 0, 951, 952, 3, 375, 187, 0, 952, 953, 3, 385, 192, 0, 953, 954, 3, 395, 197, 0, 954, 198, 1, 0, 0, 0, 955, 956, 3, 395, 197, 0, 956, 957, 3, 397, 198, 0, 957, 958, 3, 393, 196, 0, 958, 959, 3, 395, 197, 0, 959, 960, 3, 397, 198, 0, 960, 961, 3, 359, 179, 0, 961, 962, 3, 393, 196, 0, 962, 963, 3, 397, 198, 0, 963, 964, 3, 395, 197, 0, 964, 200, 1, 0, 0, 0, 965, 966, 3, 395, 197, 0, 966, 967, 3, 397, 198, 0, 967, 968, 3, 393, 196, 0, 968, 969, 3, 367, 183, 0, 969, 970, 3, 385, 192, 0, 970, 971, 3, 365, 182, 0, 971, 972, 3, 395, 197, 0, 972, 202, 1, 0, 0, 0, 973, 974, 3, 395, 197, 0, 974, 975, 3, 397, 198, 0, 975, 976, 3, 393, 196, 0, 976, 977, 3, 361, 180, 0, 977, 978, 3, 367, 183, 0, 978, 979, 3, 369, 184, 0, 979, 980, 3, 387, 193, 0, 980, 981, 3, 393, 196, 0, 981, 982, 3, 367, 183, 0, 982, 204, 1, 0, 0, 0, 983, 984, 3, 395, 197, 0, 984, 985, 3, 397, 198, 0, 985, 986, 3, 393, 196, 0, 986, 987, 3, 359, 179, 0, 987, 988, 3, 369, 184, 0, 988, 989, 3, 397, 198, 0, 989, 990, 3, 367, 183, 0, 990, 991, 3, 393, 196, 0, 991, 206, 1, 0, 0, 0, 992, 993, 3, 407, 203, 0, 993, 994, 3, 367, 183, 0, 994, 995, 3, 359, 179, 0, 995, 996, 3, 393, 196, 0, 996, 208, 1, 0, 0, 0, 997, 998, 3, 383, 191, 0, 998, 999, 3, 387, 193, 0, 999, 1000, 3, 385, 192, 0, 1000, 1001, 3, 397, 198, 0, 1001, 1002, 3, 373, 186, 0, 1002, 210, 1, 0, 0, 0, 1003, 1004, 3, 365, 182, 0, 1004, 1005, 3, 359, 179, 0, 1005, 1006, 3, 407, 203, 0, 1006, 212, 1, 0, 0, 0, 1007, 1008, 3, 373, 186, 0, 1008, 1009, 3, 387, 193, 0, 1009, 1010, 3, 399, 199, 0, 1010, 1011, 3, 393, 196, 0, 1011, 1012, 3, 395, 197, 0, 1012, 214, 1, 0, 0, 0, 1013, 1014, 3, 383, 191, 0, 1014, 1015, 3, 375, 187, 0, 1015, 1016, 3, 385, 192, 0, 1016, 1017, 3, 399, 199, 0, 1017, 1018, 3, 397, 198, 0, 1018, 1019, 3, 367, 183, 0, 1019, 1020, 3, 395, 197, 0, 1020, 216, 1, 0, 0, 0, 1021, 1022, 3, 395, 197, 0, 1022, 1023, 3, 367, 183, 0, 1023, 1024, 3, 363, 181, 0, 1024, 1025, 3, 387, 193, 0, 1025, 1026, 3, 385, 192, 0, 1026, 1027, 3, 365, 182, 0, 1027, 1028, 3, 395, 197, 0, 1028, 218, 1, 0, 0, 0, 1029, 1030, 3, 397, 198, 0, 1030, 1031, 3, 375, 187, 0, 1031, 1032, 3, 383, 191, 0, 1032, 1033, 3, 367, 183, 0, 1033, 1034, 3, 409, 204, 0, 1034, 1035, 3, 387, 193, 0, 1035, 1036, 3, 385, 192, 0, 1036, 1037, 3, 367, 183, 0, 1037, 220, 1, 0, 0, 0, 1038, 1039, 3, 397, 198, 0, 1039, 1040, 3, 409, 204, 0, 1040, 222, 1, 0, 0, 0, 1041, 1042, 3, 385, 192, 0, 1042, 1043, 3, 387, 193, 0, 1043, 1044, 3, 403, 201, 0, 1044, 224, 1, 0, 0, 0, 1045, 1046, 3, 399, 199, 0, 1046, 1047, 3, 399, 199, 0, 1047, 1048, 3, 375, 187, 0, 1048, 1049, 3, 365, 182, 0, 1049, 226, 1, 0, 0, 0, 1050, 1051, 3, 395, 197, 0, 1051, 1052, 3, 397, 198, 0, 1052, 1053, 3, 393, 196, 0, 1053, 1054, 3, 399, 199, 0, 1054, 1055, 3, 399, 199, 0, 1055, 1056, 3, 375, 187, 0, 1056, 1057, 3, 365, 182, 0, 1057, 228, 1, 0, 0, 0, 1058, 1059, 3, 395, 197, 0, 1059, 1060, 3, 373, 186, 0, 1060, 1061, 3, 359, 179, 0, 1061, 1062, 5, 49, 0, 0, 1062, 230, 1, 0, 0, 0, 1063, 1064, 3, 395, 197, 0, 1064, 1065, 3, 373, 186, 0, 1065, 1066, 3, 359, 179, 0, 1066, 1067, 5, 50, 0, 0, 1067, 1068, 5, 53, 0, 0, 1068, 1069, 5, 54, 0, 0, 1069, 232, 1, 0, 0, 0, 1070, 1071, 3, 395, 197, 0, 1071, 1072, 3, 373, 186, 0, 1072, 1073, 3, 359, 179, 0, 1073, 1074, 5, 51, 0, 0, 1074, 1075, 5, 56, 0, 0, 1075, 1076, 5, 52, 0, 0, 1076, 234, 1, 0, 0, 0, 1077, 1078, 3, 395, 197, 0, 1078, 1079, 3, 373, 186, 0, 1079, 1080, 3, 359, 179, 0, 1080, 1081, 5, 53, 0, 0, 1081, 1082, 5, 49, 0, 0, 1082, 1083, 5, 50, 0, 0, 1083, 236, 1, 0, 0, 0, 1084, 1085, 3, 383, 191, 0, 1085, 1086, 3, 365, 182, 0, 1086, 1087, 5, 53, 0, 0, 1087, 238, 1, 0, 0, 0, 1088, 1089, 3, 363, 181, 0, 1089, 1090, 3, 387, 193, 0, 1090, 1091, 3, 359, 179, 0, 1091, 1092, 3, 381, 190, 0, 1092, 1093, 3, 367, 183, 0, 1093, 1094, 3, 395, 197, 0, 1094, 1095, 3, 363, 181, 0, 1095, 1096, 3, 367, 183, 0, 1096, 240, 1, 0, 0, 0, 1097, 1098, 3, 375, 187, 0, 1098, 1099, 3, 369, 184, 0, 1099, 242, 1, 0, 0, 0, 1100, 1101, 3, 395, 197, 0, 1101, 1102, 3, 397, 198, 0, 1102, 1103, 3, 393, 196, 0, 1103, 1104, 3, 381, 190, 0, 1104, 1105, 3, 359, 179, 0, 1105, 1106, 3, 385, 192, 0, 1106, 1107, 3, 371, 185, 0, 1107, 244, 1, 0, 0, 0, 1108, 1109, 3, 395, 197, 0, 1109, 1110, 3, 397, 198, 0, 1110, 1111, 3, 393, 196, 0, 1111, 1112, 3, 365, 182, 0, 1112, 1113, 3, 397, 198, 0, 1113, 246, 1, 0, 0, 0, 1114, 1115, 3, 395, 197, 0, 1115, 1116, 3, 359, 179, 0, 1116, 1117, 3, 383, 191, 0, 1117, 1118, 3, 367, 183, 0, 1118, 1119, 3, 397, 198, 0, 1119, 1120, 3, 367, 183, 0, 1120, 1121, 3, 393, 196, 0, 1121, 1122, 3, 383, 191, 0, 1122, 248, 1, 0, 0, 0, 1123, 1124, 3, 375, 187, 0, 1124, 1125, 3, 395, 197, 0, 1125, 1126, 3, 375, 187, 0, 1126, 1127, 3, 393, 196, 0, 1127, 1128, 3, 375, 187, 0, 1128, 250, 1, 0, 0, 0, 1129, 1130, 3, 375, 187, 0, 1130, 1131, 3, 395, 197, 0, 1131, 1132, 3, 399, 199, 0, 1132, 1133, 3, 393, 196, 0, 1133, 1134, 3, 375, 187, 0, 1134, 252, 1, 0, 0, 0, 1135, 1136, 3, 375, 187, 0, 1136, 1137, 3, 395, 197, 0, 1137, 1138, 3, 361, 180, 0, 1138, 1139, 3, 381, 190, 0, 1139, 1140, 3, 359, 179, 0, 1140, 1141, 3, 385, 192, 0, 1141, 1142, 3, 379, 189, 0, 1142, 254, 1, 0, 0, 0, 1143, 1144, 3, 375, 187, 0, 1144, 1145, 3, 395, 197, 0, 1145, 1146, 3, 381, 190, 0, 1146, 1147, 3, 375, 187, 0, 1147, 1148, 3, 397, 198, 0, 1148, 1149, 3, 367, 183, 0, 1149, 1150, 3, 393, 196, 0, 1150, 1151, 3, 359, 179, 0, 1151, 1152, 3, 381, 190, 0, 1152, 256, 1, 0, 0, 0, 1153, 1154, 3, 375, 187, 0, 1154, 1155, 3, 395, 197, 0, 1155, 1156, 3, 385, 192, 0, 1156, 1157, 3, 399, 199, 0, 1157, 1158, 3, 383, 191, 0, 1158, 1159, 3, 367, 183, 0, 1159, 1160, 3, 393, 196, 0, 1160, 1161, 3, 375, 187, 0, 1161, 1162, 3, 363, 181, 0, 1162, 258, 1, 0, 0, 0, 1163, 1164, 3, 393, 196, 0, 1164, 1165, 3, 367, 183, 0, 1165, 1166, 3, 371, 185, 0, 1166, 1167, 3, 367, 183, 0, 1167, 1168, 3, 405, 202, 0, 1168, 260, 1, 0, 0, 0, 1169, 1170, 3, 395, 197, 0, 1170, 1171, 3, 399, 199, 0, 1171, 1172, 3, 361, 180, 0, 1172, 1173, 3, 395, 197, 0, 1173, 1174, 3, 397, 198, 0, 1174, 1175, 3, 393, 196, 0, 1175, 262, 1, 0, 0, 0, 1176, 1177, 3, 393, 196, 0, 1177, 1178, 3, 367, 183, 0, 1178, 1179, 3, 389, 194, 0, 1179, 1180, 3, 381, 190, 0, 1180, 1181, 3, 359, 179, 0, 1181, 1182, 3, 363, 181, 0, 1182, 1183, 3, 367, 183, 0, 1183, 264, 1, 0, 0, 0, 1184, 1185, 3, 367, 183, 0, 1185, 1186, 3, 405, 202, 0, 1186, 1187, 3, 375, 187, 0, 1187, 1188, 3, 395, 197, 0, 1188, 1189, 3, 397, 198, 0, 1189, 1190, 3, 395, 197, 0, 1190, 266, 1, 0, 0, 0, 1191, 1192, 3, 363, 181, 0, 1192, 1193, 3, 387, 193, 0, 1193, 1194, 3, 399, 199, 0, 1194, 1195, 3, 385, 192, 0, 1195, 1196, 3, 397, 198, 0, 1196, 268, 1, 0, 0, 0, 1197, 1198, 3, 395, 197, 0, 1198, 1199, 3, 399, 199, 0, 1199, 1200, 3, 383, 191, 0, 1200, 270, 1, 0, 0, 0, 1201, 1202, 3, 383, 191, 0, 1202, 1203, 3, 375, 187, 0, 1203, 1204, 3, 385, 192, 0, 1204, 272, 1, 0, 0, 0, 1205, 1206, 3, 383, 191, 0, 1206, 1207, 3, 359, 179, 0, 1207, 1208, 3, 405, 202, 0, 1208, 274, 1, 0, 0, 0, 1209, 1210, 3, 359, 179, 0, 1210, 1211, 3, 401, 200, 0, 1211, 1212, 3, 371, 185, 0, 1212, 276, 1, 0, 0, 0, 1213, 1214, 3, 395, 197, 0, 1214, 1215, 3, 397, 198, 0, 1215, 1216, 3, 365, 182, 0, 1216, 1217, 3, 367, 183, 0, 1217, 1218, 3, 401, 200, 0, 1218, 278, 1, 0, 0, 0, 1219, 1220, 3, 395, 197, 0, 1220, 1221, 3, 359, 179, 0, 1221, 1222, 3, 383, 191, 0, 1222, 1223, 3, 389, 194, 0, 1223, 1224, 3, 381, 190, 0, 1224, 1225, 3, 367, 183, 0, 1225, 280, 1, 0, 0, 0, 1226, 1227, 3, 395, 197, 0, 1227, 1228, 3, 367, 183, 0, 1228, 1229, 3, 389, 194, 0, 1229, 1230, 3, 359, 179, 0, 1230, 1231, 3, 393, 196, 0, 1231, 1232, 3, 359, 179, 0, 1232, 1233, 3, 397, 198, 0, 1233, 1234, 3, 387, 193, 0, 1234, 1235, 3, 393, 196, 0, 1235, 282, 1, 0, 0, 0, 1236, 1240, 5, 60, 0, 0, 1237, 1239, 8, 0, 0, 0, 1238, 1237, 1, 0, 0, 0, 1239, 1242, 1, 0, 0, 0, 1240, 1238, 1, 0, 0, 0, 1240, 1241, 1, 0, 0, 0, 1241, 1243, 1, 0, 0, 0, 1242, 1240, 1, 0, 0, 0, 1243, 1244, 5, 62, 0, 0, 1244, 284, 1, 0, 0, 0, 1245, 1247, 3, 339, 169, 0, 1246, 1245, 1, 0, 0, 0, 1246, 1247, 1, 0, 0, 0, 1247, 1248, 1, 0, 0, 0, 1248, 1249, 5, 58, 0, 0, 1249, 286, 1, 0, 0, 0, 1250, 1251, 3, 285, 142, 0, 1251, 1252, 3, 341, 170, 0, 1252, 288, 1, 0, 0, 0, 1253, 1254, 5, 95, 0, 0, 1254, 1255, 5, 58, 0, 0, 1255, 1258, 1, 0, 0, 0, 1256, 1259, 3, 333, 166, 0, 1257, 1259, 3, 353, 176, 0, 1258, 1256, 1, 0, 0, 0, 1258, 1257, 1, 0, 0, 0, 1259, 1268, 1, 0, 0, 0, 1260, 1263, 3, 337, 168, 0, 1261, 1263, 5, 46, 0, 0, 1262, 1260, 1, 0, 0, 0, 1262, 1261, 1, 0, 0, 0, 1263, 1266, 1, 0, 0, 0, 1264, 1262, 1, 0, 0, 0, 1264, 1265, 1, 0, 0, 0, 1265, 1267, 1, 0, 0, 0, 1266, 1264, 1, 0, 0, 0, 1267, 1269, 3, 337, 168, 0, 1268, 1264, 1, 0, 0, 0, 1268, 1269, 1, 0, 0, 0, 1269, 290, 1, 0, 0, 0, 1270, 1271, 5, 63, 0, 0, 1271, 1272, 3, 335, 167, 0, 1272, 292, 1, 0, 0, 0, 1273, 1274, 5, 36, 0, 0, 1274, 1275, 3, 335, 167, 0, 1275, 294, 1, 0, 0, 0, 1276, 1278, 5, 64, 0, 0, 1277, 1279, 7, 1, 0, 0, 1278, 1277, 1, 0, 0, 0, 1279, 1280, 1, 0, 0, 0, 1280, 1278, 1, 0, 0, 0, 1280, 1281, 1, 0, 0, 0, 1281, 1291, 1, 0, 0, 0, 1282, 1285, 5, 45, 0, 0, 1283, 1286, 7, 1, 0, 0, 1284, 1286, 3, 353, 176, 0, 1285, 1283, 1, 0, 0, 0, 1285, 1284, 1, 0, 0, 0, 1286, 1287, 1, 0, 0, 0, 1287, 1285, 1, 0, 0, 0, 1287, 1288, 1, 0, 0, 0, 1288, 1290, 1, 0, 0, 0, 1289, 1282, 1, 0, 0, 0, 1290, 1293, 1, 0, 0, 0, 1291, 1289, 1, 0, 0, 0, 1291, 1292, 1, 0, 0, 0, 1292, 296, 1, 0, 0, 0, 1293, 1291, 1, 0, 0, 0, 1294, 1295, 3, 295, 147, 0, 1295, 1296, 5, 64, 0, 0, 1296, 298, 1, 0, 0, 0, 1297, 1299, 3, 353, 176, 0, 1298, 1297, 1, 0, 0, 0, 1299, 1300, 1, 0, 0, 0, 1300, 1298, 1, 0, 0, 0, 1300, 1301, 1, 0, 0, 0, 1301, 300, 1, 0, 0, 0, 1302, 1304, 3, 353, 176, 0, 1303, 1302, 1, 0, 0, 0, 1304, 1307, 1, 0, 0, 0, 1305, 1303, 1, 0, 0, 0, 1305, 1306, 1, 0, 0, 0, 1306, 1308, 1, 0, 0, 0, 1307, 1305, 1, 0, 0, 0, 1308, 1310, 5, 46, 0, 0, 1309, 1311, 3, 353, 176, 0, 1310, 1309, 1, 0, 0, 0, 1311, 1312, 1, 0, 0, 0, 1312, 1310, 1, 0, 0, 0, 1312, 1313, 1, 0, 0, 0, 1313, 302, 1, 0, 0, 0, 1314, 1316, 3, 353, 176, 0, 1315, 1314, 1, 0, 0, 0, 1316, 1317, 1, 0, 0, 0, 1317, 1315, 1, 0, 0, 0, 1317, 1318, 1, 0, 0, 0, 1318, 1319, 1, 0, 0, 0, 1319, 1323, 5, 46, 0, 0, 1320, 1322, 3, 353, 176, 0, 1321, 1320, 1, 0, 0, 0, 1322, 1325, 1, 0, 0, 0, 1323, 1321, 1, 0, 0, 0, 1323, 1324, 1, 0, 0, 0, 1324, 1326, 1, 0, 0, 0, 1325, 1323, 1, 0, 0, 0, 1326, 1327, 3, 317, 158, 0, 1327, 1344, 1, 0, 0, 0, 1328, 1330, 5, 46, 0, 0, 1329, 1331, 3, 353, 176, 0, 1330, 1329, 1, 0, 0, 0, 1331, 1332, 1, 0, 0, 0, 1332, 1330, 1, 0, 0, 0, 1332, 1333, 1, 0, 0, 0, 1333, 1334, 1, 0, 0, 0, 1334, 1335, 3, 317, 158, 0, 1335, 1344, 1, 0, 0, 0, 1336, 1338, 3, 353, 176, 0, 1337, 1336, 1, 0, 0, 0, 1338, 1339, 1, 0, 0, 0, 1339, 1337, 1, 0, 0, 0, 1339, 1340, 1, 0, 0, 0, 1340, 1341, 1, 0, 0, 0, 1341, 1342, 3, 317, 158, 0, 1342, 1344, 1, 0, 0, 0, 1343, 1315, 1, 0, 0, 0, 1343, 1328, 1, 0, 0, 0, 1343, 1337, 1, 0, 0, 0, 1344, 304, 1, 0, 0, 0, 1345, 1346, 5, 43, 0, 0, 1346, 1347, 3, 299, 149, 0, 1347, 306, 1, 0, 0, 0, 1348, 1349, 5, 43, 0, 0, 1349, 1350, 3, 301, 150, 0, 1350, 308, 1, 0, 0, 0, 1351, 1352, 5, 43, 0, 0, 1352, 1353, 3, 303, 151, 0, 1353, 310, 1, 0, 0, 0, 1354, 1355, 5, 45, 0, 0, 1355, 1356, 3, 299, 149, 0, 1356, 312, 1, 0, 0, 0, 1357, 1358, 5, 45, 0, 0, 1358, 1359, 3, 301, 150, 0, 1359, 314, 1, 0, 0, 0, 1360, 1361, 5, 45, 0, 0, 1361, 1362, 3, 303, 151, 0, 1362, 316, 1, 0, 0, 0, 1363, 1365, 7, 2, 0, 0, 1364, 1366, 7, 3, 0, 0, 1365, 1364, 1, 0, 0, 0, 1365, 1366, 1, 0, 0, 0, 1366, 1368, 1, 0, 0, 0, 1367, 1369, 3, 353, 176, 0, 1368, 1367, 1, 0, 0, 0, 1369, 1370, 1, 0, 0, 0, 1370, 1368, 1, 0, 0, 0, 1370, 1371, 1, 0, 0, 0, 1371, 318, 1, 0, 0, 0, 1372, 1377, 5, 39, 0, 0, 1373, 1376, 8, 4, 0, 0, 1374, 1376, 3, 327, 163, 0, 1375, 1373, 1, 0, 0, 0, 1375, 1374, 1, 0, 0, 0, 1376, 1379, 1, 0, 0, 0, 1377, 1375, 1, 0, 0, 0, 1377, 1378, 1, 0, 0, 0, 1378, 1380, 1, 0, 0, 0, 1379, 1377, 1, 0, 0, 0, 1380, 1381, 5, 39, 0, 0, 1381, 320, 1, 0, 0, 0, 1382, 1387, 5, 34, 0, 0, 1383, 1386, 8, 5, 0, 0, 1384, 1386, 3, 327, 163, 0, 1385, 1383, 1, 0, 0, 0, 1385, 1384, 1, 0, 0, 0, 1386, 1389, 1, 0, 0, 0, 1387, 1385, 1, 0, 0, 0, 1387, 1388, 1, 0, 0, 0, 1388, 1390, 1, 0, 0, 0, 1389, 1387, 1, 0, 0, 0, 1390, 1391, 5, 34, 0, 0, 1391, 322, 1, 0, 0, 0, 1392, 1393, 5, 39, 0, 0, 1393, 1394, 5, 39, 0, 0, 1394, 1395, 5, 39, 0, 0, 1395, 1407, 1, 0, 0, 0, 1396, 1400, 5, 39, 0, 0, 1397, 1398, 5, 39, 0, 0, 1398, 1400, 5, 39, 0, 0, 1399, 1396, 1, 0, 0, 0, 1399, 1397, 1, 0, 0, 0, 1399, 1400, 1, 0, 0, 0, 1400, 1403, 1, 0, 0, 0, 1401, 1404, 8, 6, 0, 0, 1402, 1404, 3, 327, 163, 0, 1403, 1401, 1, 0, 0, 0, 1403, 1402, 1, 0, 0, 0, 1404, 1406, 1, 0, 0, 0, 1405, 1399, 1, 0, 0, 0, 1406, 1409, 1, 0, 0, 0, 1407, 1405, 1, 0, 0, 0, 1407, 1408, 1, 0, 0, 0, 1408, 1410, 1, 0, 0, 0, 1409, 1407, 1, 0, 0, 0, 1410, 1411, 5, 39, 0, 0, 1411, 1412, 5, 39, 0, 0, 1412, 1413, 5, 39, 0, 0, 1413, 324, 1, 0, 0, 0, 1414, 1415, 5, 34, 0, 0, 1415, 1416, 5, 34, 0, 0, 1416, 1417, 5, 34, 0, 0, 1417, 1429, 1, 0, 0, 0, 1418, 1422, 5, 34, 0, 0, 1419, 1420, 5, 34, 0, 0, 1420, 1422, 5, 34, 0, 0, 1421, 1418, 1, 0, 0, 0, 1421, 1419, 1, 0, 0, 0, 1421, 1422, 1, 0, 0, 0, 1422, 1425, 1, 0, 0, 0, 1423, 1426, 8, 7, 0, 0, 1424, 1426, 3, 327, 163, 0, 1425, 1423, 1, 0, 0, 0, 1425, 1424, 1, 0, 0, 0, 1426, 1428, 1, 0, 0, 0, 1427, 1421, 1, 0, 0, 0, 1428, 1431, 1, 0, 0, 0, 1429, 1427, 1, 0, 0, 0, 1429, 1430, 1, 0, 0, 0, 1430, 1432, 1, 0, 0, 0, 1431, 1429, 1, 0, 0, 0, 1432, 1433, 5, 34, 0, 0, 1433, 1434, 5, 34, 0, 0, 1434, 1435, 5, 34, 0, 0, 1435, 326, 1, 0, 0, 0, 1436, 1437, 5, 92, 0, 0, 1437, 1438, 7, 8, 0, 0, 1438, 328, 1, 0, 0, 0, 1439, 1443, 5, 40, 0, 0, 1440, 1442, 3, 355, 177, 0, 1441, 1440, 1, 0, 0, 0, 1442, 1445, 1, 0, 0, 0, 1443, 1441, 1, 0, 0, 0, 1443, 1444, 1, 0, 0, 0, 1444, 1446, 1, 0, 0, 0, 1445, 1443, 1, 0, 0, 0, 1446, 1447, 5, 41, 0, 0, 1447, 330, 1, 0, 0, 0, 1448, 1452, 5, 91, 0, 0, 1449, 1451, 3, 355, 177, 0, 1450, 1449, 1, 0, 0, 0, 1451, 1454, 1, 0, 0, 0, 1452, 1450, 1, 0, 0, 0, 1452, 1453, 1, 0, 0, 0, 1453, 1455, 1, 0, 0, 0, 1454, 1452, 1, 0, 0, 0, 1455, 1456, 5, 93, 0, 0, 1456, 332, 1, 0, 0, 0, 1457, 1460, 3, 351, 175, 0, 1458, 1460, 5, 95, 0, 0, 1459, 1457, 1, 0, 0, 0, 1459, 1458, 1, 0, 0, 0, 1460, 334, 1, 0, 0, 0, 1461, 1464, 3, 333, 166, 0, 1462, 1464, 3, 353, 176, 0, 1463, 1461, 1, 0, 0, 0, 1463, 1462, 1, 0, 0, 0, 1464, 1470, 1, 0, 0, 0, 1465, 1469, 3, 333, 166, 0, 1466, 1469, 3, 353, 176, 0, 1467, 1469, 7, 9, 0, 0, 1468, 1465, 1, 0, 0, 0, 1468, 1466, 1, 0, 0, 0, 1468, 1467, 1, 0, 0, 0, 1469, 1472, 1, 0, 0, 0, 1470, 1468, 1, 0, 0, 0, 1470, 1471, 1, 0, 0, 0, 1471, 336, 1, 0, 0, 0, 1472, 1470, 1, 0, 0, 0, 1473, 1478, 3, 333, 166, 0, 1474, 1478, 5, 45, 0, 0, 1475, 1478, 3, 353, 176, 0, 1476, 1478, 7, 9, 0, 0, 1477, 1473, 1, 0, 0, 0, 1477, 1474, 1, 0, 0, 0, 1477, 1475, 1, 0, 0, 0, 1477, 1476, 1, 0, 0, 0, 1478, 338, 1, 0, 0, 0, 1479, 1488, 3, 351, 175, 0, 1480, 1483, 3, 337, 168, 0, 1481, 1483, 5, 46, 0, 0, 1482, 1480, 1, 0, 0, 0, 1482, 1481, 1, 0, 0, 0, 1483, 1486, 1, 0, 0, 0, 1484, 1482, 1, 0, 0, 0, 1484, 1485, 1, 0, 0, 0, 1485, 1487, 1, 0, 0, 0, 1486, 1484, 1, 0, 0, 0, 1487, 1489, 3, 337, 168, 0, 1488, 1484, 1, 0, 0, 0, 1488, 1489, 1, 0, 0, 0, 1489, 340, 1, 0, 0, 0, 1490, 1495, 3, 333, 166, 0, 1491, 1495, 5, 58, 0, 0, 1492, 1495, 3, 353, 176, 0, 1493, 1495, 3, 343, 171, 0, 1494, 1490, 1, 0, 0, 0, 1494, 1491, 1, 0, 0, 0, 1494, 1492, 1, 0, 0, 0, 1494, 1493, 1, 0, 0, 0, 1495, 1509, 1, 0, 0, 0, 1496, 1500, 3, 337, 168, 0, 1497, 1500, 7, 10, 0, 0, 1498, 1500, 3, 343, 171, 0, 1499, 1496, 1, 0, 0, 0, 1499, 1497, 1, 0, 0, 0, 1499, 1498, 1, 0, 0, 0, 1500, 1503, 1, 0, 0, 0, 1501, 1499, 1, 0, 0, 0, 1501, 1502, 1, 0, 0, 0, 1502, 1507, 1, 0, 0, 0, 1503, 1501, 1, 0, 0, 0, 1504, 1508, 3, 337, 168, 0, 1505, 1508, 5, 58, 0, 0, 1506, 1508, 3, 343, 171, 0, 1507, 1504, 1, 0, 0, 0, 1507, 1505, 1, 0, 0, 0, 1507, 1506, 1, 0, 0, 0, 1508, 1510, 1, 0, 0, 0, 1509, 1501, 1, 0, 0, 0, 1509, 1510, 1, 0, 0, 0, 1510, 342, 1, 0, 0, 0, 1511, 1514, 3, 345, 172, 0, 1512, 1514, 3, 349, 174, 0, 1513, 1511, 1, 0, 0, 0, 1513, 1512, 1, 0, 0, 0, 1514, 344, 1, 0, 0, 0, 1515, 1516, 5, 37, 0, 0, 1516, 1517, 3, 347, 173, 0, 1517, 1518, 3, 347, 173, 0, 1518, 346, 1, 0, 0, 0, 1519, 1522, 3, 353, 176, 0, 1520, 1522, 7, 11, 0, 0, 1521, 1519, 1, 0, 0, 0, 1521, 1520, 1, 0, 0, 0, 1522, 348, 1, 0, 0, 0, 1523, 1524, 5, 92, 0, 0, 1524, 1525, 7, 12, 0, 0, 1525, 350, 1, 0, 0, 0, 1526, 1527, 7, 13, 0, 0, 1527, 352, 1, 0, 0, 0, 1528, 1529, 2, 48, 57, 0, 1529, 354, 1, 0, 0, 0, 1530, 1532, 7, 14, 0, 0, 1531, 1530, 1, 0, 0, 0, 1532, 1533, 1, 0, 0, 0, 1533, 1531, 1, 0, 0, 0, 1533, 1534, 1, 0, 0, 0, 1534, 1535, 1, 0, 0, 0, 1535, 1536, 6, 177, 0, 0, 1536, 356, 1, 0, 0, 0, 1537, 1541, 5, 35, 0, 0, 1538, 1540, 8, 15, 0, 0, 1539, 1538, 1, 0, 0, 0, 1540, 1543, 1, 0, 0, 0, 1541, 1539, 1, 0, 0, 0, 1541, 1542, 1, 0, 0, 0, 1542, 1544, 1, 0, 0, 0, 1543, 1541, 1, 0, 0, 0, 1544, 1545, 6, 178, 0, 0, 1545, 358, 1, 0, 0, 0, 1546, 1547, 7, 16, 0, 0, 1547, 360, 1, 0, 0, 0, 1548, 1549, 7, 17, 0, 0, 1549, 362, 1, 0, 0, 0, 1550, 1551, 7, 18, 0, 0, 1551, 364, 1, 0, 0, 0, 1552, 1553, 7, 19, 0, 0, 1553, 366, 1, 0, 0, 0, 1554, 1555, 7, 2, 0, 0, 1555, 368, 1, 0, 0, 0, 1556, 1557, 7, 20, 0, 0, 1557, 370, 1, 0, 0, 0, 1558, 1559, 7, 21, 0, 0, 1559, 372, 1, 0, 0, 0, 1560, 1561, 7, 22, 0, 0, 1561, 374, 1, 0, 0, 0, 1562, 1563, 7, 23, 0, 0, 1563, 376, 1, 0, 0, 0, 1564, 1565, 7, 24, 0, 0, 1565, 378, 1, 0, 0, 0, 1566, 1567, 7, 25, 0, 0, 1567, 380, 1, 0, 0, 0, 1568, 1569, 7, 26, 0, 0, 1569, 382, 1, 0, 0, 0, 1570, 1571, 7, 27, 0, 0, 1571, 384, 1, 0, 0, 0, 1572, 1573, 7, 28, 0, 0, 1573, 386, 1, 0, 0, 0, 1574, 1575, 7, 29, 0, 0, 1575, 388, 1, 0, 0, 0, 1576, 1577, 7, 30, 0, 0, 1577, 390, 1, 0, 0, 0, 1578, 1579, 7, 31, 0, 0, 1579, 392, 1, 0, 0, 0, 1580, 1581, 7, 32, 0, 0, 1581, 394, 1, 0, 0, 0, 1582, 1583, 7, 33, 0, 0, 1583, 396, 1, 0, 0, 0, 1584, 1585, 7, 34, 0, 0, 1585, 398, 1, 0, 0, 0, 1586, 1587, 7, 35, 0, 0, 1587, 400, 1, 0, 0, 0, 1588, 1589, 7, 36, 0, 0, 1589, 402, 1, 0, 0, 0, 1590, 1591, 7, 37, 0, 0, 1591, 404, 1, 0, 0, 0, 1592, 1593, 7, 38, 0, 0, 1593, 406, 1, 0, 0, 0, 1594, 1595, 7, 39, 0, 0, 1595, 408, 1, 0, 0, 0, 1596, 1597, 7, 40, 0, 0, 1597, 410, 1, 0, 0, 0, 54, 0, 569, 602, 618, 627, 1240, 1246, 1258, 1262, 1264, 1268, 1280, 1285, 1287, 1291, 1300, 1305, 1312, 1317, 1323, 1332, 1339, 1343, 1365, 1370, 1375, 1377, 1385, 1387, 1399, 1403, 1407, 1421, 1425, 1429, 1443, 1452, 1459, 1463, 1468, 1470, 1477, 1482, 1484, 1488, 1494, 1499, 1501, 1507, 1509, 1513, 1521, 1533, 1541, 1, 6, 0, 0] \ No newline at end of file diff --git a/src/parser/sparqlParser/generated/SparqlAutomaticLexer.tokens b/src/parser/sparqlParser/generated/SparqlAutomaticLexer.tokens index 4d2a3fec0c..0d98dd932a 100644 --- a/src/parser/sparqlParser/generated/SparqlAutomaticLexer.tokens +++ b/src/parser/sparqlParser/generated/SparqlAutomaticLexer.tokens @@ -136,43 +136,44 @@ SUM=135 MIN=136 MAX=137 AVG=138 -SAMPLE=139 -SEPARATOR=140 -IRI_REF=141 -PNAME_NS=142 -PNAME_LN=143 -BLANK_NODE_LABEL=144 -VAR1=145 -VAR2=146 -LANGTAG=147 -PREFIX_LANGTAG=148 -INTEGER=149 -DECIMAL=150 -DOUBLE=151 -INTEGER_POSITIVE=152 -DECIMAL_POSITIVE=153 -DOUBLE_POSITIVE=154 -INTEGER_NEGATIVE=155 -DECIMAL_NEGATIVE=156 -DOUBLE_NEGATIVE=157 -EXPONENT=158 -STRING_LITERAL1=159 -STRING_LITERAL2=160 -STRING_LITERAL_LONG1=161 -STRING_LITERAL_LONG2=162 -ECHAR=163 -NIL=164 -ANON=165 -PN_CHARS_U=166 -VARNAME=167 -PN_PREFIX=168 -PN_LOCAL=169 -PLX=170 -PERCENT=171 -HEX=172 -PN_LOCAL_ESC=173 -WS=174 -COMMENTS=175 +STDEV=139 +SAMPLE=140 +SEPARATOR=141 +IRI_REF=142 +PNAME_NS=143 +PNAME_LN=144 +BLANK_NODE_LABEL=145 +VAR1=146 +VAR2=147 +LANGTAG=148 +PREFIX_LANGTAG=149 +INTEGER=150 +DECIMAL=151 +DOUBLE=152 +INTEGER_POSITIVE=153 +DECIMAL_POSITIVE=154 +DOUBLE_POSITIVE=155 +INTEGER_NEGATIVE=156 +DECIMAL_NEGATIVE=157 +DOUBLE_NEGATIVE=158 +EXPONENT=159 +STRING_LITERAL1=160 +STRING_LITERAL2=161 +STRING_LITERAL_LONG1=162 +STRING_LITERAL_LONG2=163 +ECHAR=164 +NIL=165 +ANON=166 +PN_CHARS_U=167 +VARNAME=168 +PN_PREFIX=169 +PN_LOCAL=170 +PLX=171 +PERCENT=172 +HEX=173 +PN_LOCAL_ESC=174 +WS=175 +COMMENTS=176 '*'=1 '('=2 ')'=3 diff --git a/src/parser/sparqlParser/generated/SparqlAutomaticParser.cpp b/src/parser/sparqlParser/generated/SparqlAutomaticParser.cpp index 7468aee71f..ab6318befc 100644 --- a/src/parser/sparqlParser/generated/SparqlAutomaticParser.cpp +++ b/src/parser/sparqlParser/generated/SparqlAutomaticParser.cpp @@ -348,6 +348,7 @@ void sparqlautomaticParserInitialize() { "MIN", "MAX", "AVG", + "STDEV", "SAMPLE", "SEPARATOR", "IRI_REF", @@ -386,7 +387,7 @@ void sparqlautomaticParserInitialize() { "WS", "COMMENTS"}); static const int32_t serializedATNSegment[] = { - 4, 1, 175, 1647, 2, 0, 7, 0, 2, 1, 7, 1, + 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, @@ -667,928 +668,934 @@ void sparqlautomaticParserInitialize() { 1574, 8, 142, 1, 142, 1, 142, 1, 142, 1, 142, 1, 142, 1, 142, 3, 142, 1582, 8, 142, 1, 142, 1, 142, 1, 142, 1, 142, 1, 142, 1, 142, 3, 142, 1590, 8, - 142, 1, 142, 1, 142, 1, 142, 1, 142, 1, 142, 3, - 142, 1597, 8, 142, 1, 142, 1, 142, 3, 142, 1601, 8, - 142, 1, 143, 1, 143, 3, 143, 1605, 8, 143, 1, 144, - 1, 144, 1, 144, 1, 144, 3, 144, 1611, 8, 144, 1, - 145, 1, 145, 1, 145, 3, 145, 1616, 8, 145, 1, 146, - 1, 146, 1, 147, 1, 147, 1, 148, 1, 148, 1, 149, - 1, 149, 1, 150, 1, 150, 1, 151, 3, 151, 1629, 8, - 151, 1, 151, 1, 151, 3, 151, 1633, 8, 151, 1, 152, - 1, 152, 3, 152, 1637, 8, 152, 1, 153, 1, 153, 1, - 154, 1, 154, 1, 155, 1, 155, 1, 156, 1, 156, 1, - 156, 0, 0, 157, 0, 2, 4, 6, 8, 10, 12, 14, - 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, - 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, - 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, - 88, 90, 92, 94, 96, 98, 100, 102, 104, 106, 108, 110, - 112, 114, 116, 118, 120, 122, 124, 126, 128, 130, 132, 134, - 136, 138, 140, 142, 144, 146, 148, 150, 152, 154, 156, 158, - 160, 162, 164, 166, 168, 170, 172, 174, 176, 178, 180, 182, - 184, 186, 188, 190, 192, 194, 196, 198, 200, 202, 204, 206, - 208, 210, 212, 214, 216, 218, 220, 222, 224, 226, 228, 230, - 232, 234, 236, 238, 240, 242, 244, 246, 248, 250, 252, 254, - 256, 258, 260, 262, 264, 266, 268, 270, 272, 274, 276, 278, - 280, 282, 284, 286, 288, 290, 292, 294, 296, 298, 300, 302, - 304, 306, 308, 310, 312, 0, 10, 1, 0, 33, 34, 1, - 0, 47, 48, 2, 0, 1, 1, 13, 14, 1, 0, 145, - 146, 1, 0, 149, 151, 1, 0, 152, 154, 1, 0, 155, - 157, 1, 0, 28, 29, 1, 0, 159, 162, 2, 0, 144, - 144, 165, 165, 1761, 0, 316, 1, 0, 0, 0, 2, 320, - 1, 0, 0, 0, 4, 333, 1, 0, 0, 0, 6, 336, - 1, 0, 0, 0, 8, 339, 1, 0, 0, 0, 10, 343, - 1, 0, 0, 0, 12, 353, 1, 0, 0, 0, 14, 358, - 1, 0, 0, 0, 16, 372, 1, 0, 0, 0, 18, 374, - 1, 0, 0, 0, 20, 378, 1, 0, 0, 0, 22, 382, - 1, 0, 0, 0, 24, 408, 1, 0, 0, 0, 26, 428, - 1, 0, 0, 0, 28, 438, 1, 0, 0, 0, 30, 443, - 1, 0, 0, 0, 32, 445, 1, 0, 0, 0, 34, 448, - 1, 0, 0, 0, 36, 451, 1, 0, 0, 0, 38, 456, - 1, 0, 0, 0, 40, 467, 1, 0, 0, 0, 42, 484, - 1, 0, 0, 0, 44, 486, 1, 0, 0, 0, 46, 492, - 1, 0, 0, 0, 48, 496, 1, 0, 0, 0, 50, 509, - 1, 0, 0, 0, 52, 553, 1, 0, 0, 0, 54, 555, - 1, 0, 0, 0, 56, 558, 1, 0, 0, 0, 58, 561, - 1, 0, 0, 0, 60, 566, 1, 0, 0, 0, 62, 568, - 1, 0, 0, 0, 64, 587, 1, 0, 0, 0, 66, 589, - 1, 0, 0, 0, 68, 598, 1, 0, 0, 0, 70, 604, - 1, 0, 0, 0, 72, 610, 1, 0, 0, 0, 74, 616, - 1, 0, 0, 0, 76, 624, 1, 0, 0, 0, 78, 632, - 1, 0, 0, 0, 80, 640, 1, 0, 0, 0, 82, 644, - 1, 0, 0, 0, 84, 648, 1, 0, 0, 0, 86, 654, - 1, 0, 0, 0, 88, 672, 1, 0, 0, 0, 90, 675, - 1, 0, 0, 0, 92, 678, 1, 0, 0, 0, 94, 687, - 1, 0, 0, 0, 96, 689, 1, 0, 0, 0, 98, 696, - 1, 0, 0, 0, 100, 698, 1, 0, 0, 0, 102, 702, - 1, 0, 0, 0, 104, 707, 1, 0, 0, 0, 106, 721, - 1, 0, 0, 0, 108, 729, 1, 0, 0, 0, 110, 736, - 1, 0, 0, 0, 112, 744, 1, 0, 0, 0, 114, 752, - 1, 0, 0, 0, 116, 759, 1, 0, 0, 0, 118, 774, - 1, 0, 0, 0, 120, 776, 1, 0, 0, 0, 122, 779, - 1, 0, 0, 0, 124, 783, 1, 0, 0, 0, 126, 790, - 1, 0, 0, 0, 128, 797, 1, 0, 0, 0, 130, 802, - 1, 0, 0, 0, 132, 804, 1, 0, 0, 0, 134, 823, - 1, 0, 0, 0, 136, 843, 1, 0, 0, 0, 138, 850, - 1, 0, 0, 0, 140, 852, 1, 0, 0, 0, 142, 855, - 1, 0, 0, 0, 144, 863, 1, 0, 0, 0, 146, 869, - 1, 0, 0, 0, 148, 871, 1, 0, 0, 0, 150, 889, - 1, 0, 0, 0, 152, 903, 1, 0, 0, 0, 154, 905, - 1, 0, 0, 0, 156, 911, 1, 0, 0, 0, 158, 924, - 1, 0, 0, 0, 160, 927, 1, 0, 0, 0, 162, 929, - 1, 0, 0, 0, 164, 944, 1, 0, 0, 0, 166, 946, - 1, 0, 0, 0, 168, 954, 1, 0, 0, 0, 170, 962, - 1, 0, 0, 0, 172, 965, 1, 0, 0, 0, 174, 967, - 1, 0, 0, 0, 176, 977, 1, 0, 0, 0, 178, 979, - 1, 0, 0, 0, 180, 981, 1, 0, 0, 0, 182, 984, - 1, 0, 0, 0, 184, 989, 1, 0, 0, 0, 186, 991, - 1, 0, 0, 0, 188, 999, 1, 0, 0, 0, 190, 1001, - 1, 0, 0, 0, 192, 1003, 1, 0, 0, 0, 194, 1011, - 1, 0, 0, 0, 196, 1019, 1, 0, 0, 0, 198, 1026, - 1, 0, 0, 0, 200, 1028, 1, 0, 0, 0, 202, 1038, - 1, 0, 0, 0, 204, 1053, 1, 0, 0, 0, 206, 1062, - 1, 0, 0, 0, 208, 1064, 1, 0, 0, 0, 210, 1068, - 1, 0, 0, 0, 212, 1070, 1, 0, 0, 0, 214, 1076, - 1, 0, 0, 0, 216, 1078, 1, 0, 0, 0, 218, 1082, - 1, 0, 0, 0, 220, 1090, 1, 0, 0, 0, 222, 1100, - 1, 0, 0, 0, 224, 1104, 1, 0, 0, 0, 226, 1108, - 1, 0, 0, 0, 228, 1112, 1, 0, 0, 0, 230, 1114, - 1, 0, 0, 0, 232, 1122, 1, 0, 0, 0, 234, 1124, - 1, 0, 0, 0, 236, 1126, 1, 0, 0, 0, 238, 1134, - 1, 0, 0, 0, 240, 1142, 1, 0, 0, 0, 242, 1144, - 1, 0, 0, 0, 244, 1164, 1, 0, 0, 0, 246, 1166, - 1, 0, 0, 0, 248, 1178, 1, 0, 0, 0, 250, 1180, - 1, 0, 0, 0, 252, 1182, 1, 0, 0, 0, 254, 1186, - 1, 0, 0, 0, 256, 1194, 1, 0, 0, 0, 258, 1203, - 1, 0, 0, 0, 260, 1205, 1, 0, 0, 0, 262, 1208, - 1, 0, 0, 0, 264, 1218, 1, 0, 0, 0, 266, 1227, - 1, 0, 0, 0, 268, 1229, 1, 0, 0, 0, 270, 1487, - 1, 0, 0, 0, 272, 1489, 1, 0, 0, 0, 274, 1500, - 1, 0, 0, 0, 276, 1505, 1, 0, 0, 0, 278, 1516, - 1, 0, 0, 0, 280, 1529, 1, 0, 0, 0, 282, 1532, - 1, 0, 0, 0, 284, 1600, 1, 0, 0, 0, 286, 1602, - 1, 0, 0, 0, 288, 1606, 1, 0, 0, 0, 290, 1615, - 1, 0, 0, 0, 292, 1617, 1, 0, 0, 0, 294, 1619, - 1, 0, 0, 0, 296, 1621, 1, 0, 0, 0, 298, 1623, - 1, 0, 0, 0, 300, 1625, 1, 0, 0, 0, 302, 1628, - 1, 0, 0, 0, 304, 1636, 1, 0, 0, 0, 306, 1638, - 1, 0, 0, 0, 308, 1640, 1, 0, 0, 0, 310, 1642, - 1, 0, 0, 0, 312, 1644, 1, 0, 0, 0, 314, 317, - 3, 2, 1, 0, 315, 317, 3, 62, 31, 0, 316, 314, - 1, 0, 0, 0, 316, 315, 1, 0, 0, 0, 317, 318, - 1, 0, 0, 0, 318, 319, 5, 0, 0, 1, 319, 1, - 1, 0, 0, 0, 320, 325, 3, 4, 2, 0, 321, 326, - 3, 10, 5, 0, 322, 326, 3, 22, 11, 0, 323, 326, - 3, 24, 12, 0, 324, 326, 3, 26, 13, 0, 325, 321, - 1, 0, 0, 0, 325, 322, 1, 0, 0, 0, 325, 323, - 1, 0, 0, 0, 325, 324, 1, 0, 0, 0, 326, 327, - 1, 0, 0, 0, 327, 328, 3, 60, 30, 0, 328, 3, - 1, 0, 0, 0, 329, 332, 3, 6, 3, 0, 330, 332, - 3, 8, 4, 0, 331, 329, 1, 0, 0, 0, 331, 330, - 1, 0, 0, 0, 332, 335, 1, 0, 0, 0, 333, 331, - 1, 0, 0, 0, 333, 334, 1, 0, 0, 0, 334, 5, - 1, 0, 0, 0, 335, 333, 1, 0, 0, 0, 336, 337, - 5, 30, 0, 0, 337, 338, 3, 308, 154, 0, 338, 7, - 1, 0, 0, 0, 339, 340, 5, 31, 0, 0, 340, 341, - 5, 142, 0, 0, 341, 342, 3, 308, 154, 0, 342, 9, - 1, 0, 0, 0, 343, 347, 3, 14, 7, 0, 344, 346, - 3, 28, 14, 0, 345, 344, 1, 0, 0, 0, 346, 349, - 1, 0, 0, 0, 347, 345, 1, 0, 0, 0, 347, 348, - 1, 0, 0, 0, 348, 350, 1, 0, 0, 0, 349, 347, - 1, 0, 0, 0, 350, 351, 3, 36, 18, 0, 351, 352, - 3, 38, 19, 0, 352, 11, 1, 0, 0, 0, 353, 354, - 3, 14, 7, 0, 354, 355, 3, 36, 18, 0, 355, 356, - 3, 38, 19, 0, 356, 357, 3, 60, 30, 0, 357, 13, - 1, 0, 0, 0, 358, 360, 5, 32, 0, 0, 359, 361, - 7, 0, 0, 0, 360, 359, 1, 0, 0, 0, 360, 361, - 1, 0, 0, 0, 361, 368, 1, 0, 0, 0, 362, 364, - 3, 16, 8, 0, 363, 362, 1, 0, 0, 0, 364, 365, - 1, 0, 0, 0, 365, 363, 1, 0, 0, 0, 365, 366, - 1, 0, 0, 0, 366, 369, 1, 0, 0, 0, 367, 369, - 5, 1, 0, 0, 368, 363, 1, 0, 0, 0, 368, 367, - 1, 0, 0, 0, 369, 15, 1, 0, 0, 0, 370, 373, - 3, 230, 115, 0, 371, 373, 3, 18, 9, 0, 372, 370, - 1, 0, 0, 0, 372, 371, 1, 0, 0, 0, 373, 17, - 1, 0, 0, 0, 374, 375, 5, 2, 0, 0, 375, 376, - 3, 20, 10, 0, 376, 377, 5, 3, 0, 0, 377, 19, - 1, 0, 0, 0, 378, 379, 3, 234, 117, 0, 379, 380, - 5, 35, 0, 0, 380, 381, 3, 230, 115, 0, 381, 21, - 1, 0, 0, 0, 382, 406, 5, 36, 0, 0, 383, 387, - 3, 154, 77, 0, 384, 386, 3, 28, 14, 0, 385, 384, - 1, 0, 0, 0, 386, 389, 1, 0, 0, 0, 387, 385, - 1, 0, 0, 0, 387, 388, 1, 0, 0, 0, 388, 390, - 1, 0, 0, 0, 389, 387, 1, 0, 0, 0, 390, 391, - 3, 36, 18, 0, 391, 392, 3, 38, 19, 0, 392, 407, - 1, 0, 0, 0, 393, 395, 3, 28, 14, 0, 394, 393, - 1, 0, 0, 0, 395, 398, 1, 0, 0, 0, 396, 394, - 1, 0, 0, 0, 396, 397, 1, 0, 0, 0, 397, 399, - 1, 0, 0, 0, 398, 396, 1, 0, 0, 0, 399, 400, - 5, 37, 0, 0, 400, 402, 5, 4, 0, 0, 401, 403, - 3, 108, 54, 0, 402, 401, 1, 0, 0, 0, 402, 403, - 1, 0, 0, 0, 403, 404, 1, 0, 0, 0, 404, 405, - 5, 5, 0, 0, 405, 407, 3, 38, 19, 0, 406, 383, - 1, 0, 0, 0, 406, 396, 1, 0, 0, 0, 407, 23, - 1, 0, 0, 0, 408, 415, 5, 38, 0, 0, 409, 411, - 3, 228, 114, 0, 410, 409, 1, 0, 0, 0, 411, 412, - 1, 0, 0, 0, 412, 410, 1, 0, 0, 0, 412, 413, - 1, 0, 0, 0, 413, 416, 1, 0, 0, 0, 414, 416, - 5, 1, 0, 0, 415, 410, 1, 0, 0, 0, 415, 414, - 1, 0, 0, 0, 416, 420, 1, 0, 0, 0, 417, 419, - 3, 28, 14, 0, 418, 417, 1, 0, 0, 0, 419, 422, - 1, 0, 0, 0, 420, 418, 1, 0, 0, 0, 420, 421, - 1, 0, 0, 0, 421, 424, 1, 0, 0, 0, 422, 420, - 1, 0, 0, 0, 423, 425, 3, 36, 18, 0, 424, 423, - 1, 0, 0, 0, 424, 425, 1, 0, 0, 0, 425, 426, - 1, 0, 0, 0, 426, 427, 3, 38, 19, 0, 427, 25, - 1, 0, 0, 0, 428, 432, 5, 39, 0, 0, 429, 431, - 3, 28, 14, 0, 430, 429, 1, 0, 0, 0, 431, 434, - 1, 0, 0, 0, 432, 430, 1, 0, 0, 0, 432, 433, - 1, 0, 0, 0, 433, 435, 1, 0, 0, 0, 434, 432, - 1, 0, 0, 0, 435, 436, 3, 36, 18, 0, 436, 437, - 3, 38, 19, 0, 437, 27, 1, 0, 0, 0, 438, 441, - 5, 40, 0, 0, 439, 442, 3, 30, 15, 0, 440, 442, - 3, 32, 16, 0, 441, 439, 1, 0, 0, 0, 441, 440, - 1, 0, 0, 0, 442, 29, 1, 0, 0, 0, 443, 444, - 3, 34, 17, 0, 444, 31, 1, 0, 0, 0, 445, 446, - 5, 41, 0, 0, 446, 447, 3, 34, 17, 0, 447, 33, - 1, 0, 0, 0, 448, 449, 3, 302, 151, 0, 449, 35, - 1, 0, 0, 0, 450, 452, 5, 37, 0, 0, 451, 450, - 1, 0, 0, 0, 451, 452, 1, 0, 0, 0, 452, 453, - 1, 0, 0, 0, 453, 454, 3, 110, 55, 0, 454, 37, - 1, 0, 0, 0, 455, 457, 3, 40, 20, 0, 456, 455, - 1, 0, 0, 0, 456, 457, 1, 0, 0, 0, 457, 459, - 1, 0, 0, 0, 458, 460, 3, 44, 22, 0, 459, 458, - 1, 0, 0, 0, 459, 460, 1, 0, 0, 0, 460, 462, - 1, 0, 0, 0, 461, 463, 3, 48, 24, 0, 462, 461, - 1, 0, 0, 0, 462, 463, 1, 0, 0, 0, 463, 465, - 1, 0, 0, 0, 464, 466, 3, 52, 26, 0, 465, 464, - 1, 0, 0, 0, 465, 466, 1, 0, 0, 0, 466, 39, - 1, 0, 0, 0, 467, 469, 5, 42, 0, 0, 468, 470, - 3, 42, 21, 0, 469, 468, 1, 0, 0, 0, 470, 471, - 1, 0, 0, 0, 471, 469, 1, 0, 0, 0, 471, 472, - 1, 0, 0, 0, 472, 41, 1, 0, 0, 0, 473, 485, - 3, 270, 135, 0, 474, 485, 3, 148, 74, 0, 475, 476, - 5, 2, 0, 0, 476, 479, 3, 234, 117, 0, 477, 478, - 5, 35, 0, 0, 478, 480, 3, 230, 115, 0, 479, 477, - 1, 0, 0, 0, 479, 480, 1, 0, 0, 0, 480, 481, - 1, 0, 0, 0, 481, 482, 5, 3, 0, 0, 482, 485, - 1, 0, 0, 0, 483, 485, 3, 230, 115, 0, 484, 473, - 1, 0, 0, 0, 484, 474, 1, 0, 0, 0, 484, 475, - 1, 0, 0, 0, 484, 483, 1, 0, 0, 0, 485, 43, - 1, 0, 0, 0, 486, 488, 5, 44, 0, 0, 487, 489, - 3, 46, 23, 0, 488, 487, 1, 0, 0, 0, 489, 490, - 1, 0, 0, 0, 490, 488, 1, 0, 0, 0, 490, 491, - 1, 0, 0, 0, 491, 45, 1, 0, 0, 0, 492, 493, - 3, 146, 73, 0, 493, 47, 1, 0, 0, 0, 494, 497, - 5, 45, 0, 0, 495, 497, 5, 46, 0, 0, 496, 494, - 1, 0, 0, 0, 496, 495, 1, 0, 0, 0, 497, 499, - 1, 0, 0, 0, 498, 500, 3, 50, 25, 0, 499, 498, - 1, 0, 0, 0, 500, 501, 1, 0, 0, 0, 501, 499, - 1, 0, 0, 0, 501, 502, 1, 0, 0, 0, 502, 49, - 1, 0, 0, 0, 503, 504, 7, 1, 0, 0, 504, 510, - 3, 268, 134, 0, 505, 508, 3, 146, 73, 0, 506, 508, - 3, 230, 115, 0, 507, 505, 1, 0, 0, 0, 507, 506, - 1, 0, 0, 0, 508, 510, 1, 0, 0, 0, 509, 503, - 1, 0, 0, 0, 509, 507, 1, 0, 0, 0, 510, 51, - 1, 0, 0, 0, 511, 513, 3, 54, 27, 0, 512, 514, - 3, 56, 28, 0, 513, 512, 1, 0, 0, 0, 513, 514, - 1, 0, 0, 0, 514, 516, 1, 0, 0, 0, 515, 517, - 3, 58, 29, 0, 516, 515, 1, 0, 0, 0, 516, 517, - 1, 0, 0, 0, 517, 554, 1, 0, 0, 0, 518, 520, - 3, 54, 27, 0, 519, 521, 3, 58, 29, 0, 520, 519, - 1, 0, 0, 0, 520, 521, 1, 0, 0, 0, 521, 523, - 1, 0, 0, 0, 522, 524, 3, 56, 28, 0, 523, 522, - 1, 0, 0, 0, 523, 524, 1, 0, 0, 0, 524, 554, - 1, 0, 0, 0, 525, 527, 3, 56, 28, 0, 526, 528, - 3, 54, 27, 0, 527, 526, 1, 0, 0, 0, 527, 528, - 1, 0, 0, 0, 528, 530, 1, 0, 0, 0, 529, 531, - 3, 58, 29, 0, 530, 529, 1, 0, 0, 0, 530, 531, - 1, 0, 0, 0, 531, 554, 1, 0, 0, 0, 532, 534, - 3, 56, 28, 0, 533, 535, 3, 58, 29, 0, 534, 533, - 1, 0, 0, 0, 534, 535, 1, 0, 0, 0, 535, 537, - 1, 0, 0, 0, 536, 538, 3, 54, 27, 0, 537, 536, - 1, 0, 0, 0, 537, 538, 1, 0, 0, 0, 538, 554, - 1, 0, 0, 0, 539, 541, 3, 58, 29, 0, 540, 542, - 3, 56, 28, 0, 541, 540, 1, 0, 0, 0, 541, 542, - 1, 0, 0, 0, 542, 544, 1, 0, 0, 0, 543, 545, - 3, 54, 27, 0, 544, 543, 1, 0, 0, 0, 544, 545, - 1, 0, 0, 0, 545, 554, 1, 0, 0, 0, 546, 548, - 3, 58, 29, 0, 547, 549, 3, 54, 27, 0, 548, 547, - 1, 0, 0, 0, 548, 549, 1, 0, 0, 0, 549, 551, - 1, 0, 0, 0, 550, 552, 3, 56, 28, 0, 551, 550, - 1, 0, 0, 0, 551, 552, 1, 0, 0, 0, 552, 554, - 1, 0, 0, 0, 553, 511, 1, 0, 0, 0, 553, 518, - 1, 0, 0, 0, 553, 525, 1, 0, 0, 0, 553, 532, - 1, 0, 0, 0, 553, 539, 1, 0, 0, 0, 553, 546, - 1, 0, 0, 0, 554, 53, 1, 0, 0, 0, 555, 556, - 5, 49, 0, 0, 556, 557, 3, 208, 104, 0, 557, 55, - 1, 0, 0, 0, 558, 559, 5, 50, 0, 0, 559, 560, - 3, 208, 104, 0, 560, 57, 1, 0, 0, 0, 561, 562, - 5, 51, 0, 0, 562, 563, 3, 208, 104, 0, 563, 59, - 1, 0, 0, 0, 564, 565, 5, 52, 0, 0, 565, 567, - 3, 130, 65, 0, 566, 564, 1, 0, 0, 0, 566, 567, - 1, 0, 0, 0, 567, 61, 1, 0, 0, 0, 568, 574, - 3, 4, 2, 0, 569, 572, 3, 64, 32, 0, 570, 571, - 5, 6, 0, 0, 571, 573, 3, 62, 31, 0, 572, 570, - 1, 0, 0, 0, 572, 573, 1, 0, 0, 0, 573, 575, - 1, 0, 0, 0, 574, 569, 1, 0, 0, 0, 574, 575, - 1, 0, 0, 0, 575, 63, 1, 0, 0, 0, 576, 588, - 3, 66, 33, 0, 577, 588, 3, 68, 34, 0, 578, 588, - 3, 70, 35, 0, 579, 588, 3, 74, 37, 0, 580, 588, - 3, 76, 38, 0, 581, 588, 3, 78, 39, 0, 582, 588, - 3, 72, 36, 0, 583, 588, 3, 80, 40, 0, 584, 588, - 3, 82, 41, 0, 585, 588, 3, 84, 42, 0, 586, 588, - 3, 86, 43, 0, 587, 576, 1, 0, 0, 0, 587, 577, - 1, 0, 0, 0, 587, 578, 1, 0, 0, 0, 587, 579, - 1, 0, 0, 0, 587, 580, 1, 0, 0, 0, 587, 581, - 1, 0, 0, 0, 587, 582, 1, 0, 0, 0, 587, 583, - 1, 0, 0, 0, 587, 584, 1, 0, 0, 0, 587, 585, - 1, 0, 0, 0, 587, 586, 1, 0, 0, 0, 588, 65, - 1, 0, 0, 0, 589, 591, 5, 53, 0, 0, 590, 592, - 5, 54, 0, 0, 591, 590, 1, 0, 0, 0, 591, 592, - 1, 0, 0, 0, 592, 593, 1, 0, 0, 0, 593, 596, - 3, 302, 151, 0, 594, 595, 5, 55, 0, 0, 595, 597, - 3, 96, 48, 0, 596, 594, 1, 0, 0, 0, 596, 597, - 1, 0, 0, 0, 597, 67, 1, 0, 0, 0, 598, 600, - 5, 56, 0, 0, 599, 601, 5, 54, 0, 0, 600, 599, - 1, 0, 0, 0, 600, 601, 1, 0, 0, 0, 601, 602, - 1, 0, 0, 0, 602, 603, 3, 98, 49, 0, 603, 69, - 1, 0, 0, 0, 604, 606, 5, 57, 0, 0, 605, 607, - 5, 54, 0, 0, 606, 605, 1, 0, 0, 0, 606, 607, - 1, 0, 0, 0, 607, 608, 1, 0, 0, 0, 608, 609, - 3, 98, 49, 0, 609, 71, 1, 0, 0, 0, 610, 612, - 5, 58, 0, 0, 611, 613, 5, 54, 0, 0, 612, 611, - 1, 0, 0, 0, 612, 613, 1, 0, 0, 0, 613, 614, - 1, 0, 0, 0, 614, 615, 3, 96, 48, 0, 615, 73, - 1, 0, 0, 0, 616, 618, 5, 59, 0, 0, 617, 619, - 5, 54, 0, 0, 618, 617, 1, 0, 0, 0, 618, 619, - 1, 0, 0, 0, 619, 620, 1, 0, 0, 0, 620, 621, - 3, 94, 47, 0, 621, 622, 5, 60, 0, 0, 622, 623, - 3, 94, 47, 0, 623, 75, 1, 0, 0, 0, 624, 626, - 5, 62, 0, 0, 625, 627, 5, 54, 0, 0, 626, 625, - 1, 0, 0, 0, 626, 627, 1, 0, 0, 0, 627, 628, - 1, 0, 0, 0, 628, 629, 3, 94, 47, 0, 629, 630, - 5, 60, 0, 0, 630, 631, 3, 94, 47, 0, 631, 77, - 1, 0, 0, 0, 632, 634, 5, 63, 0, 0, 633, 635, - 5, 54, 0, 0, 634, 633, 1, 0, 0, 0, 634, 635, - 1, 0, 0, 0, 635, 636, 1, 0, 0, 0, 636, 637, - 3, 94, 47, 0, 637, 638, 5, 60, 0, 0, 638, 639, - 3, 94, 47, 0, 639, 79, 1, 0, 0, 0, 640, 641, - 5, 64, 0, 0, 641, 642, 5, 61, 0, 0, 642, 643, - 3, 102, 51, 0, 643, 81, 1, 0, 0, 0, 644, 645, - 5, 65, 0, 0, 645, 646, 5, 61, 0, 0, 646, 647, - 3, 102, 51, 0, 647, 83, 1, 0, 0, 0, 648, 649, - 5, 65, 0, 0, 649, 650, 5, 37, 0, 0, 650, 651, - 3, 100, 50, 0, 651, 85, 1, 0, 0, 0, 652, 653, - 5, 66, 0, 0, 653, 655, 3, 302, 151, 0, 654, 652, - 1, 0, 0, 0, 654, 655, 1, 0, 0, 0, 655, 661, - 1, 0, 0, 0, 656, 658, 3, 88, 44, 0, 657, 659, - 3, 90, 45, 0, 658, 657, 1, 0, 0, 0, 658, 659, - 1, 0, 0, 0, 659, 662, 1, 0, 0, 0, 660, 662, - 3, 90, 45, 0, 661, 656, 1, 0, 0, 0, 661, 660, - 1, 0, 0, 0, 662, 666, 1, 0, 0, 0, 663, 665, - 3, 92, 46, 0, 664, 663, 1, 0, 0, 0, 665, 668, - 1, 0, 0, 0, 666, 664, 1, 0, 0, 0, 666, 667, - 1, 0, 0, 0, 667, 669, 1, 0, 0, 0, 668, 666, - 1, 0, 0, 0, 669, 670, 5, 37, 0, 0, 670, 671, - 3, 110, 55, 0, 671, 87, 1, 0, 0, 0, 672, 673, - 5, 65, 0, 0, 673, 674, 3, 100, 50, 0, 674, 89, - 1, 0, 0, 0, 675, 676, 5, 64, 0, 0, 676, 677, - 3, 100, 50, 0, 677, 91, 1, 0, 0, 0, 678, 682, - 5, 67, 0, 0, 679, 683, 3, 302, 151, 0, 680, 681, - 5, 41, 0, 0, 681, 683, 3, 302, 151, 0, 682, 679, - 1, 0, 0, 0, 682, 680, 1, 0, 0, 0, 683, 93, - 1, 0, 0, 0, 684, 688, 5, 68, 0, 0, 685, 686, - 5, 69, 0, 0, 686, 688, 3, 302, 151, 0, 687, 684, - 1, 0, 0, 0, 687, 685, 1, 0, 0, 0, 688, 95, - 1, 0, 0, 0, 689, 690, 5, 69, 0, 0, 690, 691, - 3, 302, 151, 0, 691, 97, 1, 0, 0, 0, 692, 697, - 3, 96, 48, 0, 693, 697, 5, 68, 0, 0, 694, 697, - 5, 41, 0, 0, 695, 697, 5, 70, 0, 0, 696, 692, - 1, 0, 0, 0, 696, 693, 1, 0, 0, 0, 696, 694, - 1, 0, 0, 0, 696, 695, 1, 0, 0, 0, 697, 99, - 1, 0, 0, 0, 698, 699, 5, 4, 0, 0, 699, 700, - 3, 104, 52, 0, 700, 701, 5, 5, 0, 0, 701, 101, - 1, 0, 0, 0, 702, 703, 5, 4, 0, 0, 703, 704, - 3, 104, 52, 0, 704, 705, 5, 5, 0, 0, 705, 103, - 1, 0, 0, 0, 706, 708, 3, 108, 54, 0, 707, 706, - 1, 0, 0, 0, 707, 708, 1, 0, 0, 0, 708, 718, - 1, 0, 0, 0, 709, 711, 3, 106, 53, 0, 710, 712, - 5, 7, 0, 0, 711, 710, 1, 0, 0, 0, 711, 712, - 1, 0, 0, 0, 712, 714, 1, 0, 0, 0, 713, 715, - 3, 108, 54, 0, 714, 713, 1, 0, 0, 0, 714, 715, - 1, 0, 0, 0, 715, 717, 1, 0, 0, 0, 716, 709, - 1, 0, 0, 0, 717, 720, 1, 0, 0, 0, 718, 716, - 1, 0, 0, 0, 718, 719, 1, 0, 0, 0, 719, 105, - 1, 0, 0, 0, 720, 718, 1, 0, 0, 0, 721, 722, - 5, 69, 0, 0, 722, 723, 3, 228, 114, 0, 723, 725, - 5, 4, 0, 0, 724, 726, 3, 108, 54, 0, 725, 724, - 1, 0, 0, 0, 725, 726, 1, 0, 0, 0, 726, 727, - 1, 0, 0, 0, 727, 728, 5, 5, 0, 0, 728, 107, - 1, 0, 0, 0, 729, 734, 3, 158, 79, 0, 730, 732, - 5, 7, 0, 0, 731, 733, 3, 108, 54, 0, 732, 731, - 1, 0, 0, 0, 732, 733, 1, 0, 0, 0, 733, 735, - 1, 0, 0, 0, 734, 730, 1, 0, 0, 0, 734, 735, - 1, 0, 0, 0, 735, 109, 1, 0, 0, 0, 736, 739, - 5, 4, 0, 0, 737, 740, 3, 12, 6, 0, 738, 740, - 3, 112, 56, 0, 739, 737, 1, 0, 0, 0, 739, 738, - 1, 0, 0, 0, 740, 741, 1, 0, 0, 0, 741, 742, - 5, 5, 0, 0, 742, 111, 1, 0, 0, 0, 743, 745, - 3, 116, 58, 0, 744, 743, 1, 0, 0, 0, 744, 745, - 1, 0, 0, 0, 745, 749, 1, 0, 0, 0, 746, 748, - 3, 114, 57, 0, 747, 746, 1, 0, 0, 0, 748, 751, - 1, 0, 0, 0, 749, 747, 1, 0, 0, 0, 749, 750, - 1, 0, 0, 0, 750, 113, 1, 0, 0, 0, 751, 749, - 1, 0, 0, 0, 752, 754, 3, 118, 59, 0, 753, 755, - 5, 7, 0, 0, 754, 753, 1, 0, 0, 0, 754, 755, - 1, 0, 0, 0, 755, 757, 1, 0, 0, 0, 756, 758, - 3, 116, 58, 0, 757, 756, 1, 0, 0, 0, 757, 758, - 1, 0, 0, 0, 758, 115, 1, 0, 0, 0, 759, 764, - 3, 170, 85, 0, 760, 762, 5, 7, 0, 0, 761, 763, - 3, 116, 58, 0, 762, 761, 1, 0, 0, 0, 762, 763, - 1, 0, 0, 0, 763, 765, 1, 0, 0, 0, 764, 760, - 1, 0, 0, 0, 764, 765, 1, 0, 0, 0, 765, 117, - 1, 0, 0, 0, 766, 775, 3, 142, 71, 0, 767, 775, - 3, 120, 60, 0, 768, 775, 3, 140, 70, 0, 769, 775, - 3, 122, 61, 0, 770, 775, 3, 124, 62, 0, 771, 775, - 3, 144, 72, 0, 772, 775, 3, 126, 63, 0, 773, 775, - 3, 128, 64, 0, 774, 766, 1, 0, 0, 0, 774, 767, - 1, 0, 0, 0, 774, 768, 1, 0, 0, 0, 774, 769, - 1, 0, 0, 0, 774, 770, 1, 0, 0, 0, 774, 771, - 1, 0, 0, 0, 774, 772, 1, 0, 0, 0, 774, 773, - 1, 0, 0, 0, 775, 119, 1, 0, 0, 0, 776, 777, - 5, 71, 0, 0, 777, 778, 3, 110, 55, 0, 778, 121, - 1, 0, 0, 0, 779, 780, 5, 69, 0, 0, 780, 781, - 3, 228, 114, 0, 781, 782, 3, 110, 55, 0, 782, 123, - 1, 0, 0, 0, 783, 785, 5, 72, 0, 0, 784, 786, - 5, 54, 0, 0, 785, 784, 1, 0, 0, 0, 785, 786, - 1, 0, 0, 0, 786, 787, 1, 0, 0, 0, 787, 788, - 3, 228, 114, 0, 788, 789, 3, 110, 55, 0, 789, 125, - 1, 0, 0, 0, 790, 791, 5, 73, 0, 0, 791, 792, - 5, 2, 0, 0, 792, 793, 3, 234, 117, 0, 793, 794, - 5, 35, 0, 0, 794, 795, 3, 230, 115, 0, 795, 796, - 5, 3, 0, 0, 796, 127, 1, 0, 0, 0, 797, 798, - 5, 52, 0, 0, 798, 799, 3, 130, 65, 0, 799, 129, - 1, 0, 0, 0, 800, 803, 3, 132, 66, 0, 801, 803, - 3, 134, 67, 0, 802, 800, 1, 0, 0, 0, 802, 801, - 1, 0, 0, 0, 803, 131, 1, 0, 0, 0, 804, 805, - 3, 230, 115, 0, 805, 809, 5, 4, 0, 0, 806, 808, - 3, 138, 69, 0, 807, 806, 1, 0, 0, 0, 808, 811, - 1, 0, 0, 0, 809, 807, 1, 0, 0, 0, 809, 810, - 1, 0, 0, 0, 810, 812, 1, 0, 0, 0, 811, 809, - 1, 0, 0, 0, 812, 813, 5, 5, 0, 0, 813, 133, - 1, 0, 0, 0, 814, 824, 5, 164, 0, 0, 815, 819, - 5, 2, 0, 0, 816, 818, 3, 230, 115, 0, 817, 816, - 1, 0, 0, 0, 818, 821, 1, 0, 0, 0, 819, 817, - 1, 0, 0, 0, 819, 820, 1, 0, 0, 0, 820, 822, - 1, 0, 0, 0, 821, 819, 1, 0, 0, 0, 822, 824, - 5, 3, 0, 0, 823, 814, 1, 0, 0, 0, 823, 815, - 1, 0, 0, 0, 824, 825, 1, 0, 0, 0, 825, 829, - 5, 4, 0, 0, 826, 828, 3, 136, 68, 0, 827, 826, - 1, 0, 0, 0, 828, 831, 1, 0, 0, 0, 829, 827, - 1, 0, 0, 0, 829, 830, 1, 0, 0, 0, 830, 832, - 1, 0, 0, 0, 831, 829, 1, 0, 0, 0, 832, 833, - 5, 5, 0, 0, 833, 135, 1, 0, 0, 0, 834, 838, - 5, 2, 0, 0, 835, 837, 3, 138, 69, 0, 836, 835, - 1, 0, 0, 0, 837, 840, 1, 0, 0, 0, 838, 836, - 1, 0, 0, 0, 838, 839, 1, 0, 0, 0, 839, 841, - 1, 0, 0, 0, 840, 838, 1, 0, 0, 0, 841, 844, - 5, 3, 0, 0, 842, 844, 5, 164, 0, 0, 843, 834, - 1, 0, 0, 0, 843, 842, 1, 0, 0, 0, 844, 137, - 1, 0, 0, 0, 845, 851, 3, 302, 151, 0, 846, 851, - 3, 288, 144, 0, 847, 851, 3, 290, 145, 0, 848, 851, - 3, 298, 149, 0, 849, 851, 5, 74, 0, 0, 850, 845, - 1, 0, 0, 0, 850, 846, 1, 0, 0, 0, 850, 847, - 1, 0, 0, 0, 850, 848, 1, 0, 0, 0, 850, 849, - 1, 0, 0, 0, 851, 139, 1, 0, 0, 0, 852, 853, - 5, 75, 0, 0, 853, 854, 3, 110, 55, 0, 854, 141, - 1, 0, 0, 0, 855, 860, 3, 110, 55, 0, 856, 857, - 5, 76, 0, 0, 857, 859, 3, 110, 55, 0, 858, 856, - 1, 0, 0, 0, 859, 862, 1, 0, 0, 0, 860, 858, - 1, 0, 0, 0, 860, 861, 1, 0, 0, 0, 861, 143, - 1, 0, 0, 0, 862, 860, 1, 0, 0, 0, 863, 864, - 5, 77, 0, 0, 864, 865, 3, 146, 73, 0, 865, 145, - 1, 0, 0, 0, 866, 870, 3, 268, 134, 0, 867, 870, - 3, 270, 135, 0, 868, 870, 3, 148, 74, 0, 869, 866, - 1, 0, 0, 0, 869, 867, 1, 0, 0, 0, 869, 868, - 1, 0, 0, 0, 870, 147, 1, 0, 0, 0, 871, 872, - 3, 302, 151, 0, 872, 873, 3, 150, 75, 0, 873, 149, - 1, 0, 0, 0, 874, 890, 5, 164, 0, 0, 875, 877, - 5, 2, 0, 0, 876, 878, 5, 33, 0, 0, 877, 876, - 1, 0, 0, 0, 877, 878, 1, 0, 0, 0, 878, 879, - 1, 0, 0, 0, 879, 884, 3, 234, 117, 0, 880, 881, - 5, 8, 0, 0, 881, 883, 3, 234, 117, 0, 882, 880, - 1, 0, 0, 0, 883, 886, 1, 0, 0, 0, 884, 882, - 1, 0, 0, 0, 884, 885, 1, 0, 0, 0, 885, 887, - 1, 0, 0, 0, 886, 884, 1, 0, 0, 0, 887, 888, - 5, 3, 0, 0, 888, 890, 1, 0, 0, 0, 889, 874, - 1, 0, 0, 0, 889, 875, 1, 0, 0, 0, 890, 151, - 1, 0, 0, 0, 891, 904, 5, 164, 0, 0, 892, 893, - 5, 2, 0, 0, 893, 898, 3, 234, 117, 0, 894, 895, - 5, 8, 0, 0, 895, 897, 3, 234, 117, 0, 896, 894, - 1, 0, 0, 0, 897, 900, 1, 0, 0, 0, 898, 896, - 1, 0, 0, 0, 898, 899, 1, 0, 0, 0, 899, 901, - 1, 0, 0, 0, 900, 898, 1, 0, 0, 0, 901, 902, - 5, 3, 0, 0, 902, 904, 1, 0, 0, 0, 903, 891, - 1, 0, 0, 0, 903, 892, 1, 0, 0, 0, 904, 153, - 1, 0, 0, 0, 905, 907, 5, 4, 0, 0, 906, 908, - 3, 156, 78, 0, 907, 906, 1, 0, 0, 0, 907, 908, - 1, 0, 0, 0, 908, 909, 1, 0, 0, 0, 909, 910, - 5, 5, 0, 0, 910, 155, 1, 0, 0, 0, 911, 916, - 3, 158, 79, 0, 912, 914, 5, 7, 0, 0, 913, 915, - 3, 156, 78, 0, 914, 913, 1, 0, 0, 0, 914, 915, - 1, 0, 0, 0, 915, 917, 1, 0, 0, 0, 916, 912, - 1, 0, 0, 0, 916, 917, 1, 0, 0, 0, 917, 157, - 1, 0, 0, 0, 918, 919, 3, 226, 113, 0, 919, 920, - 3, 162, 81, 0, 920, 925, 1, 0, 0, 0, 921, 922, - 3, 210, 105, 0, 922, 923, 3, 160, 80, 0, 923, 925, - 1, 0, 0, 0, 924, 918, 1, 0, 0, 0, 924, 921, - 1, 0, 0, 0, 925, 159, 1, 0, 0, 0, 926, 928, - 3, 162, 81, 0, 927, 926, 1, 0, 0, 0, 927, 928, - 1, 0, 0, 0, 928, 161, 1, 0, 0, 0, 929, 930, - 3, 164, 82, 0, 930, 939, 3, 166, 83, 0, 931, 935, - 5, 6, 0, 0, 932, 933, 3, 164, 82, 0, 933, 934, - 3, 166, 83, 0, 934, 936, 1, 0, 0, 0, 935, 932, - 1, 0, 0, 0, 935, 936, 1, 0, 0, 0, 936, 938, - 1, 0, 0, 0, 937, 931, 1, 0, 0, 0, 938, 941, - 1, 0, 0, 0, 939, 937, 1, 0, 0, 0, 939, 940, - 1, 0, 0, 0, 940, 163, 1, 0, 0, 0, 941, 939, - 1, 0, 0, 0, 942, 945, 3, 228, 114, 0, 943, 945, - 5, 9, 0, 0, 944, 942, 1, 0, 0, 0, 944, 943, - 1, 0, 0, 0, 945, 165, 1, 0, 0, 0, 946, 951, - 3, 168, 84, 0, 947, 948, 5, 8, 0, 0, 948, 950, - 3, 168, 84, 0, 949, 947, 1, 0, 0, 0, 950, 953, - 1, 0, 0, 0, 951, 949, 1, 0, 0, 0, 951, 952, - 1, 0, 0, 0, 952, 167, 1, 0, 0, 0, 953, 951, - 1, 0, 0, 0, 954, 955, 3, 222, 111, 0, 955, 169, - 1, 0, 0, 0, 956, 957, 3, 226, 113, 0, 957, 958, - 3, 174, 87, 0, 958, 963, 1, 0, 0, 0, 959, 960, - 3, 214, 107, 0, 960, 961, 3, 172, 86, 0, 961, 963, - 1, 0, 0, 0, 962, 956, 1, 0, 0, 0, 962, 959, - 1, 0, 0, 0, 963, 171, 1, 0, 0, 0, 964, 966, - 3, 174, 87, 0, 965, 964, 1, 0, 0, 0, 965, 966, - 1, 0, 0, 0, 966, 173, 1, 0, 0, 0, 967, 974, - 3, 182, 91, 0, 968, 970, 5, 6, 0, 0, 969, 971, - 3, 180, 90, 0, 970, 969, 1, 0, 0, 0, 970, 971, - 1, 0, 0, 0, 971, 973, 1, 0, 0, 0, 972, 968, - 1, 0, 0, 0, 973, 976, 1, 0, 0, 0, 974, 972, - 1, 0, 0, 0, 974, 975, 1, 0, 0, 0, 975, 175, - 1, 0, 0, 0, 976, 974, 1, 0, 0, 0, 977, 978, - 3, 190, 95, 0, 978, 177, 1, 0, 0, 0, 979, 980, - 3, 230, 115, 0, 980, 179, 1, 0, 0, 0, 981, 982, - 3, 184, 92, 0, 982, 983, 3, 166, 83, 0, 983, 181, - 1, 0, 0, 0, 984, 985, 3, 184, 92, 0, 985, 986, - 3, 186, 93, 0, 986, 183, 1, 0, 0, 0, 987, 990, - 3, 176, 88, 0, 988, 990, 3, 178, 89, 0, 989, 987, - 1, 0, 0, 0, 989, 988, 1, 0, 0, 0, 990, 185, - 1, 0, 0, 0, 991, 996, 3, 188, 94, 0, 992, 993, - 5, 8, 0, 0, 993, 995, 3, 188, 94, 0, 994, 992, - 1, 0, 0, 0, 995, 998, 1, 0, 0, 0, 996, 994, - 1, 0, 0, 0, 996, 997, 1, 0, 0, 0, 997, 187, - 1, 0, 0, 0, 998, 996, 1, 0, 0, 0, 999, 1000, - 3, 224, 112, 0, 1000, 189, 1, 0, 0, 0, 1001, 1002, - 3, 192, 96, 0, 1002, 191, 1, 0, 0, 0, 1003, 1008, - 3, 194, 97, 0, 1004, 1005, 5, 10, 0, 0, 1005, 1007, - 3, 194, 97, 0, 1006, 1004, 1, 0, 0, 0, 1007, 1010, - 1, 0, 0, 0, 1008, 1006, 1, 0, 0, 0, 1008, 1009, - 1, 0, 0, 0, 1009, 193, 1, 0, 0, 0, 1010, 1008, - 1, 0, 0, 0, 1011, 1016, 3, 198, 99, 0, 1012, 1013, - 5, 11, 0, 0, 1013, 1015, 3, 198, 99, 0, 1014, 1012, - 1, 0, 0, 0, 1015, 1018, 1, 0, 0, 0, 1016, 1014, - 1, 0, 0, 0, 1016, 1017, 1, 0, 0, 0, 1017, 195, - 1, 0, 0, 0, 1018, 1016, 1, 0, 0, 0, 1019, 1021, - 3, 202, 101, 0, 1020, 1022, 3, 200, 100, 0, 1021, 1020, - 1, 0, 0, 0, 1021, 1022, 1, 0, 0, 0, 1022, 197, - 1, 0, 0, 0, 1023, 1027, 3, 196, 98, 0, 1024, 1025, - 5, 12, 0, 0, 1025, 1027, 3, 196, 98, 0, 1026, 1023, - 1, 0, 0, 0, 1026, 1024, 1, 0, 0, 0, 1027, 199, - 1, 0, 0, 0, 1028, 1029, 7, 2, 0, 0, 1029, 201, - 1, 0, 0, 0, 1030, 1039, 3, 302, 151, 0, 1031, 1039, - 5, 9, 0, 0, 1032, 1033, 5, 15, 0, 0, 1033, 1039, - 3, 204, 102, 0, 1034, 1035, 5, 2, 0, 0, 1035, 1036, - 3, 190, 95, 0, 1036, 1037, 5, 3, 0, 0, 1037, 1039, - 1, 0, 0, 0, 1038, 1030, 1, 0, 0, 0, 1038, 1031, - 1, 0, 0, 0, 1038, 1032, 1, 0, 0, 0, 1038, 1034, - 1, 0, 0, 0, 1039, 203, 1, 0, 0, 0, 1040, 1054, - 3, 206, 103, 0, 1041, 1050, 5, 2, 0, 0, 1042, 1047, - 3, 206, 103, 0, 1043, 1044, 5, 10, 0, 0, 1044, 1046, - 3, 206, 103, 0, 1045, 1043, 1, 0, 0, 0, 1046, 1049, - 1, 0, 0, 0, 1047, 1045, 1, 0, 0, 0, 1047, 1048, - 1, 0, 0, 0, 1048, 1051, 1, 0, 0, 0, 1049, 1047, - 1, 0, 0, 0, 1050, 1042, 1, 0, 0, 0, 1050, 1051, - 1, 0, 0, 0, 1051, 1052, 1, 0, 0, 0, 1052, 1054, - 5, 3, 0, 0, 1053, 1040, 1, 0, 0, 0, 1053, 1041, - 1, 0, 0, 0, 1054, 205, 1, 0, 0, 0, 1055, 1063, - 3, 302, 151, 0, 1056, 1063, 5, 9, 0, 0, 1057, 1060, - 5, 12, 0, 0, 1058, 1061, 3, 302, 151, 0, 1059, 1061, - 5, 9, 0, 0, 1060, 1058, 1, 0, 0, 0, 1060, 1059, - 1, 0, 0, 0, 1061, 1063, 1, 0, 0, 0, 1062, 1055, - 1, 0, 0, 0, 1062, 1056, 1, 0, 0, 0, 1062, 1057, - 1, 0, 0, 0, 1063, 207, 1, 0, 0, 0, 1064, 1065, - 5, 149, 0, 0, 1065, 209, 1, 0, 0, 0, 1066, 1069, - 3, 218, 109, 0, 1067, 1069, 3, 212, 106, 0, 1068, 1066, - 1, 0, 0, 0, 1068, 1067, 1, 0, 0, 0, 1069, 211, - 1, 0, 0, 0, 1070, 1071, 5, 16, 0, 0, 1071, 1072, - 3, 162, 81, 0, 1072, 1073, 5, 17, 0, 0, 1073, 213, - 1, 0, 0, 0, 1074, 1077, 3, 220, 110, 0, 1075, 1077, - 3, 216, 108, 0, 1076, 1074, 1, 0, 0, 0, 1076, 1075, - 1, 0, 0, 0, 1077, 215, 1, 0, 0, 0, 1078, 1079, - 5, 16, 0, 0, 1079, 1080, 3, 174, 87, 0, 1080, 1081, - 5, 17, 0, 0, 1081, 217, 1, 0, 0, 0, 1082, 1084, - 5, 2, 0, 0, 1083, 1085, 3, 222, 111, 0, 1084, 1083, - 1, 0, 0, 0, 1085, 1086, 1, 0, 0, 0, 1086, 1084, - 1, 0, 0, 0, 1086, 1087, 1, 0, 0, 0, 1087, 1088, - 1, 0, 0, 0, 1088, 1089, 5, 3, 0, 0, 1089, 219, - 1, 0, 0, 0, 1090, 1092, 5, 2, 0, 0, 1091, 1093, - 3, 224, 112, 0, 1092, 1091, 1, 0, 0, 0, 1093, 1094, - 1, 0, 0, 0, 1094, 1092, 1, 0, 0, 0, 1094, 1095, - 1, 0, 0, 0, 1095, 1096, 1, 0, 0, 0, 1096, 1097, - 5, 3, 0, 0, 1097, 221, 1, 0, 0, 0, 1098, 1101, - 3, 226, 113, 0, 1099, 1101, 3, 210, 105, 0, 1100, 1098, - 1, 0, 0, 0, 1100, 1099, 1, 0, 0, 0, 1101, 223, - 1, 0, 0, 0, 1102, 1105, 3, 226, 113, 0, 1103, 1105, - 3, 214, 107, 0, 1104, 1102, 1, 0, 0, 0, 1104, 1103, - 1, 0, 0, 0, 1105, 225, 1, 0, 0, 0, 1106, 1109, - 3, 230, 115, 0, 1107, 1109, 3, 232, 116, 0, 1108, 1106, - 1, 0, 0, 0, 1108, 1107, 1, 0, 0, 0, 1109, 227, - 1, 0, 0, 0, 1110, 1113, 3, 230, 115, 0, 1111, 1113, - 3, 302, 151, 0, 1112, 1110, 1, 0, 0, 0, 1112, 1111, - 1, 0, 0, 0, 1113, 229, 1, 0, 0, 0, 1114, 1115, - 7, 3, 0, 0, 1115, 231, 1, 0, 0, 0, 1116, 1123, - 3, 302, 151, 0, 1117, 1123, 3, 288, 144, 0, 1118, 1123, - 3, 290, 145, 0, 1119, 1123, 3, 298, 149, 0, 1120, 1123, - 3, 306, 153, 0, 1121, 1123, 5, 164, 0, 0, 1122, 1116, - 1, 0, 0, 0, 1122, 1117, 1, 0, 0, 0, 1122, 1118, - 1, 0, 0, 0, 1122, 1119, 1, 0, 0, 0, 1122, 1120, - 1, 0, 0, 0, 1122, 1121, 1, 0, 0, 0, 1123, 233, - 1, 0, 0, 0, 1124, 1125, 3, 236, 118, 0, 1125, 235, - 1, 0, 0, 0, 1126, 1131, 3, 238, 119, 0, 1127, 1128, - 5, 18, 0, 0, 1128, 1130, 3, 238, 119, 0, 1129, 1127, - 1, 0, 0, 0, 1130, 1133, 1, 0, 0, 0, 1131, 1129, - 1, 0, 0, 0, 1131, 1132, 1, 0, 0, 0, 1132, 237, - 1, 0, 0, 0, 1133, 1131, 1, 0, 0, 0, 1134, 1139, - 3, 240, 120, 0, 1135, 1136, 5, 19, 0, 0, 1136, 1138, - 3, 240, 120, 0, 1137, 1135, 1, 0, 0, 0, 1138, 1141, - 1, 0, 0, 0, 1139, 1137, 1, 0, 0, 0, 1139, 1140, - 1, 0, 0, 0, 1140, 239, 1, 0, 0, 0, 1141, 1139, - 1, 0, 0, 0, 1142, 1143, 3, 242, 121, 0, 1143, 241, - 1, 0, 0, 0, 1144, 1162, 3, 244, 122, 0, 1145, 1146, - 5, 20, 0, 0, 1146, 1163, 3, 244, 122, 0, 1147, 1148, - 5, 21, 0, 0, 1148, 1163, 3, 244, 122, 0, 1149, 1150, - 5, 22, 0, 0, 1150, 1163, 3, 244, 122, 0, 1151, 1152, - 5, 23, 0, 0, 1152, 1163, 3, 244, 122, 0, 1153, 1154, - 5, 24, 0, 0, 1154, 1163, 3, 244, 122, 0, 1155, 1156, - 5, 25, 0, 0, 1156, 1163, 3, 244, 122, 0, 1157, 1158, - 5, 79, 0, 0, 1158, 1163, 3, 152, 76, 0, 1159, 1160, - 5, 78, 0, 0, 1160, 1161, 5, 79, 0, 0, 1161, 1163, - 3, 152, 76, 0, 1162, 1145, 1, 0, 0, 0, 1162, 1147, - 1, 0, 0, 0, 1162, 1149, 1, 0, 0, 0, 1162, 1151, - 1, 0, 0, 0, 1162, 1153, 1, 0, 0, 0, 1162, 1155, - 1, 0, 0, 0, 1162, 1157, 1, 0, 0, 0, 1162, 1159, - 1, 0, 0, 0, 1162, 1163, 1, 0, 0, 0, 1163, 243, - 1, 0, 0, 0, 1164, 1165, 3, 246, 123, 0, 1165, 245, - 1, 0, 0, 0, 1166, 1170, 3, 256, 128, 0, 1167, 1169, - 3, 248, 124, 0, 1168, 1167, 1, 0, 0, 0, 1169, 1172, - 1, 0, 0, 0, 1170, 1168, 1, 0, 0, 0, 1170, 1171, - 1, 0, 0, 0, 1171, 247, 1, 0, 0, 0, 1172, 1170, - 1, 0, 0, 0, 1173, 1174, 5, 13, 0, 0, 1174, 1179, - 3, 250, 125, 0, 1175, 1176, 5, 26, 0, 0, 1176, 1179, - 3, 252, 126, 0, 1177, 1179, 3, 254, 127, 0, 1178, 1173, - 1, 0, 0, 0, 1178, 1175, 1, 0, 0, 0, 1178, 1177, - 1, 0, 0, 0, 1179, 249, 1, 0, 0, 0, 1180, 1181, - 3, 256, 128, 0, 1181, 251, 1, 0, 0, 0, 1182, 1183, - 3, 256, 128, 0, 1183, 253, 1, 0, 0, 0, 1184, 1187, - 3, 294, 147, 0, 1185, 1187, 3, 296, 148, 0, 1186, 1184, - 1, 0, 0, 0, 1186, 1185, 1, 0, 0, 0, 1187, 1191, - 1, 0, 0, 0, 1188, 1190, 3, 258, 129, 0, 1189, 1188, - 1, 0, 0, 0, 1190, 1193, 1, 0, 0, 0, 1191, 1189, - 1, 0, 0, 0, 1191, 1192, 1, 0, 0, 0, 1192, 255, - 1, 0, 0, 0, 1193, 1191, 1, 0, 0, 0, 1194, 1198, - 3, 264, 132, 0, 1195, 1197, 3, 258, 129, 0, 1196, 1195, - 1, 0, 0, 0, 1197, 1200, 1, 0, 0, 0, 1198, 1196, - 1, 0, 0, 0, 1198, 1199, 1, 0, 0, 0, 1199, 257, - 1, 0, 0, 0, 1200, 1198, 1, 0, 0, 0, 1201, 1204, - 3, 260, 130, 0, 1202, 1204, 3, 262, 131, 0, 1203, 1201, - 1, 0, 0, 0, 1203, 1202, 1, 0, 0, 0, 1204, 259, - 1, 0, 0, 0, 1205, 1206, 5, 1, 0, 0, 1206, 1207, - 3, 264, 132, 0, 1207, 261, 1, 0, 0, 0, 1208, 1209, - 5, 11, 0, 0, 1209, 1210, 3, 264, 132, 0, 1210, 263, - 1, 0, 0, 0, 1211, 1212, 5, 15, 0, 0, 1212, 1219, - 3, 266, 133, 0, 1213, 1214, 5, 13, 0, 0, 1214, 1219, - 3, 266, 133, 0, 1215, 1216, 5, 26, 0, 0, 1216, 1219, - 3, 266, 133, 0, 1217, 1219, 3, 266, 133, 0, 1218, 1211, - 1, 0, 0, 0, 1218, 1213, 1, 0, 0, 0, 1218, 1215, - 1, 0, 0, 0, 1218, 1217, 1, 0, 0, 0, 1219, 265, - 1, 0, 0, 0, 1220, 1228, 3, 268, 134, 0, 1221, 1228, - 3, 270, 135, 0, 1222, 1228, 3, 286, 143, 0, 1223, 1228, - 3, 288, 144, 0, 1224, 1228, 3, 290, 145, 0, 1225, 1228, - 3, 298, 149, 0, 1226, 1228, 3, 230, 115, 0, 1227, 1220, - 1, 0, 0, 0, 1227, 1221, 1, 0, 0, 0, 1227, 1222, - 1, 0, 0, 0, 1227, 1223, 1, 0, 0, 0, 1227, 1224, - 1, 0, 0, 0, 1227, 1225, 1, 0, 0, 0, 1227, 1226, - 1, 0, 0, 0, 1228, 267, 1, 0, 0, 0, 1229, 1230, - 5, 2, 0, 0, 1230, 1231, 3, 234, 117, 0, 1231, 1232, - 5, 3, 0, 0, 1232, 269, 1, 0, 0, 0, 1233, 1488, - 3, 284, 142, 0, 1234, 1235, 5, 80, 0, 0, 1235, 1236, - 5, 2, 0, 0, 1236, 1237, 3, 234, 117, 0, 1237, 1238, - 5, 3, 0, 0, 1238, 1488, 1, 0, 0, 0, 1239, 1488, - 3, 274, 137, 0, 1240, 1241, 5, 82, 0, 0, 1241, 1242, - 5, 2, 0, 0, 1242, 1243, 3, 234, 117, 0, 1243, 1244, - 5, 8, 0, 0, 1244, 1245, 3, 234, 117, 0, 1245, 1246, - 5, 3, 0, 0, 1246, 1488, 1, 0, 0, 0, 1247, 1248, - 5, 83, 0, 0, 1248, 1249, 5, 2, 0, 0, 1249, 1250, - 3, 234, 117, 0, 1250, 1251, 5, 3, 0, 0, 1251, 1488, - 1, 0, 0, 0, 1252, 1253, 5, 84, 0, 0, 1253, 1254, - 5, 2, 0, 0, 1254, 1255, 3, 230, 115, 0, 1255, 1256, - 5, 3, 0, 0, 1256, 1488, 1, 0, 0, 0, 1257, 1258, - 5, 85, 0, 0, 1258, 1259, 5, 2, 0, 0, 1259, 1260, - 3, 234, 117, 0, 1260, 1261, 5, 3, 0, 0, 1261, 1488, - 1, 0, 0, 0, 1262, 1263, 5, 86, 0, 0, 1263, 1264, - 5, 2, 0, 0, 1264, 1265, 3, 234, 117, 0, 1265, 1266, - 5, 3, 0, 0, 1266, 1488, 1, 0, 0, 0, 1267, 1273, - 5, 87, 0, 0, 1268, 1269, 5, 2, 0, 0, 1269, 1270, - 3, 234, 117, 0, 1270, 1271, 5, 3, 0, 0, 1271, 1274, - 1, 0, 0, 0, 1272, 1274, 5, 164, 0, 0, 1273, 1268, - 1, 0, 0, 0, 1273, 1272, 1, 0, 0, 0, 1274, 1488, - 1, 0, 0, 0, 1275, 1276, 5, 88, 0, 0, 1276, 1488, - 5, 164, 0, 0, 1277, 1278, 5, 89, 0, 0, 1278, 1279, - 5, 2, 0, 0, 1279, 1280, 3, 234, 117, 0, 1280, 1281, - 5, 3, 0, 0, 1281, 1488, 1, 0, 0, 0, 1282, 1283, - 5, 90, 0, 0, 1283, 1284, 5, 2, 0, 0, 1284, 1285, - 3, 234, 117, 0, 1285, 1286, 5, 3, 0, 0, 1286, 1488, - 1, 0, 0, 0, 1287, 1288, 5, 91, 0, 0, 1288, 1289, - 5, 2, 0, 0, 1289, 1290, 3, 234, 117, 0, 1290, 1291, - 5, 3, 0, 0, 1291, 1488, 1, 0, 0, 0, 1292, 1293, - 5, 92, 0, 0, 1293, 1294, 5, 2, 0, 0, 1294, 1295, - 3, 234, 117, 0, 1295, 1296, 5, 3, 0, 0, 1296, 1488, - 1, 0, 0, 0, 1297, 1298, 5, 93, 0, 0, 1298, 1488, - 3, 152, 76, 0, 1299, 1488, 3, 276, 138, 0, 1300, 1301, - 5, 94, 0, 0, 1301, 1302, 5, 2, 0, 0, 1302, 1303, - 3, 234, 117, 0, 1303, 1304, 5, 3, 0, 0, 1304, 1488, - 1, 0, 0, 0, 1305, 1488, 3, 278, 139, 0, 1306, 1307, - 5, 95, 0, 0, 1307, 1308, 5, 2, 0, 0, 1308, 1309, - 3, 234, 117, 0, 1309, 1310, 5, 3, 0, 0, 1310, 1488, - 1, 0, 0, 0, 1311, 1312, 5, 96, 0, 0, 1312, 1313, - 5, 2, 0, 0, 1313, 1314, 3, 234, 117, 0, 1314, 1315, - 5, 3, 0, 0, 1315, 1488, 1, 0, 0, 0, 1316, 1317, - 5, 97, 0, 0, 1317, 1318, 5, 2, 0, 0, 1318, 1319, - 3, 234, 117, 0, 1319, 1320, 5, 3, 0, 0, 1320, 1488, - 1, 0, 0, 0, 1321, 1322, 5, 99, 0, 0, 1322, 1323, - 5, 2, 0, 0, 1323, 1324, 3, 234, 117, 0, 1324, 1325, - 5, 8, 0, 0, 1325, 1326, 3, 234, 117, 0, 1326, 1327, - 5, 3, 0, 0, 1327, 1488, 1, 0, 0, 0, 1328, 1329, - 5, 100, 0, 0, 1329, 1330, 5, 2, 0, 0, 1330, 1331, - 3, 234, 117, 0, 1331, 1332, 5, 8, 0, 0, 1332, 1333, - 3, 234, 117, 0, 1333, 1334, 5, 3, 0, 0, 1334, 1488, - 1, 0, 0, 0, 1335, 1336, 5, 101, 0, 0, 1336, 1337, - 5, 2, 0, 0, 1337, 1338, 3, 234, 117, 0, 1338, 1339, - 5, 8, 0, 0, 1339, 1340, 3, 234, 117, 0, 1340, 1341, - 5, 3, 0, 0, 1341, 1488, 1, 0, 0, 0, 1342, 1343, - 5, 102, 0, 0, 1343, 1344, 5, 2, 0, 0, 1344, 1345, - 3, 234, 117, 0, 1345, 1346, 5, 8, 0, 0, 1346, 1347, - 3, 234, 117, 0, 1347, 1348, 5, 3, 0, 0, 1348, 1488, - 1, 0, 0, 0, 1349, 1350, 5, 103, 0, 0, 1350, 1351, - 5, 2, 0, 0, 1351, 1352, 3, 234, 117, 0, 1352, 1353, - 5, 8, 0, 0, 1353, 1354, 3, 234, 117, 0, 1354, 1355, - 5, 3, 0, 0, 1355, 1488, 1, 0, 0, 0, 1356, 1357, - 5, 104, 0, 0, 1357, 1358, 5, 2, 0, 0, 1358, 1359, - 3, 234, 117, 0, 1359, 1360, 5, 3, 0, 0, 1360, 1488, - 1, 0, 0, 0, 1361, 1362, 5, 105, 0, 0, 1362, 1363, - 5, 2, 0, 0, 1363, 1364, 3, 234, 117, 0, 1364, 1365, - 5, 3, 0, 0, 1365, 1488, 1, 0, 0, 0, 1366, 1367, - 5, 106, 0, 0, 1367, 1368, 5, 2, 0, 0, 1368, 1369, - 3, 234, 117, 0, 1369, 1370, 5, 3, 0, 0, 1370, 1488, - 1, 0, 0, 0, 1371, 1372, 5, 107, 0, 0, 1372, 1373, - 5, 2, 0, 0, 1373, 1374, 3, 234, 117, 0, 1374, 1375, - 5, 3, 0, 0, 1375, 1488, 1, 0, 0, 0, 1376, 1377, - 5, 108, 0, 0, 1377, 1378, 5, 2, 0, 0, 1378, 1379, - 3, 234, 117, 0, 1379, 1380, 5, 3, 0, 0, 1380, 1488, - 1, 0, 0, 0, 1381, 1382, 5, 109, 0, 0, 1382, 1383, - 5, 2, 0, 0, 1383, 1384, 3, 234, 117, 0, 1384, 1385, - 5, 3, 0, 0, 1385, 1488, 1, 0, 0, 0, 1386, 1387, - 5, 110, 0, 0, 1387, 1388, 5, 2, 0, 0, 1388, 1389, - 3, 234, 117, 0, 1389, 1390, 5, 3, 0, 0, 1390, 1488, - 1, 0, 0, 0, 1391, 1392, 5, 111, 0, 0, 1392, 1393, - 5, 2, 0, 0, 1393, 1394, 3, 234, 117, 0, 1394, 1395, - 5, 3, 0, 0, 1395, 1488, 1, 0, 0, 0, 1396, 1397, - 5, 112, 0, 0, 1397, 1488, 5, 164, 0, 0, 1398, 1399, - 5, 113, 0, 0, 1399, 1488, 5, 164, 0, 0, 1400, 1401, - 5, 114, 0, 0, 1401, 1488, 5, 164, 0, 0, 1402, 1403, - 5, 119, 0, 0, 1403, 1404, 5, 2, 0, 0, 1404, 1405, - 3, 234, 117, 0, 1405, 1406, 5, 3, 0, 0, 1406, 1488, - 1, 0, 0, 0, 1407, 1408, 5, 115, 0, 0, 1408, 1409, - 5, 2, 0, 0, 1409, 1410, 3, 234, 117, 0, 1410, 1411, - 5, 3, 0, 0, 1411, 1488, 1, 0, 0, 0, 1412, 1413, - 5, 116, 0, 0, 1413, 1414, 5, 2, 0, 0, 1414, 1415, - 3, 234, 117, 0, 1415, 1416, 5, 3, 0, 0, 1416, 1488, - 1, 0, 0, 0, 1417, 1418, 5, 117, 0, 0, 1418, 1419, - 5, 2, 0, 0, 1419, 1420, 3, 234, 117, 0, 1420, 1421, - 5, 3, 0, 0, 1421, 1488, 1, 0, 0, 0, 1422, 1423, - 5, 118, 0, 0, 1423, 1424, 5, 2, 0, 0, 1424, 1425, - 3, 234, 117, 0, 1425, 1426, 5, 3, 0, 0, 1426, 1488, - 1, 0, 0, 0, 1427, 1428, 5, 120, 0, 0, 1428, 1488, - 3, 152, 76, 0, 1429, 1430, 5, 121, 0, 0, 1430, 1431, - 5, 2, 0, 0, 1431, 1432, 3, 234, 117, 0, 1432, 1433, - 5, 8, 0, 0, 1433, 1434, 3, 234, 117, 0, 1434, 1435, - 5, 8, 0, 0, 1435, 1436, 3, 234, 117, 0, 1436, 1437, - 5, 3, 0, 0, 1437, 1488, 1, 0, 0, 0, 1438, 1439, - 5, 122, 0, 0, 1439, 1440, 5, 2, 0, 0, 1440, 1441, - 3, 234, 117, 0, 1441, 1442, 5, 8, 0, 0, 1442, 1443, - 3, 234, 117, 0, 1443, 1444, 5, 3, 0, 0, 1444, 1488, - 1, 0, 0, 0, 1445, 1446, 5, 123, 0, 0, 1446, 1447, - 5, 2, 0, 0, 1447, 1448, 3, 234, 117, 0, 1448, 1449, - 5, 8, 0, 0, 1449, 1450, 3, 234, 117, 0, 1450, 1451, - 5, 3, 0, 0, 1451, 1488, 1, 0, 0, 0, 1452, 1453, - 5, 124, 0, 0, 1453, 1454, 5, 2, 0, 0, 1454, 1455, - 3, 234, 117, 0, 1455, 1456, 5, 8, 0, 0, 1456, 1457, - 3, 234, 117, 0, 1457, 1458, 5, 3, 0, 0, 1458, 1488, - 1, 0, 0, 0, 1459, 1460, 5, 125, 0, 0, 1460, 1461, - 5, 2, 0, 0, 1461, 1462, 3, 234, 117, 0, 1462, 1463, - 5, 3, 0, 0, 1463, 1488, 1, 0, 0, 0, 1464, 1465, - 5, 126, 0, 0, 1465, 1466, 5, 2, 0, 0, 1466, 1467, - 3, 234, 117, 0, 1467, 1468, 5, 3, 0, 0, 1468, 1488, - 1, 0, 0, 0, 1469, 1470, 5, 127, 0, 0, 1470, 1471, - 5, 2, 0, 0, 1471, 1472, 3, 234, 117, 0, 1472, 1473, - 5, 3, 0, 0, 1473, 1488, 1, 0, 0, 0, 1474, 1475, - 5, 128, 0, 0, 1475, 1476, 5, 2, 0, 0, 1476, 1477, - 3, 234, 117, 0, 1477, 1478, 5, 3, 0, 0, 1478, 1488, - 1, 0, 0, 0, 1479, 1480, 5, 129, 0, 0, 1480, 1481, - 5, 2, 0, 0, 1481, 1482, 3, 234, 117, 0, 1482, 1483, - 5, 3, 0, 0, 1483, 1488, 1, 0, 0, 0, 1484, 1488, - 3, 272, 136, 0, 1485, 1488, 3, 280, 140, 0, 1486, 1488, - 3, 282, 141, 0, 1487, 1233, 1, 0, 0, 0, 1487, 1234, - 1, 0, 0, 0, 1487, 1239, 1, 0, 0, 0, 1487, 1240, - 1, 0, 0, 0, 1487, 1247, 1, 0, 0, 0, 1487, 1252, - 1, 0, 0, 0, 1487, 1257, 1, 0, 0, 0, 1487, 1262, - 1, 0, 0, 0, 1487, 1267, 1, 0, 0, 0, 1487, 1275, - 1, 0, 0, 0, 1487, 1277, 1, 0, 0, 0, 1487, 1282, - 1, 0, 0, 0, 1487, 1287, 1, 0, 0, 0, 1487, 1292, - 1, 0, 0, 0, 1487, 1297, 1, 0, 0, 0, 1487, 1299, - 1, 0, 0, 0, 1487, 1300, 1, 0, 0, 0, 1487, 1305, - 1, 0, 0, 0, 1487, 1306, 1, 0, 0, 0, 1487, 1311, - 1, 0, 0, 0, 1487, 1316, 1, 0, 0, 0, 1487, 1321, - 1, 0, 0, 0, 1487, 1328, 1, 0, 0, 0, 1487, 1335, - 1, 0, 0, 0, 1487, 1342, 1, 0, 0, 0, 1487, 1349, - 1, 0, 0, 0, 1487, 1356, 1, 0, 0, 0, 1487, 1361, - 1, 0, 0, 0, 1487, 1366, 1, 0, 0, 0, 1487, 1371, - 1, 0, 0, 0, 1487, 1376, 1, 0, 0, 0, 1487, 1381, - 1, 0, 0, 0, 1487, 1386, 1, 0, 0, 0, 1487, 1391, - 1, 0, 0, 0, 1487, 1396, 1, 0, 0, 0, 1487, 1398, - 1, 0, 0, 0, 1487, 1400, 1, 0, 0, 0, 1487, 1402, - 1, 0, 0, 0, 1487, 1407, 1, 0, 0, 0, 1487, 1412, - 1, 0, 0, 0, 1487, 1417, 1, 0, 0, 0, 1487, 1422, - 1, 0, 0, 0, 1487, 1427, 1, 0, 0, 0, 1487, 1429, - 1, 0, 0, 0, 1487, 1438, 1, 0, 0, 0, 1487, 1445, - 1, 0, 0, 0, 1487, 1452, 1, 0, 0, 0, 1487, 1459, - 1, 0, 0, 0, 1487, 1464, 1, 0, 0, 0, 1487, 1469, - 1, 0, 0, 0, 1487, 1474, 1, 0, 0, 0, 1487, 1479, - 1, 0, 0, 0, 1487, 1484, 1, 0, 0, 0, 1487, 1485, - 1, 0, 0, 0, 1487, 1486, 1, 0, 0, 0, 1488, 271, - 1, 0, 0, 0, 1489, 1490, 5, 130, 0, 0, 1490, 1491, - 5, 2, 0, 0, 1491, 1492, 3, 234, 117, 0, 1492, 1493, - 5, 8, 0, 0, 1493, 1496, 3, 234, 117, 0, 1494, 1495, - 5, 8, 0, 0, 1495, 1497, 3, 234, 117, 0, 1496, 1494, - 1, 0, 0, 0, 1496, 1497, 1, 0, 0, 0, 1497, 1498, - 1, 0, 0, 0, 1498, 1499, 5, 3, 0, 0, 1499, 273, - 1, 0, 0, 0, 1500, 1501, 5, 81, 0, 0, 1501, 1502, - 5, 2, 0, 0, 1502, 1503, 3, 234, 117, 0, 1503, 1504, - 5, 3, 0, 0, 1504, 275, 1, 0, 0, 0, 1505, 1506, - 5, 131, 0, 0, 1506, 1507, 5, 2, 0, 0, 1507, 1508, - 3, 234, 117, 0, 1508, 1509, 5, 8, 0, 0, 1509, 1512, - 3, 234, 117, 0, 1510, 1511, 5, 8, 0, 0, 1511, 1513, - 3, 234, 117, 0, 1512, 1510, 1, 0, 0, 0, 1512, 1513, - 1, 0, 0, 0, 1513, 1514, 1, 0, 0, 0, 1514, 1515, - 5, 3, 0, 0, 1515, 277, 1, 0, 0, 0, 1516, 1517, - 5, 132, 0, 0, 1517, 1518, 5, 2, 0, 0, 1518, 1519, - 3, 234, 117, 0, 1519, 1520, 5, 8, 0, 0, 1520, 1521, - 3, 234, 117, 0, 1521, 1522, 5, 8, 0, 0, 1522, 1525, - 3, 234, 117, 0, 1523, 1524, 5, 8, 0, 0, 1524, 1526, - 3, 234, 117, 0, 1525, 1523, 1, 0, 0, 0, 1525, 1526, - 1, 0, 0, 0, 1526, 1527, 1, 0, 0, 0, 1527, 1528, - 5, 3, 0, 0, 1528, 279, 1, 0, 0, 0, 1529, 1530, - 5, 133, 0, 0, 1530, 1531, 3, 110, 55, 0, 1531, 281, - 1, 0, 0, 0, 1532, 1533, 5, 78, 0, 0, 1533, 1534, - 5, 133, 0, 0, 1534, 1535, 3, 110, 55, 0, 1535, 283, - 1, 0, 0, 0, 1536, 1537, 5, 134, 0, 0, 1537, 1539, - 5, 2, 0, 0, 1538, 1540, 5, 33, 0, 0, 1539, 1538, - 1, 0, 0, 0, 1539, 1540, 1, 0, 0, 0, 1540, 1543, - 1, 0, 0, 0, 1541, 1544, 5, 1, 0, 0, 1542, 1544, - 3, 234, 117, 0, 1543, 1541, 1, 0, 0, 0, 1543, 1542, - 1, 0, 0, 0, 1544, 1545, 1, 0, 0, 0, 1545, 1601, - 5, 3, 0, 0, 1546, 1547, 5, 135, 0, 0, 1547, 1549, - 5, 2, 0, 0, 1548, 1550, 5, 33, 0, 0, 1549, 1548, - 1, 0, 0, 0, 1549, 1550, 1, 0, 0, 0, 1550, 1551, - 1, 0, 0, 0, 1551, 1552, 3, 234, 117, 0, 1552, 1553, - 5, 3, 0, 0, 1553, 1601, 1, 0, 0, 0, 1554, 1555, - 5, 136, 0, 0, 1555, 1557, 5, 2, 0, 0, 1556, 1558, - 5, 33, 0, 0, 1557, 1556, 1, 0, 0, 0, 1557, 1558, - 1, 0, 0, 0, 1558, 1559, 1, 0, 0, 0, 1559, 1560, - 3, 234, 117, 0, 1560, 1561, 5, 3, 0, 0, 1561, 1601, - 1, 0, 0, 0, 1562, 1563, 5, 137, 0, 0, 1563, 1565, - 5, 2, 0, 0, 1564, 1566, 5, 33, 0, 0, 1565, 1564, - 1, 0, 0, 0, 1565, 1566, 1, 0, 0, 0, 1566, 1567, - 1, 0, 0, 0, 1567, 1568, 3, 234, 117, 0, 1568, 1569, - 5, 3, 0, 0, 1569, 1601, 1, 0, 0, 0, 1570, 1571, - 5, 138, 0, 0, 1571, 1573, 5, 2, 0, 0, 1572, 1574, - 5, 33, 0, 0, 1573, 1572, 1, 0, 0, 0, 1573, 1574, - 1, 0, 0, 0, 1574, 1575, 1, 0, 0, 0, 1575, 1576, - 3, 234, 117, 0, 1576, 1577, 5, 3, 0, 0, 1577, 1601, - 1, 0, 0, 0, 1578, 1579, 5, 139, 0, 0, 1579, 1581, - 5, 2, 0, 0, 1580, 1582, 5, 33, 0, 0, 1581, 1580, - 1, 0, 0, 0, 1581, 1582, 1, 0, 0, 0, 1582, 1583, - 1, 0, 0, 0, 1583, 1584, 3, 234, 117, 0, 1584, 1585, - 5, 3, 0, 0, 1585, 1601, 1, 0, 0, 0, 1586, 1587, - 5, 43, 0, 0, 1587, 1589, 5, 2, 0, 0, 1588, 1590, - 5, 33, 0, 0, 1589, 1588, 1, 0, 0, 0, 1589, 1590, - 1, 0, 0, 0, 1590, 1591, 1, 0, 0, 0, 1591, 1596, - 3, 234, 117, 0, 1592, 1593, 5, 6, 0, 0, 1593, 1594, - 5, 140, 0, 0, 1594, 1595, 5, 20, 0, 0, 1595, 1597, - 3, 300, 150, 0, 1596, 1592, 1, 0, 0, 0, 1596, 1597, - 1, 0, 0, 0, 1597, 1598, 1, 0, 0, 0, 1598, 1599, - 5, 3, 0, 0, 1599, 1601, 1, 0, 0, 0, 1600, 1536, - 1, 0, 0, 0, 1600, 1546, 1, 0, 0, 0, 1600, 1554, - 1, 0, 0, 0, 1600, 1562, 1, 0, 0, 0, 1600, 1570, - 1, 0, 0, 0, 1600, 1578, 1, 0, 0, 0, 1600, 1586, - 1, 0, 0, 0, 1601, 285, 1, 0, 0, 0, 1602, 1604, - 3, 302, 151, 0, 1603, 1605, 3, 150, 75, 0, 1604, 1603, - 1, 0, 0, 0, 1604, 1605, 1, 0, 0, 0, 1605, 287, - 1, 0, 0, 0, 1606, 1610, 3, 300, 150, 0, 1607, 1611, - 5, 147, 0, 0, 1608, 1609, 5, 27, 0, 0, 1609, 1611, - 3, 302, 151, 0, 1610, 1607, 1, 0, 0, 0, 1610, 1608, - 1, 0, 0, 0, 1610, 1611, 1, 0, 0, 0, 1611, 289, - 1, 0, 0, 0, 1612, 1616, 3, 292, 146, 0, 1613, 1616, - 3, 294, 147, 0, 1614, 1616, 3, 296, 148, 0, 1615, 1612, - 1, 0, 0, 0, 1615, 1613, 1, 0, 0, 0, 1615, 1614, - 1, 0, 0, 0, 1616, 291, 1, 0, 0, 0, 1617, 1618, - 7, 4, 0, 0, 1618, 293, 1, 0, 0, 0, 1619, 1620, - 7, 5, 0, 0, 1620, 295, 1, 0, 0, 0, 1621, 1622, - 7, 6, 0, 0, 1622, 297, 1, 0, 0, 0, 1623, 1624, - 7, 7, 0, 0, 1624, 299, 1, 0, 0, 0, 1625, 1626, - 7, 8, 0, 0, 1626, 301, 1, 0, 0, 0, 1627, 1629, - 5, 148, 0, 0, 1628, 1627, 1, 0, 0, 0, 1628, 1629, - 1, 0, 0, 0, 1629, 1632, 1, 0, 0, 0, 1630, 1633, - 3, 308, 154, 0, 1631, 1633, 3, 304, 152, 0, 1632, 1630, - 1, 0, 0, 0, 1632, 1631, 1, 0, 0, 0, 1633, 303, - 1, 0, 0, 0, 1634, 1637, 3, 310, 155, 0, 1635, 1637, - 3, 312, 156, 0, 1636, 1634, 1, 0, 0, 0, 1636, 1635, - 1, 0, 0, 0, 1637, 305, 1, 0, 0, 0, 1638, 1639, - 7, 9, 0, 0, 1639, 307, 1, 0, 0, 0, 1640, 1641, - 5, 141, 0, 0, 1641, 309, 1, 0, 0, 0, 1642, 1643, - 5, 143, 0, 0, 1643, 311, 1, 0, 0, 0, 1644, 1645, - 5, 142, 0, 0, 1645, 313, 1, 0, 0, 0, 161, 316, - 325, 331, 333, 347, 360, 365, 368, 372, 387, 396, 402, 406, - 412, 415, 420, 424, 432, 441, 451, 456, 459, 462, 465, 471, - 479, 484, 490, 496, 501, 507, 509, 513, 516, 520, 523, 527, - 530, 534, 537, 541, 544, 548, 551, 553, 566, 572, 574, 587, - 591, 596, 600, 606, 612, 618, 626, 634, 654, 658, 661, 666, - 682, 687, 696, 707, 711, 714, 718, 725, 732, 734, 739, 744, - 749, 754, 757, 762, 764, 774, 785, 802, 809, 819, 823, 829, - 838, 843, 850, 860, 869, 877, 884, 889, 898, 903, 907, 914, - 916, 924, 927, 935, 939, 944, 951, 962, 965, 970, 974, 989, - 996, 1008, 1016, 1021, 1026, 1038, 1047, 1050, 1053, 1060, 1062, 1068, - 1076, 1086, 1094, 1100, 1104, 1108, 1112, 1122, 1131, 1139, 1162, 1170, - 1178, 1186, 1191, 1198, 1203, 1218, 1227, 1273, 1487, 1496, 1512, 1525, - 1539, 1543, 1549, 1557, 1565, 1573, 1581, 1589, 1596, 1600, 1604, 1610, - 1615, 1628, 1632, 1636}; + 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}; staticData->serializedATN = antlr4::atn::SerializedATNView( serializedATNSegment, sizeof(serializedATNSegment) / sizeof(serializedATNSegment[0])); @@ -2770,8 +2777,8 @@ SparqlAutomaticParser::constructQuery() { _la = _input->LA(1); if (((_la & ~0x3fULL) == 0) && ((1ULL << _la) & 805371908) != 0 || - (((_la - 141) & ~0x3fULL) == 0) && - ((1ULL << (_la - 141)) & 29228991) != 0) { + (((_la - 142) & ~0x3fULL) == 0) && + ((1ULL << (_la - 142)) & 29228991) != 0) { setState(401); triplesTemplate(); } @@ -2896,8 +2903,8 @@ SparqlAutomaticParser::describeQuery() { setState(412); _errHandler->sync(this); _la = _input->LA(1); - } while ((((_la - 141) & ~0x3fULL) == 0) && - ((1ULL << (_la - 141)) & 183) != 0); + } while ((((_la - 142) & ~0x3fULL) == 0) && + ((1ULL << (_la - 142)) & 183) != 0); break; } @@ -3589,9 +3596,9 @@ SparqlAutomaticParser::groupClause() { || _la == SparqlAutomaticParser::GROUP_CONCAT || (((_la - 78) & ~0x3fULL) == 0) && - ((1ULL << (_la - 78)) & -4611686018428436483) != 0 || + ((1ULL << (_la - 78)) & 9223372036853727229) != 0 || (((_la - 142) & ~0x3fULL) == 0) && - ((1ULL << (_la - 142)) & 91) != 0); + ((1ULL << (_la - 142)) & 183) != 0); } catch (RecognitionException& e) { _errHandler->reportError(this, e); @@ -3735,6 +3742,7 @@ SparqlAutomaticParser::groupCondition() { case SparqlAutomaticParser::MIN: case SparqlAutomaticParser::MAX: case SparqlAutomaticParser::AVG: + case SparqlAutomaticParser::STDEV: case SparqlAutomaticParser::SAMPLE: { enterOuterAlt(_localctx, 1); setState(473); @@ -3870,9 +3878,9 @@ SparqlAutomaticParser::havingClause() { || _la == SparqlAutomaticParser::GROUP_CONCAT || (((_la - 78) & ~0x3fULL) == 0) && - ((1ULL << (_la - 78)) & -4611686018428436483) != 0 || + ((1ULL << (_la - 78)) & 9223372036853727229) != 0 || (((_la - 142) & ~0x3fULL) == 0) && - ((1ULL << (_la - 142)) & 67) != 0); + ((1ULL << (_la - 142)) & 135) != 0); } catch (RecognitionException& e) { _errHandler->reportError(this, e); @@ -4044,8 +4052,8 @@ SparqlAutomaticParser::orderClause() { } while ( ((_la & ~0x3fULL) == 0) && ((1ULL << _la) & 431008558088196) != 0 || (((_la - 78) & ~0x3fULL) == 0) && - ((1ULL << (_la - 78)) & -4611686018428436483) != 0 || - (((_la - 142) & ~0x3fULL) == 0) && ((1ULL << (_la - 142)) & 91) != 0); + ((1ULL << (_la - 78)) & 9223372036853727229) != 0 || + (((_la - 142) & ~0x3fULL) == 0) && ((1ULL << (_la - 142)) & 183) != 0); } catch (RecognitionException& e) { _errHandler->reportError(this, e); @@ -4207,6 +4215,7 @@ SparqlAutomaticParser::orderCondition() { case SparqlAutomaticParser::MIN: case SparqlAutomaticParser::MAX: case SparqlAutomaticParser::AVG: + case SparqlAutomaticParser::STDEV: case SparqlAutomaticParser::SAMPLE: case SparqlAutomaticParser::IRI_REF: case SparqlAutomaticParser::PNAME_NS: @@ -4279,6 +4288,7 @@ SparqlAutomaticParser::orderCondition() { case SparqlAutomaticParser::MIN: case SparqlAutomaticParser::MAX: case SparqlAutomaticParser::AVG: + case SparqlAutomaticParser::STDEV: case SparqlAutomaticParser::SAMPLE: case SparqlAutomaticParser::IRI_REF: case SparqlAutomaticParser::PNAME_NS: @@ -6787,8 +6797,8 @@ SparqlAutomaticParser::QuadsContext* SparqlAutomaticParser::quads() { _la = _input->LA(1); if (((_la & ~0x3fULL) == 0) && ((1ULL << _la) & 805371908) != 0 || - (((_la - 141) & ~0x3fULL) == 0) && - ((1ULL << (_la - 141)) & 29228991) != 0) { + (((_la - 142) & ~0x3fULL) == 0) && + ((1ULL << (_la - 142)) & 29228991) != 0) { setState(706); triplesTemplate(); } @@ -6811,8 +6821,8 @@ SparqlAutomaticParser::QuadsContext* SparqlAutomaticParser::quads() { _la = _input->LA(1); if (((_la & ~0x3fULL) == 0) && ((1ULL << _la) & 805371908) != 0 || - (((_la - 141) & ~0x3fULL) == 0) && - ((1ULL << (_la - 141)) & 29228991) != 0) { + (((_la - 142) & ~0x3fULL) == 0) && + ((1ULL << (_la - 142)) & 29228991) != 0) { setState(713); triplesTemplate(); } @@ -6902,8 +6912,8 @@ SparqlAutomaticParser::quadsNotTriples() { _la = _input->LA(1); if (((_la & ~0x3fULL) == 0) && ((1ULL << _la) & 805371908) != 0 || - (((_la - 141) & ~0x3fULL) == 0) && - ((1ULL << (_la - 141)) & 29228991) != 0) { + (((_la - 142) & ~0x3fULL) == 0) && + ((1ULL << (_la - 142)) & 29228991) != 0) { setState(724); triplesTemplate(); } @@ -6990,8 +7000,8 @@ SparqlAutomaticParser::triplesTemplate() { _la = _input->LA(1); if (((_la & ~0x3fULL) == 0) && ((1ULL << _la) & 805371908) != 0 || - (((_la - 141) & ~0x3fULL) == 0) && - ((1ULL << (_la - 141)) & 29228991) != 0) { + (((_la - 142) & ~0x3fULL) == 0) && + ((1ULL << (_la - 142)) & 29228991) != 0) { setState(731); triplesTemplate(); } @@ -7202,8 +7212,8 @@ SparqlAutomaticParser::groupGraphPatternSub() { _la = _input->LA(1); if (((_la & ~0x3fULL) == 0) && ((1ULL << _la) & 805371908) != 0 || - (((_la - 141) & ~0x3fULL) == 0) && - ((1ULL << (_la - 141)) & 29228991) != 0) { + (((_la - 142) & ~0x3fULL) == 0) && + ((1ULL << (_la - 142)) & 29228991) != 0) { setState(743); triplesBlock(); } @@ -7311,8 +7321,8 @@ SparqlAutomaticParser::graphPatternNotTriplesAndMaybeTriples() { _la = _input->LA(1); if (((_la & ~0x3fULL) == 0) && ((1ULL << _la) & 805371908) != 0 || - (((_la - 141) & ~0x3fULL) == 0) && - ((1ULL << (_la - 141)) & 29228991) != 0) { + (((_la - 142) & ~0x3fULL) == 0) && + ((1ULL << (_la - 142)) & 29228991) != 0) { setState(756); triplesBlock(); } @@ -7398,8 +7408,8 @@ SparqlAutomaticParser::triplesBlock() { _la = _input->LA(1); if (((_la & ~0x3fULL) == 0) && ((1ULL << _la) & 805371908) != 0 || - (((_la - 141) & ~0x3fULL) == 0) && - ((1ULL << (_la - 141)) & 29228991) != 0) { + (((_la - 142) & ~0x3fULL) == 0) && + ((1ULL << (_la - 142)) & 29228991) != 0) { setState(761); triplesBlock(); } @@ -8124,8 +8134,8 @@ SparqlAutomaticParser::inlineDataOneVar() { _la = _input->LA(1); while ((((_la - 28) & ~0x3fULL) == 0) && ((1ULL << (_la - 28)) & 70368744177667) != 0 || - (((_la - 141) & ~0x3fULL) == 0) && - ((1ULL << (_la - 141)) & 4063111) != 0) { + (((_la - 142) & ~0x3fULL) == 0) && + ((1ULL << (_la - 142)) & 4063111) != 0) { setState(806); dataBlockValue(); setState(811); @@ -8344,8 +8354,8 @@ SparqlAutomaticParser::dataBlockSingle() { _la = _input->LA(1); while ((((_la - 28) & ~0x3fULL) == 0) && ((1ULL << (_la - 28)) & 70368744177667) != 0 || - (((_la - 141) & ~0x3fULL) == 0) && - ((1ULL << (_la - 141)) & 4063111) != 0) { + (((_la - 142) & ~0x3fULL) == 0) && + ((1ULL << (_la - 142)) & 4063111) != 0) { setState(835); dataBlockValue(); setState(840); @@ -8875,6 +8885,7 @@ SparqlAutomaticParser::ConstraintContext* SparqlAutomaticParser::constraint() { case SparqlAutomaticParser::MIN: case SparqlAutomaticParser::MAX: case SparqlAutomaticParser::AVG: + case SparqlAutomaticParser::STDEV: case SparqlAutomaticParser::SAMPLE: { enterOuterAlt(_localctx, 2); setState(867); @@ -9257,8 +9268,8 @@ SparqlAutomaticParser::constructTemplate() { _la = _input->LA(1); if (((_la & ~0x3fULL) == 0) && ((1ULL << _la) & 805371908) != 0 || - (((_la - 141) & ~0x3fULL) == 0) && - ((1ULL << (_la - 141)) & 29228991) != 0) { + (((_la - 142) & ~0x3fULL) == 0) && + ((1ULL << (_la - 142)) & 29228991) != 0) { setState(906); constructTriples(); } @@ -9345,8 +9356,8 @@ SparqlAutomaticParser::constructTriples() { _la = _input->LA(1); if (((_la & ~0x3fULL) == 0) && ((1ULL << _la) & 805371908) != 0 || - (((_la - 141) & ~0x3fULL) == 0) && - ((1ULL << (_la - 141)) & 29228991) != 0) { + (((_la - 142) & ~0x3fULL) == 0) && + ((1ULL << (_la - 142)) & 29228991) != 0) { setState(913); constructTriples(); } @@ -9541,7 +9552,7 @@ SparqlAutomaticParser::propertyList() { _la = _input->LA(1); if (_la == SparqlAutomaticParser::T__8 || - (((_la - 141) & ~0x3fULL) == 0) && ((1ULL << (_la - 141)) & 183) != 0) { + (((_la - 142) & ~0x3fULL) == 0) && ((1ULL << (_la - 142)) & 183) != 0) { setState(926); propertyListNotEmpty(); } @@ -9639,8 +9650,8 @@ SparqlAutomaticParser::propertyListNotEmpty() { _la = _input->LA(1); if (_la == SparqlAutomaticParser::T__8 || - (((_la - 141) & ~0x3fULL) == 0) && - ((1ULL << (_la - 141)) & 183) != 0) { + (((_la - 142) & ~0x3fULL) == 0) && + ((1ULL << (_la - 142)) & 183) != 0) { setState(932); verb(); setState(933); @@ -10072,7 +10083,7 @@ SparqlAutomaticParser::propertyListPath() { _la = _input->LA(1); if (((_la & ~0x3fULL) == 0) && ((1ULL << _la) & 37380) != 0 || - (((_la - 141) & ~0x3fULL) == 0) && ((1ULL << (_la - 141)) & 183) != 0) { + (((_la - 142) & ~0x3fULL) == 0) && ((1ULL << (_la - 142)) & 183) != 0) { setState(964); propertyListPathNotEmpty(); } @@ -10168,8 +10179,8 @@ SparqlAutomaticParser::propertyListPathNotEmpty() { _la = _input->LA(1); if (((_la & ~0x3fULL) == 0) && ((1ULL << _la) & 37380) != 0 || - (((_la - 141) & ~0x3fULL) == 0) && - ((1ULL << (_la - 141)) & 183) != 0) { + (((_la - 142) & ~0x3fULL) == 0) && + ((1ULL << (_la - 142)) & 183) != 0) { setState(969); tupleWithoutPath(); } @@ -11340,8 +11351,8 @@ SparqlAutomaticParser::pathNegatedPropertySet() { if (_la == SparqlAutomaticParser::T__8 || _la == SparqlAutomaticParser::T__11 || - (((_la - 141) & ~0x3fULL) == 0) && - ((1ULL << (_la - 141)) & 135) != 0) { + (((_la - 142) & ~0x3fULL) == 0) && + ((1ULL << (_la - 142)) & 135) != 0) { setState(1042); pathOneInPropertySet(); setState(1047); @@ -11933,8 +11944,8 @@ SparqlAutomaticParser::CollectionContext* SparqlAutomaticParser::collection() { _errHandler->sync(this); _la = _input->LA(1); } while (((_la & ~0x3fULL) == 0) && ((1ULL << _la) & 805371908) != 0 || - (((_la - 141) & ~0x3fULL) == 0) && - ((1ULL << (_la - 141)) & 29228991) != 0); + (((_la - 142) & ~0x3fULL) == 0) && + ((1ULL << (_la - 142)) & 29228991) != 0); setState(1088); match(SparqlAutomaticParser::T__2); @@ -12016,8 +12027,8 @@ SparqlAutomaticParser::collectionPath() { _errHandler->sync(this); _la = _input->LA(1); } while (((_la & ~0x3fULL) == 0) && ((1ULL << _la) & 805371908) != 0 || - (((_la - 141) & ~0x3fULL) == 0) && - ((1ULL << (_la - 141)) & 29228991) != 0); + (((_la - 142) & ~0x3fULL) == 0) && + ((1ULL << (_la - 142)) & 29228991) != 0); setState(1096); match(SparqlAutomaticParser::T__2); @@ -13267,8 +13278,8 @@ SparqlAutomaticParser::additiveExpression() { while (_la == SparqlAutomaticParser::T__12 || _la == SparqlAutomaticParser::T__25 || - (((_la - 152) & ~0x3fULL) == 0) && - ((1ULL << (_la - 152)) & 63) != 0) { + (((_la - 153) & ~0x3fULL) == 0) && + ((1ULL << (_la - 153)) & 63) != 0) { setState(1167); multiplicativeExpressionWithSign(); setState(1172); @@ -14126,6 +14137,7 @@ SparqlAutomaticParser::unaryExpression() { case SparqlAutomaticParser::MIN: case SparqlAutomaticParser::MAX: case SparqlAutomaticParser::AVG: + case SparqlAutomaticParser::STDEV: case SparqlAutomaticParser::SAMPLE: case SparqlAutomaticParser::IRI_REF: case SparqlAutomaticParser::PNAME_NS: @@ -14315,6 +14327,7 @@ SparqlAutomaticParser::primaryExpression() { case SparqlAutomaticParser::MIN: case SparqlAutomaticParser::MAX: case SparqlAutomaticParser::AVG: + case SparqlAutomaticParser::STDEV: case SparqlAutomaticParser::SAMPLE: { enterOuterAlt(_localctx, 2); setState(1221); @@ -14761,6 +14774,7 @@ SparqlAutomaticParser::builtInCall() { case SparqlAutomaticParser::MIN: case SparqlAutomaticParser::MAX: case SparqlAutomaticParser::AVG: + case SparqlAutomaticParser::STDEV: case SparqlAutomaticParser::SAMPLE: { enterOuterAlt(_localctx, 1); setState(1233); @@ -16022,6 +16036,10 @@ tree::TerminalNode* SparqlAutomaticParser::AggregateContext::AVG() { return getToken(SparqlAutomaticParser::AVG, 0); } +tree::TerminalNode* SparqlAutomaticParser::AggregateContext::STDEV() { + return getToken(SparqlAutomaticParser::STDEV, 0); +} + tree::TerminalNode* SparqlAutomaticParser::AggregateContext::SAMPLE() { return getToken(SparqlAutomaticParser::SAMPLE, 0); } @@ -16077,7 +16095,7 @@ SparqlAutomaticParser::AggregateContext* SparqlAutomaticParser::aggregate() { exitRule(); }); try { - setState(1600); + setState(1608); _errHandler->sync(this); switch (_input->LA(1)) { case SparqlAutomaticParser::COUNT: { @@ -16169,6 +16187,7 @@ SparqlAutomaticParser::AggregateContext* SparqlAutomaticParser::aggregate() { case SparqlAutomaticParser::MIN: case SparqlAutomaticParser::MAX: case SparqlAutomaticParser::AVG: + case SparqlAutomaticParser::STDEV: case SparqlAutomaticParser::SAMPLE: case SparqlAutomaticParser::IRI_REF: case SparqlAutomaticParser::PNAME_NS: @@ -16286,10 +16305,10 @@ SparqlAutomaticParser::AggregateContext* SparqlAutomaticParser::aggregate() { break; } - case SparqlAutomaticParser::SAMPLE: { + case SparqlAutomaticParser::STDEV: { enterOuterAlt(_localctx, 6); setState(1578); - match(SparqlAutomaticParser::SAMPLE); + match(SparqlAutomaticParser::STDEV); setState(1579); match(SparqlAutomaticParser::T__1); setState(1581); @@ -16307,10 +16326,10 @@ SparqlAutomaticParser::AggregateContext* SparqlAutomaticParser::aggregate() { break; } - case SparqlAutomaticParser::GROUP_CONCAT: { + case SparqlAutomaticParser::SAMPLE: { enterOuterAlt(_localctx, 7); setState(1586); - match(SparqlAutomaticParser::GROUP_CONCAT); + match(SparqlAutomaticParser::SAMPLE); setState(1587); match(SparqlAutomaticParser::T__1); setState(1589); @@ -16323,21 +16342,42 @@ SparqlAutomaticParser::AggregateContext* SparqlAutomaticParser::aggregate() { } setState(1591); expression(); - setState(1596); + setState(1592); + match(SparqlAutomaticParser::T__2); + break; + } + + case SparqlAutomaticParser::GROUP_CONCAT: { + enterOuterAlt(_localctx, 8); + setState(1594); + match(SparqlAutomaticParser::GROUP_CONCAT); + setState(1595); + match(SparqlAutomaticParser::T__1); + setState(1597); + _errHandler->sync(this); + + _la = _input->LA(1); + if (_la == SparqlAutomaticParser::DISTINCT) { + setState(1596); + match(SparqlAutomaticParser::DISTINCT); + } + setState(1599); + expression(); + setState(1604); _errHandler->sync(this); _la = _input->LA(1); if (_la == SparqlAutomaticParser::T__5) { - setState(1592); + setState(1600); match(SparqlAutomaticParser::T__5); - setState(1593); + setState(1601); match(SparqlAutomaticParser::SEPARATOR); - setState(1594); + setState(1602); match(SparqlAutomaticParser::T__19); - setState(1595); + setState(1603); string(); } - setState(1598); + setState(1606); match(SparqlAutomaticParser::T__2); break; } @@ -16412,15 +16452,15 @@ SparqlAutomaticParser::iriOrFunction() { }); try { enterOuterAlt(_localctx, 1); - setState(1602); + setState(1610); iri(); - setState(1604); + setState(1612); _errHandler->sync(this); _la = _input->LA(1); if (_la == SparqlAutomaticParser::T__1 || _la == SparqlAutomaticParser::NIL) { - setState(1603); + setState(1611); argList(); } @@ -16492,21 +16532,21 @@ SparqlAutomaticParser::RdfLiteralContext* SparqlAutomaticParser::rdfLiteral() { }); try { enterOuterAlt(_localctx, 1); - setState(1606); + setState(1614); string(); - setState(1610); + setState(1618); _errHandler->sync(this); switch (_input->LA(1)) { case SparqlAutomaticParser::LANGTAG: { - setState(1607); + setState(1615); match(SparqlAutomaticParser::LANGTAG); break; } case SparqlAutomaticParser::T__26: { - setState(1608); + setState(1616); match(SparqlAutomaticParser::T__26); - setState(1609); + setState(1617); iri(); break; } @@ -16649,14 +16689,14 @@ SparqlAutomaticParser::numericLiteral() { exitRule(); }); try { - setState(1615); + setState(1623); _errHandler->sync(this); switch (_input->LA(1)) { case SparqlAutomaticParser::INTEGER: case SparqlAutomaticParser::DECIMAL: case SparqlAutomaticParser::DOUBLE: { enterOuterAlt(_localctx, 1); - setState(1612); + setState(1620); numericLiteralUnsigned(); break; } @@ -16665,7 +16705,7 @@ SparqlAutomaticParser::numericLiteral() { case SparqlAutomaticParser::DECIMAL_POSITIVE: case SparqlAutomaticParser::DOUBLE_POSITIVE: { enterOuterAlt(_localctx, 2); - setState(1613); + setState(1621); numericLiteralPositive(); break; } @@ -16674,7 +16714,7 @@ SparqlAutomaticParser::numericLiteral() { case SparqlAutomaticParser::DECIMAL_NEGATIVE: case SparqlAutomaticParser::DOUBLE_NEGATIVE: { enterOuterAlt(_localctx, 3); - setState(1614); + setState(1622); numericLiteralNegative(); break; } @@ -16758,10 +16798,10 @@ SparqlAutomaticParser::numericLiteralUnsigned() { }); try { enterOuterAlt(_localctx, 1); - setState(1617); + setState(1625); _la = _input->LA(1); - if (!((((_la - 149) & ~0x3fULL) == 0) && - ((1ULL << (_la - 149)) & 7) != 0)) { + if (!((((_la - 150) & ~0x3fULL) == 0) && + ((1ULL << (_la - 150)) & 7) != 0)) { _errHandler->recoverInline(this); } else { _errHandler->reportMatch(this); @@ -16843,10 +16883,10 @@ SparqlAutomaticParser::numericLiteralPositive() { }); try { enterOuterAlt(_localctx, 1); - setState(1619); + setState(1627); _la = _input->LA(1); - if (!((((_la - 152) & ~0x3fULL) == 0) && - ((1ULL << (_la - 152)) & 7) != 0)) { + if (!((((_la - 153) & ~0x3fULL) == 0) && + ((1ULL << (_la - 153)) & 7) != 0)) { _errHandler->recoverInline(this); } else { _errHandler->reportMatch(this); @@ -16928,10 +16968,10 @@ SparqlAutomaticParser::numericLiteralNegative() { }); try { enterOuterAlt(_localctx, 1); - setState(1621); + setState(1629); _la = _input->LA(1); - if (!((((_la - 155) & ~0x3fULL) == 0) && - ((1ULL << (_la - 155)) & 7) != 0)) { + if (!((((_la - 156) & ~0x3fULL) == 0) && + ((1ULL << (_la - 156)) & 7) != 0)) { _errHandler->recoverInline(this); } else { _errHandler->reportMatch(this); @@ -16994,7 +17034,7 @@ SparqlAutomaticParser::booleanLiteral() { }); try { enterOuterAlt(_localctx, 1); - setState(1623); + setState(1631); _la = _input->LA(1); if (!(_la == SparqlAutomaticParser::T__27 @@ -17078,10 +17118,10 @@ SparqlAutomaticParser::StringContext* SparqlAutomaticParser::string() { }); try { enterOuterAlt(_localctx, 1); - setState(1625); + setState(1633); _la = _input->LA(1); - if (!((((_la - 159) & ~0x3fULL) == 0) && - ((1ULL << (_la - 159)) & 15) != 0)) { + if (!((((_la - 160) & ~0x3fULL) == 0) && + ((1ULL << (_la - 160)) & 15) != 0)) { _errHandler->recoverInline(this); } else { _errHandler->reportMatch(this); @@ -17156,26 +17196,26 @@ SparqlAutomaticParser::IriContext* SparqlAutomaticParser::iri() { }); try { enterOuterAlt(_localctx, 1); - setState(1628); + setState(1636); _errHandler->sync(this); _la = _input->LA(1); if (_la == SparqlAutomaticParser::PREFIX_LANGTAG) { - setState(1627); + setState(1635); match(SparqlAutomaticParser::PREFIX_LANGTAG); } - setState(1632); + setState(1640); _errHandler->sync(this); switch (_input->LA(1)) { case SparqlAutomaticParser::IRI_REF: { - setState(1630); + setState(1638); iriref(); break; } case SparqlAutomaticParser::PNAME_NS: case SparqlAutomaticParser::PNAME_LN: { - setState(1631); + setState(1639); prefixedName(); break; } @@ -17248,19 +17288,19 @@ SparqlAutomaticParser::prefixedName() { exitRule(); }); try { - setState(1636); + setState(1644); _errHandler->sync(this); switch (_input->LA(1)) { case SparqlAutomaticParser::PNAME_LN: { enterOuterAlt(_localctx, 1); - setState(1634); + setState(1642); pnameLn(); break; } case SparqlAutomaticParser::PNAME_NS: { enterOuterAlt(_localctx, 2); - setState(1635); + setState(1643); pnameNs(); break; } @@ -17333,7 +17373,7 @@ SparqlAutomaticParser::BlankNodeContext* SparqlAutomaticParser::blankNode() { }); try { enterOuterAlt(_localctx, 1); - setState(1638); + setState(1646); _la = _input->LA(1); if (!(_la == SparqlAutomaticParser::BLANK_NODE_LABEL @@ -17402,7 +17442,7 @@ SparqlAutomaticParser::IrirefContext* SparqlAutomaticParser::iriref() { }); try { enterOuterAlt(_localctx, 1); - setState(1640); + setState(1648); match(SparqlAutomaticParser::IRI_REF); } catch (RecognitionException& e) { @@ -17463,7 +17503,7 @@ SparqlAutomaticParser::PnameLnContext* SparqlAutomaticParser::pnameLn() { }); try { enterOuterAlt(_localctx, 1); - setState(1642); + setState(1650); match(SparqlAutomaticParser::PNAME_LN); } catch (RecognitionException& e) { @@ -17524,7 +17564,7 @@ SparqlAutomaticParser::PnameNsContext* SparqlAutomaticParser::pnameNs() { }); try { enterOuterAlt(_localctx, 1); - setState(1644); + setState(1652); match(SparqlAutomaticParser::PNAME_NS); } catch (RecognitionException& e) { diff --git a/src/parser/sparqlParser/generated/SparqlAutomaticParser.h b/src/parser/sparqlParser/generated/SparqlAutomaticParser.h index 9e2449ee69..9b1fce885b 100644 --- a/src/parser/sparqlParser/generated/SparqlAutomaticParser.h +++ b/src/parser/sparqlParser/generated/SparqlAutomaticParser.h @@ -146,43 +146,44 @@ class SparqlAutomaticParser : public antlr4::Parser { MIN = 136, MAX = 137, AVG = 138, - SAMPLE = 139, - SEPARATOR = 140, - IRI_REF = 141, - PNAME_NS = 142, - PNAME_LN = 143, - BLANK_NODE_LABEL = 144, - VAR1 = 145, - VAR2 = 146, - LANGTAG = 147, - PREFIX_LANGTAG = 148, - INTEGER = 149, - DECIMAL = 150, - DOUBLE = 151, - INTEGER_POSITIVE = 152, - DECIMAL_POSITIVE = 153, - DOUBLE_POSITIVE = 154, - INTEGER_NEGATIVE = 155, - DECIMAL_NEGATIVE = 156, - DOUBLE_NEGATIVE = 157, - EXPONENT = 158, - STRING_LITERAL1 = 159, - STRING_LITERAL2 = 160, - STRING_LITERAL_LONG1 = 161, - STRING_LITERAL_LONG2 = 162, - ECHAR = 163, - NIL = 164, - ANON = 165, - PN_CHARS_U = 166, - VARNAME = 167, - PN_PREFIX = 168, - PN_LOCAL = 169, - PLX = 170, - PERCENT = 171, - HEX = 172, - PN_LOCAL_ESC = 173, - WS = 174, - COMMENTS = 175 + STDEV = 139, + SAMPLE = 140, + SEPARATOR = 141, + IRI_REF = 142, + PNAME_NS = 143, + PNAME_LN = 144, + BLANK_NODE_LABEL = 145, + VAR1 = 146, + VAR2 = 147, + LANGTAG = 148, + PREFIX_LANGTAG = 149, + INTEGER = 150, + DECIMAL = 151, + DOUBLE = 152, + INTEGER_POSITIVE = 153, + DECIMAL_POSITIVE = 154, + DOUBLE_POSITIVE = 155, + INTEGER_NEGATIVE = 156, + DECIMAL_NEGATIVE = 157, + DOUBLE_NEGATIVE = 158, + EXPONENT = 159, + STRING_LITERAL1 = 160, + STRING_LITERAL2 = 161, + STRING_LITERAL_LONG1 = 162, + STRING_LITERAL_LONG2 = 163, + ECHAR = 164, + NIL = 165, + ANON = 166, + PN_CHARS_U = 167, + VARNAME = 168, + PN_PREFIX = 169, + PN_LOCAL = 170, + PLX = 171, + PERCENT = 172, + HEX = 173, + PN_LOCAL_ESC = 174, + WS = 175, + COMMENTS = 176 }; enum { @@ -2929,6 +2930,7 @@ class SparqlAutomaticParser : public antlr4::Parser { antlr4::tree::TerminalNode* MIN(); antlr4::tree::TerminalNode* MAX(); antlr4::tree::TerminalNode* AVG(); + antlr4::tree::TerminalNode* STDEV(); antlr4::tree::TerminalNode* SAMPLE(); antlr4::tree::TerminalNode* GROUP_CONCAT(); antlr4::tree::TerminalNode* SEPARATOR(); diff --git a/test/AggregateExpressionTest.cpp b/test/AggregateExpressionTest.cpp index 2977548812..54e6d72e79 100644 --- a/test/AggregateExpressionTest.cpp +++ b/test/AggregateExpressionTest.cpp @@ -2,6 +2,9 @@ // Chair of Algorithms and Data Structures // Author: Johannes Kalmbach +#include +#include + #include "./SparqlExpressionTestHelpers.h" #include "./util/GTestHelpers.h" #include "./util/IdTableHelpers.h" @@ -11,6 +14,9 @@ #include "engine/sparqlExpressions/AggregateExpression.h" #include "engine/sparqlExpressions/CountStarExpression.h" #include "engine/sparqlExpressions/SampleExpression.h" +#include "engine/sparqlExpressions/SparqlExpressionTypes.h" +#include "engine/sparqlExpressions/StdevExpression.h" +#include "global/ValueId.h" #include "gtest/gtest.h" using namespace sparqlExpression; @@ -94,6 +100,33 @@ TEST(AggregateExpression, avg) { testAvgString({lit("alpha"), lit("äpfel"), lit("Beta"), lit("unfug")}, U); } +// Test `StdevExpression`. +TEST(StdevExpression, avg) { + auto testStdevId = testAggregate; + + auto inputAsVector = std::vector{I(3), D(0), I(0), I(4), I(-2)}; + VectorWithMemoryLimit input(inputAsVector.begin(), + inputAsVector.end(), makeAllocator()); + auto d = std::make_unique(input.clone()); + auto t = TestContext{}; + t.context._endIndex = 5; + StdevExpression m{false, std::move(d)}; + auto resAsVariant = m.evaluate(&t.context); + ASSERT_NEAR(std::get(resAsVariant).getDouble(), 2.44949, 0.0001); + + testStdevId({D(2), D(2), D(2), D(2)}, D(0), true); + + testStdevId({I(3), U}, U); + testStdevId({I(3), NaN}, NaN); + + testStdevId({}, D(0)); + testStdevId({D(500)}, D(0)); + testStdevId({D(500), D(500), D(500)}, D(0)); + + auto testStdevString = testAggregate; + testStdevString({lit("alpha"), lit("äpfel"), lit("Beta"), lit("unfug")}, U); +} + // Test `MinExpression`. TEST(AggregateExpression, min) { auto testMinId = testAggregate; diff --git a/test/GroupByTest.cpp b/test/GroupByTest.cpp index e12bac05a7..2abdd5ec38 100644 --- a/test/GroupByTest.cpp +++ b/test/GroupByTest.cpp @@ -23,6 +23,7 @@ #include "engine/sparqlExpressions/LiteralExpression.h" #include "engine/sparqlExpressions/NaryExpression.h" #include "engine/sparqlExpressions/SampleExpression.h" +#include "engine/sparqlExpressions/StdevExpression.h" #include "global/RuntimeParameters.h" #include "gtest/gtest.h" #include "index/ConstantsIndexBuilding.h" diff --git a/test/SparqlAntlrParserTest.cpp b/test/SparqlAntlrParserTest.cpp index 4b93153522..14a49b6d19 100644 --- a/test/SparqlAntlrParserTest.cpp +++ b/test/SparqlAntlrParserTest.cpp @@ -25,6 +25,7 @@ #include "engine/sparqlExpressions/RegexExpression.h" #include "engine/sparqlExpressions/RelationalExpressions.h" #include "engine/sparqlExpressions/SampleExpression.h" +#include "engine/sparqlExpressions/StdevExpression.h" #include "engine/sparqlExpressions/UuidExpressions.h" #include "parser/ConstructClause.h" #include "parser/SparqlParserHelpers.h" @@ -1854,6 +1855,23 @@ ::testing::Matcher matchAggregate( AD_PROPERTY(Exp, children, ElementsAre(variableExpressionMatcher(child))), WhenDynamicCastTo(innerMatcher))); } + +// Return a matcher that checks whether a given `SparqlExpression::Ptr` actually +// points to an `AggregateExpr` and that the distinctness of the aggregate +// expression matches. It does not check the child. This is required to test +// aggregates that implicitly replace their child, like `StdevExpression`. +template +::testing::Matcher matchAggregateWithoutChild( + bool distinct) { + using namespace ::testing; + using namespace builtInCallTestHelpers; + using Exp = SparqlExpression; + + using enum SparqlExpression::AggregateStatus; + auto aggregateStatus = distinct ? DistinctAggregate : NonDistinctAggregate; + return Pointee(AllOf(AD_PROPERTY(Exp, isAggregate, Eq(aggregateStatus)), + WhenDynamicCastTo(testing::_))); +} } // namespace aggregateTestHelpers // ___________________________________________________________ @@ -1924,6 +1942,17 @@ TEST(SparqlParser, aggregateExpressions) { expectAggregate( "group_concat(DISTINCT ?x; SEPARATOR=\";\")", matchAggregate(true, V{"?x"}, separator(";"))); + + // The STDEV expression + // Here we don't match the child, because StdevExpression replaces it with a + // DeviationExpression. + expectAggregate("STDEV(?x)", + matchAggregateWithoutChild(false)); + expectAggregate("stdev(?x)", + matchAggregateWithoutChild(false)); + // A distinct stdev is probably not very useful, but should be possible anyway + expectAggregate("STDEV(DISTINCT ?x)", + matchAggregateWithoutChild(true)); } TEST(SparqlParser, Quads) { diff --git a/test/SparqlExpressionTest.cpp b/test/SparqlExpressionTest.cpp index f55d8ed0d1..391f4fcfd8 100644 --- a/test/SparqlExpressionTest.cpp +++ b/test/SparqlExpressionTest.cpp @@ -17,6 +17,7 @@ #include "engine/sparqlExpressions/NaryExpression.h" #include "engine/sparqlExpressions/SampleExpression.h" #include "engine/sparqlExpressions/SparqlExpression.h" +#include "engine/sparqlExpressions/StdevExpression.h" #include "parser/GeoPoint.h" #include "util/AllocatorTestHelpers.h" #include "util/Conversions.h" @@ -1373,6 +1374,9 @@ TEST(SparqlExpression, isAggregateAndIsDistinct) { EXPECT_THAT(GroupConcatExpression(false, varX(), " "), match(false)); EXPECT_THAT(GroupConcatExpression(true, varX(), " "), match(true)); + EXPECT_THAT(StdevExpression(false, varX()), match(false)); + EXPECT_THAT(StdevExpression(true, varX()), match(true)); + EXPECT_THAT(SampleExpression(false, varX()), match(false)); // For `SAMPLE` the distinctness makes no difference, so we always return `not // distinct`. diff --git a/test/SparqlParserTest.cpp b/test/SparqlParserTest.cpp index dc3967aff3..b8499b0a99 100644 --- a/test/SparqlParserTest.cpp +++ b/test/SparqlParserTest.cpp @@ -951,6 +951,20 @@ TEST(ParserTest, testSolutionModifiers) { ASSERT_FALSE(pq._orderBy[0].isDescending_); } + { + auto pq = SparqlParser::parseQuery( + "SELECT ?r (STDEV(?r) as ?stdev) WHERE {" + "?a ?b ." + "?a ql:has-relation ?r }" + "GROUP BY ?r " + "ORDER BY ?stdev"); + ASSERT_EQ(1u, pq.children().size()); + ASSERT_EQ(1u, pq._orderBy.size()); + EXPECT_THAT(pq, m::GroupByVariables({Var{"?r"}})); + ASSERT_EQ(Var{"?stdev"}, pq._orderBy[0].variable_); + ASSERT_FALSE(pq._orderBy[0].isDescending_); + } + { auto pq = SparqlParser::parseQuery( "SELECT ?r (COUNT(DISTINCT ?r) as ?count) WHERE {" From e9e6f8df9d7cc287daf1028c171d787623e04b8f Mon Sep 17 00:00:00 2001 From: Johannes Kalmbach Date: Thu, 14 Nov 2024 16:54:58 +0100 Subject: [PATCH 54/81] Merge located triples when performing index scans (#1597) PR #1582 and #1603 gave all index-lookup methods access to a snapshot of the (located) delta triples. With this change, these triples are now merged with the original triples during query processing whenever necessary. When an index block does not contain any located triples, the performance for accessing that block is the same as before. The methods for obtaining the result size of an index scan now have two versions: one for obtaining an approximate size (this is cheap because it can be computed from the metadata of the blocks and the located triples) and one for obtaining the exact size (if there are located triples this is expensive because it requires reading and decompressing a block and merging the located triples). --- src/engine/IndexScan.cpp | 15 +- src/engine/IndexScan.h | 17 +- src/engine/Join.cpp | 19 +- src/index/CompressedRelation.cpp | 573 +++++++++++++++++++++---------- src/index/CompressedRelation.h | 138 +++++--- src/index/LocatedTriples.cpp | 29 +- src/index/LocatedTriples.h | 28 +- src/index/Permutation.cpp | 10 + src/index/Permutation.h | 12 +- test/CompressedRelationsTest.cpp | 184 +++++++--- test/DeltaTriplesTest.cpp | 25 +- test/engine/IndexScanTest.cpp | 8 +- test/util/GTestHelpers.h | 13 +- 13 files changed, 733 insertions(+), 338 deletions(-) diff --git a/src/engine/IndexScan.cpp b/src/engine/IndexScan.cpp index 650fc3adc3..2040d3d8de 100644 --- a/src/engine/IndexScan.cpp +++ b/src/engine/IndexScan.cpp @@ -36,7 +36,7 @@ IndexScan::IndexScan(QueryExecutionContext* qec, Permutation::Enum permutation, additionalColumns_.push_back(idx); additionalVariables_.push_back(variable); } - sizeEstimate_ = computeSizeEstimate(); + std::tie(sizeEstimateIsExact_, sizeEstimate_) = computeSizeEstimate(); // Check the following invariant: All the variables must be at the end of the // permuted triple. For example in the PSO permutation, either only the O, or @@ -215,7 +215,18 @@ ProtoResult IndexScan::computeResult(bool requestLaziness) { } // _____________________________________________________________________________ -size_t IndexScan::computeSizeEstimate() const { +std::pair IndexScan::computeSizeEstimate() const { + AD_CORRECTNESS_CHECK(_executionContext); + auto [lower, upper] = getIndex() + .getImpl() + .getPermutation(permutation()) + .getSizeEstimateForScan(getScanSpecification(), + locatedTriplesSnapshot()); + return {lower == upper, std::midpoint(lower, upper)}; +} + +// _____________________________________________________________________________ +size_t IndexScan::getExactSize() const { AD_CORRECTNESS_CHECK(_executionContext); return getIndex().getResultSizeOfScan(getScanSpecification(), permutation_, locatedTriplesSnapshot()); diff --git a/src/engine/IndexScan.h b/src/engine/IndexScan.h index 540acd5fa1..c5efb552f1 100644 --- a/src/engine/IndexScan.h +++ b/src/engine/IndexScan.h @@ -28,6 +28,7 @@ class IndexScan final : public Operation { PrefilterIndexPairs prefilters_; size_t numVariables_; size_t sizeEstimate_; + bool sizeEstimateIsExact_; vector multiplicity_; // Additional columns (e.g. patterns) that are being retrieved in addition to @@ -73,7 +74,7 @@ class IndexScan final : public Operation { // Return the exact result size of the index scan. This is always known as it // can be read from the Metadata. - size_t getExactSize() const { return sizeEstimate_; } + size_t getExactSize() const; // Return two generators that lazily yield the results of `s1` and `s2` in // blocks, but only the blocks that can theoretically contain matching rows @@ -92,7 +93,7 @@ class IndexScan final : public Operation { private: // TODO Make the `getSizeEstimateBeforeLimit()` function `const` for // ALL the `Operations`. - uint64_t getSizeEstimateBeforeLimit() override { return getExactSize(); } + uint64_t getSizeEstimateBeforeLimit() override { return sizeEstimate_; } public: size_t getCostEstimate() override; @@ -107,7 +108,9 @@ class IndexScan final : public Operation { return multiplicity_[col]; } - bool knownEmptyResult() override { return getExactSize() == 0; } + bool knownEmptyResult() override { + return sizeEstimateIsExact_ && sizeEstimate_ == 0; + } bool isIndexScanWithNumVariables(size_t target) const override { return numVariables() == target; @@ -117,7 +120,7 @@ class IndexScan final : public Operation { // size of wikidata, so we don't even need to try and waste performance. bool unlikelyToFitInCache( ad_utility::MemorySize maxCacheableSize) const override { - return ad_utility::MemorySize::bytes(getExactSize() * getResultWidth() * + return ad_utility::MemorySize::bytes(sizeEstimate_ * getResultWidth() * sizeof(Id)) > maxCacheableSize; } @@ -138,7 +141,11 @@ class IndexScan final : public Operation { vector getChildren() override { return {}; } - size_t computeSizeEstimate() const; + // Compute the size estimate of the index scan, taking delta triples (from + // the `queryExecutionContext_`) into account. The `bool` is true iff the + // estimate is exact. If not, the estimate is the mean of the lower and upper + // bound. + std::pair computeSizeEstimate() const; std::string getCacheKeyImpl() const override; diff --git a/src/engine/Join.cpp b/src/engine/Join.cpp index 92a9bf66b3..184df75128 100644 --- a/src/engine/Join.cpp +++ b/src/engine/Join.cpp @@ -566,13 +566,18 @@ void updateRuntimeInfoForLazyScan( rti.addDetail("num-blocks-read", metadata.numBlocksRead_); rti.addDetail("num-blocks-all", metadata.numBlocksAll_); rti.addDetail("num-elements-read", metadata.numElementsRead_); - if (metadata.numBlocksSkippedBecauseOfGraph_ > 0) { - rti.addDetail("num-blocks-skipped-graph", - metadata.numBlocksSkippedBecauseOfGraph_); - } - if (metadata.numBlocksPostprocessed_ > 0) { - rti.addDetail("num-blocks-postprocessed", metadata.numBlocksPostprocessed_); - } + + // Add more details, but only if the respective value is non-zero. + auto updateIfPositive = [&rti](const auto& value, const std::string& key) { + if (value > 0) { + rti.addDetail(key, value); + } + }; + updateIfPositive(metadata.numBlocksSkippedBecauseOfGraph_, + "num-blocks-skipped-graph"); + updateIfPositive(metadata.numBlocksPostprocessed_, + "num-blocks-postprocessed"); + updateIfPositive(metadata.numBlocksWithUpdate_, "num-blocks-with-update"); } } // namespace diff --git a/src/index/CompressedRelation.cpp b/src/index/CompressedRelation.cpp index 4ec41069ae..8e41809c5f 100644 --- a/src/index/CompressedRelation.cpp +++ b/src/index/CompressedRelation.cpp @@ -11,9 +11,8 @@ #include "engine/idTable/IdTable.h" #include "global/RuntimeParameters.h" #include "index/ConstantsIndexBuilding.h" -#include "util/Cache.h" +#include "index/LocatedTriples.h" #include "util/CompressionUsingZstd/ZstdWrapper.h" -#include "util/ConcurrentCache.h" #include "util/Generator.h" #include "util/OnDestructionDontThrowDuringStackUnwinding.h" #include "util/OverloadCallOperator.h" @@ -54,20 +53,30 @@ static void pruneBlock(auto& block, LimitOffsetClause& limitOffset) { // ____________________________________________________________________________ CompressedRelationReader::IdTableGenerator CompressedRelationReader::asyncParallelBlockGenerator( - auto beginBlock, auto endBlock, ColumnIndices columnIndices, - CancellationHandle cancellationHandle, LimitOffsetClause& limitOffset, - FilterDuplicatesAndGraphs blockGraphFilter) const { - LazyScanMetadata& details = co_await cppcoro::getDetails; + auto beginBlock, auto endBlock, const ScanImplConfig& scanConfig, + CancellationHandle cancellationHandle, + LimitOffsetClause& limitOffset) const { + // Empty range. if (beginBlock == endBlock) { co_return; } - std::atomic numBlocksPostprocessed = 0; + + // Preparation. + const auto& columnIndices = scanConfig.scanColumns_; + const auto& blockGraphFilter = scanConfig.graphFilter_; + LazyScanMetadata& details = co_await cppcoro::getDetails; const size_t queueSize = RuntimeParameters().get<"lazy-index-scan-queue-size">(); auto blockMetadataIterator = beginBlock; std::mutex blockIteratorMutex; + + // Helper lambda that reads and decompessed the next block and returns it + // together with its index relative to `beginBlock`. Return `std::nullopt` + // when `endBlock` is reached. Returns `std::nullopt` for the block if it + // is skipped due to the graph filter. auto readAndDecompressBlock = [&]() - -> std::optional>> { + -> std::optional< + std::pair>> { cancellationHandle->throwIfCancelled(); std::unique_lock lock{blockIteratorMutex}; if (blockMetadataIterator == endBlock) { @@ -93,44 +102,43 @@ CompressedRelationReader::asyncParallelBlockGenerator( CompressedBlock compressedBlock = readCompressedBlockFromFile(blockMetadata, columnIndices); lock.unlock(); - auto decompressedBlock = - decompressBlock(compressedBlock, blockMetadata.numRows_); - if (blockGraphFilter.postprocessBlock(decompressedBlock, blockMetadata)) { - numBlocksPostprocessed++; - } - return std::pair{myIndex, std::optional{std::move(decompressedBlock)}}; + auto decompressedBlockAndMetadata = decompressAndPostprocessBlock( + compressedBlock, blockMetadata.numRows_, scanConfig, blockMetadata); + return std::pair{myIndex, + std::optional{std::move(decompressedBlockAndMetadata)}}; }; + + // Prepare queue for reading and decompressing blocks concurrently using + // `numThreads` threads. const size_t numThreads = RuntimeParameters().get<"lazy-index-scan-num-threads">(); - ad_utility::Timer popTimer{ad_utility::timer::Timer::InitialStatus::Started}; // In case the coroutine is destroyed early we still want to have this // information. auto setTimer = ad_utility::makeOnDestructionDontThrowDuringStackUnwinding( [&details, &popTimer]() { details.blockingTime_ = popTimer.msecs(); }); - auto queue = ad_utility::data_structures::queueManager< ad_utility::data_structures::OrderedThreadSafeQueue< - std::optional>>(queueSize, numThreads, - readAndDecompressBlock); - for (std::optional& optBlock : queue) { + std::optional>>(queueSize, numThreads, + readAndDecompressBlock); + + // Yield the blocks (in the right order) as soon as they become available. + // Stop when all the blocks have been yielded or the LIMIT of the query is + // reached. Keep track of various statistics. + for (std::optional& optBlock : queue) { popTimer.stop(); cancellationHandle->throwIfCancelled(); + details.update(optBlock); if (optBlock.has_value()) { - auto& block = optBlock.value(); - ++details.numBlocksRead_; - details.numElementsRead_ += block.numRows(); + auto& block = optBlock.value().block_; pruneBlock(block, limitOffset); details.numElementsYielded_ += block.numRows(); - details.numBlocksPostprocessed_ = numBlocksPostprocessed; if (!block.empty()) { co_yield block; } if (limitOffset._limit.value_or(1) == 0) { co_return; } - } else { - ++details.numBlocksSkippedBecauseOfGraph_; } popTimer.cont(); } @@ -229,65 +237,38 @@ CompressedRelationReader::IdTableGenerator CompressedRelationReader::lazyScan( ColumnIndices additionalColumns, CancellationHandle cancellationHandle, [[maybe_unused]] const LocatedTriplesPerBlock& locatedTriplesPerBlock, LimitOffsetClause limitOffset) const { - // We will modify `limitOffset` as we go, so we have to copy the original - // value for sanity checks which we apply later. - const auto originalLimit = limitOffset; AD_CONTRACT_CHECK(cancellationHandle); - auto relevantBlocks = getRelevantBlocks(scanSpec, blockMetadata); - auto [beginBlockMetadata, endBlockMetadata] = getBeginAndEnd(relevantBlocks); - LazyScanMetadata& details = co_await cppcoro::getDetails; - size_t numBlocksTotal = endBlockMetadata - beginBlockMetadata; + // We will modify `limitOffset` as we go. We make a copy of the original + // value for some sanity checks at the end of the function. + const auto originalLimit = limitOffset; - if (beginBlockMetadata == endBlockMetadata) { + // Compute the sequence of relevant blocks. If the sequence is empty, there + // is nothing to yield. + auto relevantBlocks = getRelevantBlocks(scanSpec, blockMetadata); + if (std::ranges::empty(relevantBlocks)) { co_return; } - auto columnIndices = prepareColumnIndices(scanSpec, additionalColumns); - - // If we need to filter by the graph ID of the triples, then we need to add - // the graph column to the scan. If the graph column is not needed as an - // output anyway, then we have to delete it after the filtering. The following - // lambda determines the column index in which the graph column will reside - // (and adds it if necessary), and also determines whether we have to delete - // this column. - auto [graphColumnIndex, - deleteGraphColumn] = [&]() -> std::pair { - if (!scanSpec.graphsToFilter().has_value()) { - // No filtering required, these are dummy values that are ignored by the - // filtering logic. - return {0, false}; - } - auto idx = static_cast( - std::ranges::find(columnIndices, ADDITIONAL_COLUMN_GRAPH_ID) - - columnIndices.begin()); - bool deleteColumn = false; - if (idx == columnIndices.size()) { - columnIndices.push_back(ADDITIONAL_COLUMN_GRAPH_ID); - deleteColumn = true; - } - return {idx, deleteColumn}; - }(); - - auto filterDuplicatesAndGraphs = FilterDuplicatesAndGraphs{ - scanSpec.graphsToFilter(), graphColumnIndex, deleteGraphColumn}; + // Some preparation. + auto [beginBlockMetadata, endBlockMetadata] = getBeginAndEnd(relevantBlocks); + LazyScanMetadata& details = co_await cppcoro::getDetails; + size_t numBlocksTotal = endBlockMetadata - beginBlockMetadata; + auto config = + getScanConfig(scanSpec, additionalColumns, locatedTriplesPerBlock); + // Helper lambda for reading the first and last block, of which only a part + // is needed. auto getIncompleteBlock = [&](auto it) { - auto result = readPossiblyIncompleteBlock(scanSpec, *it, std::ref(details), - columnIndices); + auto result = readPossiblyIncompleteBlock( + scanSpec, config, *it, std::ref(details), locatedTriplesPerBlock); cancellationHandle->throwIfCancelled(); return result; }; + // Get and yield the first block. if (beginBlockMetadata < endBlockMetadata) { auto block = getIncompleteBlock(beginBlockMetadata); - if (filterDuplicatesAndGraphs.postprocessBlock(block, - *beginBlockMetadata)) { - ++details.numBlocksPostprocessed_; - } - // TODO Can we merge this `pruneBlock` with the above `postprocess` - // method? And can /should we include the skipped blocks into the - // `limitOffset` calculation? pruneBlock(block, limitOffset); details.numElementsYielded_ += block.numRows(); if (!block.empty()) { @@ -295,88 +276,107 @@ CompressedRelationReader::IdTableGenerator CompressedRelationReader::lazyScan( } } + // Get and yield the remaining blocks. if (beginBlockMetadata + 1 < endBlockMetadata) { - // We copy the cancellationHandle because it is still captured by reference - // inside the `getIncompleteBlock` lambda. + // We copy the cancellationHandle because it is still captured by + // reference inside the `getIncompleteBlock` lambda. auto blockGenerator = asyncParallelBlockGenerator( - beginBlockMetadata + 1, endBlockMetadata - 1, columnIndices, - cancellationHandle, limitOffset, filterDuplicatesAndGraphs); + beginBlockMetadata + 1, endBlockMetadata - 1, config, + cancellationHandle, limitOffset); blockGenerator.setDetailsPointer(&details); for (auto& block : blockGenerator) { co_yield block; } auto lastBlock = getIncompleteBlock(endBlockMetadata - 1); - if (filterDuplicatesAndGraphs.postprocessBlock(lastBlock, - *(endBlockMetadata - 1))) { - ++details.numBlocksPostprocessed_; - } pruneBlock(lastBlock, limitOffset); if (!lastBlock.empty()) { details.numElementsYielded_ += lastBlock.numRows(); co_yield lastBlock; } } + + // Some sanity checks. const auto& limit = originalLimit._limit; AD_CORRECTNESS_CHECK(!limit.has_value() || details.numElementsYielded_ <= limit.value()); AD_CORRECTNESS_CHECK( - numBlocksTotal == - (details.numBlocksRead_ + details.numBlocksSkippedBecauseOfGraph_) || - !limitOffset.isUnconstrained()); + numBlocksTotal == (details.numBlocksRead_ + + details.numBlocksSkippedBecauseOfGraph_) || + !limitOffset.isUnconstrained(), + [&]() { + return absl::StrCat(numBlocksTotal, " ", details.numBlocksRead_, " ", + details.numBlocksSkippedBecauseOfGraph_); + }); } +// Helper function that enables a comparison of a triple with an `Id` in +// the function `getBlocksForJoin` below. +// +// If the given triple matches `col0Id` of the given `ScanSpecification`, then +// `col1Id` is returned. +// respective other `Id` of the triple is returned. +// +// If the given triple matches neither, a sentinel value is returned (`Id::min` +// if the triple is lower than all triples matching the `ScanSpecification`, or +// `Id::max` if it is higher). namespace { -// An internal helper function for the `getBlocksForJoin` functions below. -// Get the ID from the `triple` that pertains to the join column (the col2 if -// the col1 is specified, else the col1). There are two special cases: If the -// triple doesn't match the `col0` and (if specified) the `col1` then a sentinel -// value is returned that is `Id::min` if the triple is lower than all matching -// triples, and `Id::max` if is higher. That way we can consistently compare a -// single ID from a join column with a complete triple from a block. auto getRelevantIdFromTriple( CompressedBlockMetadata::PermutedTriple triple, const CompressedRelationReader::ScanSpecAndBlocksAndBounds& metadataAndBlocks) { - auto idForNonMatchingBlock = [](Id fromTriple, Id key, Id minKey, - Id maxKey) -> std::optional { - if (fromTriple < key) { - return minKey; - } - if (fromTriple > key) { - return maxKey; - } - return std::nullopt; - }; - + // The `ScanSpecifcation`, which must ask for at least one column. const auto& scanSpec = metadataAndBlocks.scanSpec_; - AD_CORRECTNESS_CHECK(!scanSpec.col2Id().has_value()); + AD_CORRECTNESS_CHECK(!scanSpec.col2Id()); - auto [minKey, maxKey] = [&]() { + // For a full scan, return the triples's `col0Id`. + if (!scanSpec.col0Id().has_value()) { + return triple.col0Id_; + } + + // Compute the following range: If the `scanSpec` specifies both `col0Id` and + // `col1Id`, the first and last `col2Id` of the blocks. If the `scanSpec` + // specifies only `col0Id`, the first and last `col1Id` of the blocks. + auto [minId, maxId] = [&]() { const auto& [first, last] = metadataAndBlocks.firstAndLastTriple_; if (scanSpec.col1Id().has_value()) { return std::array{first.col2Id_, last.col2Id_}; - } else if (scanSpec.col0Id().has_value()) { - return std::array{first.col1Id_, last.col1Id_}; } else { - return std::array{Id::min(), Id::max()}; + AD_CORRECTNESS_CHECK(scanSpec.col0Id().has_value()); + return std::array{first.col1Id_, last.col1Id_}; } }(); - if (!scanSpec.col0Id().has_value()) { - return triple.col0Id_; - } + // Helper lambda that returns `std::nullopt` if `idFromTriple` equals `id`, + // `minId` if is smaller, and `maxId` if it is larger. + auto idForNonMatchingBlock = [](Id idFromTriple, Id id, Id minId, + Id maxId) -> std::optional { + if (idFromTriple < id) { + return minId; + } + if (idFromTriple > id) { + return maxId; + } + return std::nullopt; + }; + // If the `col0Id` of the triple does not match that of the `scanSpec`, + // return `minId` (if it is smaller) or `maxId` (if it is larger). if (auto optId = idForNonMatchingBlock( - triple.col0Id_, scanSpec.col0Id().value(), minKey, maxKey)) { + triple.col0Id_, scanSpec.col0Id().value(), minId, maxId)) { return optId.value(); } + // If the `col0Id` of the triple matches that of the `scanSpec`, and the + // `scanSpec` does not specify `col1Id`, return the triples's `col1Id`. if (!scanSpec.col1Id().has_value()) { return triple.col1Id_; } - return idForNonMatchingBlock(triple.col1Id_, scanSpec.col1Id().value(), - minKey, maxKey) + // If the `col1Id` of the triple matches that of the `scanSpec`, return the + // triples's `col2Id`. Otherwise, return `minId` (if it is smaller) or `maxId` + // (if it is larger). + return idForNonMatchingBlock(triple.col1Id_, scanSpec.col1Id().value(), minId, + maxId) .value_or(triple.col2Id_); } } // namespace @@ -413,10 +413,9 @@ std::vector CompressedRelationReader::getBlocksForJoin( // Find the matching blocks by performing binary search on the `joinColumn`. // Note that it is tempting to reuse the `zipperJoinWithUndef` routine, but - // this doesn't work because the implicit equality defined by `!lessThan(a,b) - // && !lessThan(b, a)` is not transitive. + // this doesn't work because the implicit equality defined by + // `!lessThan(a,b) && !lessThan(b, a)` is not transitive. std::vector result; - auto blockIsNeeded = [&joinColumn, &lessThan](const auto& block) { return !std::ranges::equal_range(joinColumn, block, lessThan).empty(); }; @@ -502,7 +501,8 @@ IdTable CompressedRelationReader::scan( auto columnIndices = prepareColumnIndices(scanSpec, additionalColumns); IdTable result(columnIndices.size(), allocator_); - // Compute an upper bound for the size and reserve enough space in the result. + // Compute an upper bound for the size and reserve enough space in the + // result. auto relevantBlocks = getRelevantBlocks(scanSpec, blocks); auto sizes = relevantBlocks | std::views::transform(&CompressedBlockMetadata::numRows_); @@ -525,34 +525,53 @@ IdTable CompressedRelationReader::scan( // ____________________________________________________________________________ DecompressedBlock CompressedRelationReader::readPossiblyIncompleteBlock( - const ScanSpecification& scanSpec, + const ScanSpecification& scanSpec, const ScanImplConfig& scanConfig, const CompressedBlockMetadata& blockMetadata, std::optional> scanMetadata, - ColumnIndicesRef columnIndices) const { - std::vector allColumns; + const LocatedTriplesPerBlock& locatedTriples) const { + AD_CORRECTNESS_CHECK(ADDITIONAL_COLUMN_GRAPH_ID < + blockMetadata.offsetsAndCompressedSize_.size()); + + // We first scan the complete block including ALL columns. + std::vector allAdditionalColumns; std::ranges::copy( - ad_utility::integerRange(blockMetadata.offsetsAndCompressedSize_.size()), - std::back_inserter(allColumns)); - // A block is uniquely identified by its start position in the file. - auto cacheKey = blockMetadata.offsetsAndCompressedSize_.at(0).offsetInFile_; - auto sharedResultFromCache = - blockCache_ - .computeOnce( - cacheKey, - [&]() { - return readAndDecompressBlock(blockMetadata, allColumns); - }, - false, [](const auto&) { return true; }) - ._resultPointer; - const DecompressedBlock& block = *sharedResultFromCache; - - // Find the range in the blockMetadata, that belongs to the same relation - // `col0Id` + std::views::iota(ADDITIONAL_COLUMN_GRAPH_ID, + blockMetadata.offsetsAndCompressedSize_.size()), + std::back_inserter(allAdditionalColumns)); + ScanSpecification specForAllColumns{std::nullopt, + std::nullopt, + std::nullopt, + {}, + scanConfig.graphFilter_.desiredGraphs_}; + auto config = getScanConfig(specForAllColumns, + std::move(allAdditionalColumns), locatedTriples); + bool manuallyDeleteGraphColumn = scanConfig.graphFilter_.deleteGraphColumn_; + + // Helper lambda that returns the decompressed block or an empty block if + // `readAndDecompressBlock` returns `std::nullopt`. + const DecompressedBlock& block = [&]() { + auto result = readAndDecompressBlock(blockMetadata, config); + if (scanMetadata.has_value()) { + scanMetadata.value().get().update(result); + } + if (result.has_value()) { + return std::move(result.value().block_); + } else { + return DecompressedBlock{config.scanColumns_.size(), allocator_}; + } + }(); + // We now compute the range of the block according to the `scanSpec`. We + // start with the full range of the block. size_t beginIdx = 0; size_t endIdx = block.numRows(); + // Set `beginIdx` and `endIdx` s.t. that they only represent the range in // `block` where the column with the `columnIdx` matches the `relevantId`. + + // Helper lambda that narrows down the range of the block so that all values + // in column `columnIdx` are equal to `relevantId`. If `relevantId` is + // `std::nullopt`, the range is not narrowed down. auto filterColumn = [&block, &beginIdx, &endIdx](std::optional relevantId, size_t columnIdx) { if (!relevantId.has_value()) { @@ -565,29 +584,37 @@ DecompressedBlock CompressedRelationReader::readPossiblyIncompleteBlock( endIdx = matchingRange.end() - column.begin(); }; - // The order of the calls is important because the block is sorted by [col0Id, - // col1Id, col2Id]. + // Now narrow down the range of the block by first `scanSpec.col0Id()`, + // then `scanSpec.col1Id()`, and then `scanSpec.col2Id()`. This order is + // important because the rows are sorted in that order. filterColumn(scanSpec.col0Id(), 0); filterColumn(scanSpec.col1Id(), 1); filterColumn(scanSpec.col2Id(), 2); - DecompressedBlock result{columnIndices.size(), allocator_}; + // Now copy the range `[beginIdx, endIdx)` from `block` to `result`. + DecompressedBlock result{scanConfig.scanColumns_.size() - + static_cast(manuallyDeleteGraphColumn), + allocator_}; result.resize(endIdx - beginIdx); - for (auto i : ad_utility::integerRange(columnIndices.size())) { - const auto& inputCol = block.getColumn(columnIndices[i]); + size_t i = 0; + const auto& columnIndices = scanConfig.scanColumns_; + for (const auto& inputColIdx : + columnIndices | std::views::filter([&](const auto& idx) { + return !manuallyDeleteGraphColumn || idx != ADDITIONAL_COLUMN_GRAPH_ID; + })) { + const auto& inputCol = block.getColumn(inputColIdx); std::ranges::copy(inputCol.begin() + beginIdx, inputCol.begin() + endIdx, result.getColumn(i).begin()); + ++i; } - if (scanMetadata.has_value()) { - auto& details = scanMetadata.value().get(); - ++details.numBlocksRead_; - details.numElementsRead_ += result.numRows(); - } + + // Return the result. return result; }; // ____________________________________________________________________________ -size_t CompressedRelationReader::getResultSizeOfScan( +template +std::pair CompressedRelationReader::getResultSizeImpl( const ScanSpecification& scanSpec, const vector& blocks, [[maybe_unused]] const LocatedTriplesPerBlock& locatedTriplesPerBlock) @@ -596,15 +623,16 @@ size_t CompressedRelationReader::getResultSizeOfScan( // col1Id auto relevantBlocks = getRelevantBlocks(scanSpec, blocks); auto [beginBlock, endBlock] = getBeginAndEnd(relevantBlocks); - std::array columnIndices{0u}; + + auto config = getScanConfig(scanSpec, {}, locatedTriplesPerBlock); // The first and the last block might be incomplete (that is, only // 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. auto readSizeOfPossiblyIncompleteBlock = [&](const auto& block) { - return readPossiblyIncompleteBlock(scanSpec, block, std::nullopt, - columnIndices) + return readPossiblyIncompleteBlock(scanSpec, config, block, std::nullopt, + locatedTriplesPerBlock) .numRows(); }; @@ -620,13 +648,50 @@ size_t CompressedRelationReader::getResultSizeOfScan( --endBlock; } + if (beginBlock == endBlock) { + return {numResults, numResults}; + } + // Determine the total size of the result. // First accumulate the complete blocks in the "middle" - numResults += std::accumulate(beginBlock, endBlock, 0UL, - [](const auto& count, const auto& block) { - return count + block.numRows_; - }); - return numResults; + std::size_t inserted = 0; + std::size_t deleted = 0; + std::ranges::for_each( + std::ranges::subrange{beginBlock, endBlock}, [&](const auto& block) { + const auto [ins, del] = + locatedTriplesPerBlock.numTriples(block.blockIndex_); + if (!exactSize || (ins == 0 && del == 0)) { + inserted += ins; + 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. + auto b = readAndDecompressBlock(block, config); + numResults += b.has_value() ? b.value().block_.numRows() : 0u; + } + }); + return {numResults - std::min(deleted, numResults), numResults + inserted}; +} + +// ____________________________________________________________________________ +std::pair CompressedRelationReader::getSizeEstimateForScan( + const ScanSpecification& scanSpec, + const vector& blocks, + const LocatedTriplesPerBlock& locatedTriplesPerBlock) const { + return getResultSizeImpl(scanSpec, blocks, locatedTriplesPerBlock); +} + +// ____________________________________________________________________________ +size_t CompressedRelationReader::getResultSizeOfScan( + const ScanSpecification& scanSpec, + const vector& blocks, + [[maybe_unused]] const LocatedTriplesPerBlock& locatedTriplesPerBlock) + const { + auto [lower, upper] = + getResultSizeImpl(scanSpec, blocks, locatedTriplesPerBlock); + AD_CORRECTNESS_CHECK(lower == upper); + return lower; } // ____________________________________________________________________________ @@ -635,7 +700,8 @@ IdTable CompressedRelationReader::getDistinctColIdsAndCountsImpl( Id, const CompressedBlockMetadata::PermutedTriple&> auto idGetter, const ScanSpecification& scanSpec, const std::vector& allBlocksMetadata, - const CancellationHandle& cancellationHandle) const { + const CancellationHandle& cancellationHandle, + const LocatedTriplesPerBlock& locatedTriplesPerBlock) const { // The result has two columns: one for the distinct `Id`s and one for their // counts. IdTableStatic<2> table(allocator_); @@ -663,14 +729,13 @@ IdTable CompressedRelationReader::getDistinctColIdsAndCountsImpl( std::span relevantBlocksMetadata = getRelevantBlocks(scanSpec, allBlocksMetadata); - // Get the index of the column with the `colId`s that we are interested in. - auto relevantColumnIndex = prepareColumnIndices(scanSpec, {}); - AD_CORRECTNESS_CHECK(!relevantColumnIndex.empty()); - relevantColumnIndex.resize(1); - - // Iterate over the blocks and only read (and decompress) those which contain - // more than one different `colId`. For the others, we can determine the - // count from the metadata. + // TODO We have to read the other columns for the merging of the + // located triples. We could skip this for blocks with no updates, but that + // would require more arguments to the `decompressBlock` function. + auto scanConfig = getScanConfig(scanSpec, {}, locatedTriplesPerBlock); + // Iterate over the blocks and only read (and decompress) those which + // contain more than one different `colId`. For the others, we can determine + // the count from the metadata. for (size_t i = 0; i < relevantBlocksMetadata.size(); ++i) { const auto& blockMetadata = relevantBlocksMetadata[i]; Id firstColId = std::invoke(idGetter, blockMetadata.firstTriple_); @@ -681,12 +746,26 @@ IdTable CompressedRelationReader::getDistinctColIdsAndCountsImpl( processColId(firstColId, blockMetadata.numRows_); } else { // Multiple `colId`s -> we have to read the block. - const auto& block = - i == 0 - ? readPossiblyIncompleteBlock(scanSpec, blockMetadata, - std::nullopt, relevantColumnIndex) - : readAndDecompressBlock(blockMetadata, relevantColumnIndex); + const auto& optionalBlock = [&]() -> std::optional { + if (i == 0) { + return readPossiblyIncompleteBlock(scanSpec, scanConfig, + blockMetadata, std::nullopt, + locatedTriplesPerBlock); + } else { + auto optionalBlock = + readAndDecompressBlock(blockMetadata, scanConfig); + if (!optionalBlock.has_value()) { + return std::nullopt; + } + return std::move(optionalBlock.value().block_); + } + }(); cancellationHandle->throwIfCancelled(); + if (!optionalBlock.has_value()) { + // The block was skipped because of the graph filter + continue; + } + const auto& block = optionalBlock.value(); // TODO: use `std::views::chunk_by`. for (size_t j = 0; j < block.numRows(); ++j) { Id colId = block(j, 0); @@ -708,7 +787,7 @@ IdTable CompressedRelationReader::getDistinctCol0IdsAndCounts( ScanSpecification scanSpec{std::nullopt, std::nullopt, std::nullopt}; return getDistinctColIdsAndCountsImpl( &CompressedBlockMetadata::PermutedTriple::col0Id_, scanSpec, - allBlocksMetadata, cancellationHandle); + allBlocksMetadata, cancellationHandle, locatedTriplesPerBlock); } // ____________________________________________________________________________ @@ -721,7 +800,7 @@ IdTable CompressedRelationReader::getDistinctCol1IdsAndCounts( return getDistinctColIdsAndCountsImpl( &CompressedBlockMetadata::PermutedTriple::col1Id_, scanSpec, - allBlocksMetadata, cancellationHandle); + allBlocksMetadata, cancellationHandle, locatedTriplesPerBlock); } // ____________________________________________________________________________ @@ -785,6 +864,27 @@ DecompressedBlock CompressedRelationReader::decompressBlock( return decompressedBlock; } +// ____________________________________________________________________________ +DecompressedBlockAndMetadata +CompressedRelationReader::decompressAndPostprocessBlock( + const CompressedBlock& compressedBlock, size_t numRowsToRead, + const CompressedRelationReader::ScanImplConfig& scanConfig, + const CompressedBlockMetadata& metadata) const { + auto decompressedBlock = decompressBlock(compressedBlock, numRowsToRead); + auto [numIndexColumns, includeGraphColumn] = + prepareLocatedTriples(scanConfig.scanColumns_); + bool hasUpdates = false; + if (scanConfig.locatedTriples_.containsTriples(metadata.blockIndex_)) { + decompressedBlock = scanConfig.locatedTriples_.mergeTriples( + metadata.blockIndex_, decompressedBlock, numIndexColumns, + includeGraphColumn); + hasUpdates = true; + } + bool wasPostprocessed = + scanConfig.graphFilter_.postprocessBlock(decompressedBlock, metadata); + return {std::move(decompressedBlock), wasPostprocessed, hasUpdates}; +} + // ____________________________________________________________________________ template void CompressedRelationReader::decompressColumn( @@ -798,13 +898,18 @@ void CompressedRelationReader::decompressColumn( } // ____________________________________________________________________________ -DecompressedBlock CompressedRelationReader::readAndDecompressBlock( +std::optional +CompressedRelationReader::readAndDecompressBlock( const CompressedBlockMetadata& blockMetaData, - ColumnIndicesRef columnIndices) const { + const ScanImplConfig& scanConfig) const { + if (scanConfig.graphFilter_.canBlockBeSkipped(blockMetaData)) { + return std::nullopt; + } CompressedBlock compressedColumns = - readCompressedBlockFromFile(blockMetaData, columnIndices); + readCompressedBlockFromFile(blockMetaData, scanConfig.scanColumns_); const auto numRowsToRead = blockMetaData.numRows_; - return decompressBlock(compressedColumns, numRowsToRead); + return decompressAndPostprocessBlock(compressedColumns, numRowsToRead, + scanConfig, blockMetaData); } // ____________________________________________________________________________ @@ -946,11 +1051,17 @@ auto CompressedRelationReader::getFirstAndLastTriple( } const auto& scanSpec = metadataAndBlocks.scanSpec_; + ScanSpecification scanSpecForAllColumns{ + std::nullopt, std::nullopt, std::nullopt, {}, std::nullopt}; + auto config = + getScanConfig(scanSpecForAllColumns, + std::array{ColumnIndex{ADDITIONAL_COLUMN_GRAPH_ID}}, + locatedTriplesPerBlock); auto scanBlock = [&](const CompressedBlockMetadata& block) { // Note: the following call only returns the part of the block that // actually matches the col0 and col1. - return readPossiblyIncompleteBlock(scanSpec, block, std::nullopt, - {{0, 1, 2, ADDITIONAL_COLUMN_GRAPH_ID}}); + return readPossiblyIncompleteBlock(scanSpec, config, block, std::nullopt, + locatedTriplesPerBlock); }; auto rowToTriple = @@ -995,6 +1106,29 @@ std::vector CompressedRelationReader::prepareColumnIndices( } } +// ___________________________________________________________________________ +std::pair CompressedRelationReader::prepareLocatedTriples( + ColumnIndicesRef columns) { + AD_CORRECTNESS_CHECK(std::ranges::is_sorted(columns)); + // Compute number of columns that should be read (except the graph column + // and any payload columns). + size_t numScanColumns = [&]() -> size_t { + if (columns.empty() || columns[0] > 3) { + return 0; + } else { + return 3 - columns[0]; + } + }(); + // Check if one of the columns is the graph column. + auto it = std::ranges::find(columns, ADDITIONAL_COLUMN_GRAPH_ID); + bool containsGraphId = it != columns.end(); + if (containsGraphId) { + AD_CORRECTNESS_CHECK(it - columns.begin() == + static_cast(numScanColumns)); + } + return {numScanColumns, containsGraphId}; +} + // _____________________________________________________________________________ CompressedRelationMetadata CompressedRelationWriter::addSmallRelation( Id col0Id, size_t numDistinctC1, IdTableView<0> relation) { @@ -1002,9 +1136,9 @@ CompressedRelationMetadata CompressedRelationWriter::addSmallRelation( size_t numRows = relation.numRows(); // Make sure that the blocks don't become too large: If the previously // buffered small relations together with the new relations would exceed - // `1.5 - // * blocksize` then we start a new block for the current relation. Note: - // there are some unit tests that rely on this factor being `1.5`. + // `1.5 * blocksize` then we start a new block for the current relation. + // + // NOTE: there are some unit tests that rely on this factor being `1.5`. if (static_cast(numRows + smallRelationsBuffer_.numRows()) > static_cast(blocksize()) * 1.5) { writeBufferedRelationsToSingleBlock(); @@ -1222,8 +1356,8 @@ auto CompressedRelationWriter::createPermutationPair( // Set up the handling of small relations for the twin permutation. // A complete block of them is handed from `writer1` to the following lambda - // (via the `smallBlocksCallback_` mechanism. The lambda the resorts the block - // and feeds it to `writer2`.) + // (via the `smallBlocksCallback_` mechanism. The lambda the resorts the + // block and feeds it to `writer2`.) auto addBlockOfSmallRelationsToSwitched = [&writer2](std::shared_ptr relationPtr) { auto& relation = *relationPtr; @@ -1231,14 +1365,13 @@ 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, not the - // additional payload. - // TODO As soon as we implement named graphs, those should - // probably also be part of the ordering. - // 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]) < std::tie(b[0], b[1], b[2]); + return std::tie(a[0], a[1], a[2], a[3]) < + std::tie(b[0], b[1], b[2], b[3]); }; std::ranges::sort(relation, compare); AD_CORRECTNESS_CHECK(!relation.empty()); @@ -1375,13 +1508,14 @@ CompressedRelationReader::getMetadataForSmallRelation( metadata.offsetInBlock_ = 0; ScanSpecification scanSpec{col0Id, std::nullopt, std::nullopt}; auto blocks = getRelevantBlocks(scanSpec, allBlocksMetadata); + auto config = getScanConfig(scanSpec, {}, locatedTriplesPerBlock); AD_CONTRACT_CHECK(blocks.size() <= 1, "Should only be called for small relations"); if (blocks.empty()) { return std::nullopt; } - auto block = readPossiblyIncompleteBlock(scanSpec, blocks.front(), - std::nullopt, {{1, 2}}); + auto block = readPossiblyIncompleteBlock( + scanSpec, config, blocks.front(), std::nullopt, locatedTriplesPerBlock); if (block.empty()) { return std::nullopt; } @@ -1394,10 +1528,69 @@ CompressedRelationReader::getMetadataForSmallRelation( metadata.multiplicityCol1_ = CompressedRelationWriter::computeMultiplicity(block.size(), numDistinct); - // The `col2` is not sorted, so we compute the multiplicity using a hash set. + // The `col2` is not sorted, so we compute the multiplicity using a hash + // set. ad_utility::HashSet distinctCol2{block.getColumn(1).begin(), block.getColumn(1).end()}; metadata.multiplicityCol2_ = CompressedRelationWriter::computeMultiplicity( block.size(), distinctCol2.size()); return metadata; } + +// _____________________________________________________________________________ +auto CompressedRelationReader::getScanConfig( + const ScanSpecification& scanSpec, + CompressedRelationReader::ColumnIndicesRef additionalColumns, + const LocatedTriplesPerBlock& locatedTriples) -> ScanImplConfig { + auto columnIndices = prepareColumnIndices(scanSpec, additionalColumns); + // Determine the index of the graph column (which we need either for + // filtering or for the output or both) and whether we we need it for + // the output or not. + // + // NOTE: The graph column has to come directly after the triple columns and + // before any additional payload columns. Otherwise `prepareLocatedTriples` + // will throw an assertion. + auto [graphColumnIndex, + deleteGraphColumn] = [&]() -> std::pair { + if (!scanSpec.graphsToFilter().has_value()) { + // No filtering required, these are dummy values that are ignored by the + // filtering logic. + return {0, false}; + } + auto idx = static_cast( + std::ranges::find(columnIndices, ADDITIONAL_COLUMN_GRAPH_ID) - + columnIndices.begin()); + bool deleteColumn = false; + if (idx == columnIndices.size()) { + idx = columnIndices.size() - additionalColumns.size(); + columnIndices.insert(columnIndices.begin() + idx, + ADDITIONAL_COLUMN_GRAPH_ID); + deleteColumn = true; + } + return {idx, deleteColumn}; + }(); + FilterDuplicatesAndGraphs graphFilter{scanSpec.graphsToFilter(), + graphColumnIndex, deleteGraphColumn}; + return {std::move(columnIndices), std::move(graphFilter), locatedTriples}; +} + +// _____________________________________________________________________________ +void CompressedRelationReader::LazyScanMetadata::update( + const DecompressedBlockAndMetadata& blockAndMetadata) { + numBlocksPostprocessed_ += + static_cast(blockAndMetadata.wasPostprocessed_); + numBlocksWithUpdate_ += + static_cast(blockAndMetadata.containsUpdates_); + ++numBlocksRead_; + numElementsRead_ += blockAndMetadata.block_.numRows(); +} + +// _____________________________________________________________________________ +void CompressedRelationReader::LazyScanMetadata::update( + const std::optional& blockAndMetadata) { + if (blockAndMetadata.has_value()) { + update(blockAndMetadata.value()); + } else { + ++numBlocksSkippedBecauseOfGraph_; + } +} diff --git a/src/index/CompressedRelation.h b/src/index/CompressedRelation.h index 763a86fd34..9f7095993e 100644 --- a/src/index/CompressedRelation.h +++ b/src/index/CompressedRelation.h @@ -1,6 +1,6 @@ -// Copyright 2021, University of Freiburg, +// Copyright 2021 - 2024, University of Freiburg // Chair of Algorithms and Data Structures -// Author: Johannes Kalmbach +// Author: Johannes Kalmbach #pragma once @@ -12,9 +12,7 @@ #include "global/Id.h" #include "index/ScanSpecification.h" #include "parser/data/LimitOffsetClause.h" -#include "util/Cache.h" #include "util/CancellationHandle.h" -#include "util/ConcurrentCache.h" #include "util/File.h" #include "util/Generator.h" #include "util/MemorySize/MemorySize.h" @@ -37,13 +35,15 @@ using SmallRelationsBuffer = IdTable; // to use a dynamic `IdTable`. using DecompressedBlock = IdTable; -// To be able to use `DecompressedBlock` with `Caches`, we need a function for -// calculating the memory used for it. -struct DecompressedBlockSizeGetter { - ad_utility::MemorySize operator()(const DecompressedBlock& block) const { - return ad_utility::MemorySize::bytes(block.numColumns() * block.numRows() * - sizeof(Id)); - } +// A decompressed block together with some metadata about the process of +// decompressing + postprocessing it. +struct DecompressedBlockAndMetadata { + DecompressedBlock block_; + // True iff the block had to be modified because of the contained graphs. + bool wasPostprocessed_; + // True iff triples this block had to be merged with the `LocatedTriples` + // because it contained updates. + bool containsUpdates_; }; // After compression the columns have different sizes, so we cannot use an @@ -162,6 +162,7 @@ AD_SERIALIZE_FUNCTION(CompressedBlockMetadata) { // permutation). struct CompressedRelationMetadata { Id col0Id_; + // TODO: Is this still needed? Same for `offsetInBlock_`. size_t numRows_; float multiplicityCol1_; // E.g., in PSO this is the multiplicity of "S". float multiplicityCol2_; // E.g., in PSO this is the multiplicity of "O". @@ -412,7 +413,7 @@ class CompressedRelationReader { // or whether it should be deleted after filtering. It can then filter a given // block according to those settings. struct FilterDuplicatesAndGraphs { - const ScanSpecification::Graphs& desiredGraphs_; + ScanSpecification::Graphs desiredGraphs_; ColumnIndex graphColumn_; bool deleteGraphColumn_; // Filter `block` such that it contains only the specified graphs and no @@ -443,6 +444,14 @@ class CompressedRelationReader { IdTable& block, const CompressedBlockMetadata& blockMetadata); }; + // Classes holding various subsets of parameters relevant for a scan of a + // permutation, including a reference to the relevant located triples. + struct ScanImplConfig { + ColumnIndices scanColumns_; + FilterDuplicatesAndGraphs graphFilter_; + const LocatedTriplesPerBlock& locatedTriples_; + }; + // The specification of scan, together with the blocks on which this scan is // to be performed. struct ScanSpecAndBlocks { @@ -480,26 +489,27 @@ class CompressedRelationReader { // (because the graph IDs of the block did not match the query). size_t numBlocksSkippedBecauseOfGraph_ = 0; size_t numBlocksPostprocessed_ = 0; + // The number of blocks that contain updated (inserted or deleted) triples. + size_t numBlocksWithUpdate_ = 0; // If a LIMIT or OFFSET is present we possibly read more rows than we // actually yield. size_t numElementsRead_ = 0; size_t numElementsYielded_ = 0; std::chrono::milliseconds blockingTime_ = std::chrono::milliseconds::zero(); + + // Update this metadata, given the metadata from `blockAndMetadata`. + // Currently updates: `numBlocksPostprocessed_`, `numBlocksWithUpdate_`, + // `numElementsRead_`, and `numBlocksRead_`. + void update(const DecompressedBlockAndMetadata& blockAndMetadata); + // `nullopt` means the block was skipped because of the graph filters, else + // call the overload directly above. + void update( + const std::optional& blockAndMetadata); }; using IdTableGenerator = cppcoro::generator; private: - // This cache stores a small number of decompressed blocks. Its current - // purpose is to make the e2e-tests run fast. They contain many SPARQL queries - // with ?s ?p ?o triples in the body. - // Note: The cache is thread-safe and using it does not change the semantics - // of this class, so it is safe to mark it as `mutable` to make the `scan` - // functions below `const`. - mutable ad_utility::ConcurrentCache> - blockCache_{20ul}; - // The allocator used to allocate intermediate buffers. mutable Allocator allocator_; @@ -567,16 +577,33 @@ class CompressedRelationReader { const LocatedTriplesPerBlock& locatedTriplesPerBlock, LimitOffsetClause limitOffset = {}) const; - // Only get the size of the result for a given permutation XYZ for a given X - // and Y. This can be done by scanning one or two blocks. Note: The overload - // of this function where only the X is given is not needed, as the size of - // these scans can be retrieved from the `CompressedRelationMetadata` - // directly. + // Get the exact size of the result of the scan, taking the given located + // triples into account. This requires locating the triples exactly in each + // of the relevant blocks. size_t getResultSizeOfScan( const ScanSpecification& scanSpec, const vector& blocks, const LocatedTriplesPerBlock& locatedTriplesPerBlock) const; + // Get a lower and an upper bound for the size of the result of the scan. For + // this call, it is enough that each located triple knows the block to which + // it belongs (which is the case for `LocatedTriplesPerBlock`). + std::pair getSizeEstimateForScan( + const ScanSpecification& scanSpec, + const vector& blocks, + const LocatedTriplesPerBlock& locatedTriplesPerBlock) const; + + private: + // Common implementation of `getResultSizeOfScan` and `getSizeEstimateForScan` + // above. + template + std::pair getResultSizeImpl( + const ScanSpecification& scanSpec, + const vector& blocks, + [[maybe_unused]] const LocatedTriplesPerBlock& locatedTriplesPerBlock) + const; + + public: // For a given relation, determine the `col1Id`s and their counts. This is // used for `computeGroupByObjectWithCount`. IdTable getDistinctCol1IdsAndCounts( @@ -643,38 +670,43 @@ class CompressedRelationReader { static void decompressColumn(const std::vector& compressedColumn, size_t numRowsToRead, Iterator iterator); - // Read the block that is identified by the `blockMetaData` from the `file`, - // decompress and return it. Only the columns specified by the `columnIndices` - // are returned. - DecompressedBlock readAndDecompressBlock( + // Read and decompress the parts of the block given by `blockMetaData` (which + // identifies the block) and `scanConfig` (which specifies the part of that + // block). + std::optional readAndDecompressBlock( const CompressedBlockMetadata& blockMetaData, - ColumnIndicesRef columnIndices) const; - - // Read the block identified by `blockMetadata` from disk, decompress it, and - // return the part that matches `col1Id` (or the whole block if `col1Id` is - // `nullopt`). The block must contain triples from the relation identified by - // `relationMetadata`. If `scanMetadata` is not `nullopt`, update information - // about the number of blocks and elements read. + const ScanImplConfig& scanConfig) const; + + // Like `readAndDecompressBlock`, and postprocess by merging the located + // triples (if any) and applying the graph filters (if any), both specified + // as part of the `scanConfig`. + DecompressedBlockAndMetadata decompressAndPostprocessBlock( + const CompressedBlock& compressedBlock, size_t numRowsToRead, + const CompressedRelationReader::ScanImplConfig& scanConfig, + const CompressedBlockMetadata& metadata) const; + + // Read, decompress, and postprocess the part of the block according to + // `blockMetadata` (which identifies the block) and `scanConfig` (which + // specifies the part of that block, graph filters, and located triples). // // NOTE: When all triples in the block match the `col1Id`, this method makes // an unnecessary copy of the block. Therefore, if you know that you need the // whole block, use `readAndDecompressBlock` instead. DecompressedBlock readPossiblyIncompleteBlock( - const ScanSpecification& scanSpec, + const ScanSpecification& scanSpec, const ScanImplConfig& scanConfig, const CompressedBlockMetadata& blockMetadata, std::optional> scanMetadata, - ColumnIndicesRef columnIndices) const; + const LocatedTriplesPerBlock&) const; // Yield all the blocks in the range `[beginBlock, endBlock)`. If the // `columnIndices` are set, only the specified columns from the blocks // are yielded, else all columns are yielded. The blocks are yielded // in the correct order, but asynchronously read and decompressed using // multiple worker threads. - IdTableGenerator asyncParallelBlockGenerator( - auto beginBlock, auto endBlock, ColumnIndices columnIndices, - CancellationHandle cancellationHandle, LimitOffsetClause& limitOffset, - FilterDuplicatesAndGraphs blockGraphFilter) const; + auto beginBlock, auto endBlock, const ScanImplConfig& scanConfig, + CancellationHandle cancellationHandle, + LimitOffsetClause& limitOffset) const; // Return a vector that consists of the concatenation of `baseColumns` and // `additionalColumns` @@ -682,6 +714,12 @@ class CompressedRelationReader { std::initializer_list baseColumns, ColumnIndicesRef additionalColumns); + // Return the number of columns that should be read (except for the graph + // column and any payload columns, this is one of 0, 1, 2, 3) and whether the + // graph column is contained in `columns`. + static std::pair prepareLocatedTriples( + ColumnIndicesRef columns); + // If `col1Id` is specified, `return {1, additionalColumns...}`, else return // `{0, 1, additionalColumns}`. // These are exactly the columns that are returned by a scan depending on @@ -689,6 +727,10 @@ class CompressedRelationReader { static std::vector prepareColumnIndices( const ScanSpecification& scanSpec, ColumnIndicesRef additionalColumns); + static ScanImplConfig getScanConfig( + const ScanSpecification& scanSpec, ColumnIndicesRef additionalColumns, + const LocatedTriplesPerBlock& locatedTriples); + // The common implementation for `getDistinctCol0IdsAndCounts` and // `getCol1IdsAndCounts`. IdTable getDistinctColIdsAndCountsImpl( @@ -696,13 +738,13 @@ class CompressedRelationReader { Id, const CompressedBlockMetadata::PermutedTriple&> auto idGetter, const ScanSpecification& scanSpec, const std::vector& allBlocksMetadata, - const CancellationHandle& cancellationHandle) const; + const CancellationHandle& cancellationHandle, + const LocatedTriplesPerBlock& locatedTriplesPerBlock) const; }; // TODO /* - * 1. Also let the compressedRelationReader know about the contained block data - * and the number of columns etc. to make the permutation class a thinner - * wrapper. + * 1. Also let the compressedRelationReader know about the number of columns + * that the given permutation has. * 2. Then add assertions that we only get valid column indices specified. */ diff --git a/src/index/LocatedTriples.cpp b/src/index/LocatedTriples.cpp index e898327a9a..b2244f8960 100644 --- a/src/index/LocatedTriples.cpp +++ b/src/index/LocatedTriples.cpp @@ -268,6 +268,27 @@ void LocatedTriplesPerBlock::updateAugmentedMetadata() { } blockIndex++; } + // Also account for the last block that contains the triples that are larger + // than all the inserted triples. + if (hasUpdates(blockIndex)) { + const auto& blockUpdates = map_.at(blockIndex); + auto firstTriple = blockUpdates.begin()->triple_.toPermutedTriple(); + auto lastTriple = blockUpdates.rbegin()->triple_.toPermutedTriple(); + + using O = CompressedBlockMetadata::OffsetAndCompressedSize; + O emptyBlock{0, 0}; + + // TODO We need the appropriate number of columns here, or we need + // to make the reading code work regardless of the number of columns. + CompressedBlockMetadataNoBlockIndex lastBlockN{ + std::vector(4, emptyBlock), + 0, + firstTriple, + lastTriple, + std::nullopt, + true}; + augmentedMetadata_->emplace_back(lastBlockN, blockIndex); + } } // ____________________________________________________________________________ @@ -285,11 +306,11 @@ std::ostream& operator<<(std::ostream& os, const std::vector>& v) { } // ____________________________________________________________________________ -bool LocatedTriplesPerBlock::containsTriple(const IdTriple<0>& triple, - bool shouldExist) const { - auto blockContains = [&triple, shouldExist](const LocatedTriples& lt, +bool LocatedTriplesPerBlock::isLocatedTriple(const IdTriple<0>& triple, + bool isInsertion) const { + auto blockContains = [&triple, isInsertion](const LocatedTriples& lt, size_t blockIndex) { - LocatedTriple locatedTriple{blockIndex, triple, shouldExist}; + LocatedTriple locatedTriple{blockIndex, triple, isInsertion}; locatedTriple.blockIndex_ = blockIndex; return ad_utility::contains(lt, locatedTriple); }; diff --git a/src/index/LocatedTriples.h b/src/index/LocatedTriples.h index c1b612a775..a424a9de63 100644 --- a/src/index/LocatedTriples.h +++ b/src/index/LocatedTriples.h @@ -90,6 +90,8 @@ class LocatedTriplesPerBlock { // the updated triples. std::optional> augmentedMetadata_; std::vector originalMetadata_; + + public: void updateAugmentedMetadata(); public: @@ -127,14 +129,18 @@ class LocatedTriplesPerBlock { IdTable mergeTriples(size_t blockIndex, const IdTable& block, size_t numIndexColumns, bool includeGraphColumn) const; - // Add `getLocatedTriplesForPermutation` to the `LocatedTriplesPerBlock`. - // Return handles to where they were added (`LocatedTriples` is a sorted set, - // see above). We need the handles so that we can easily remove the - // `getLocatedTriplesForPermutation` from the set again in case we need to. - // - // PRECONDITIONS: + // Return true iff there are located triples in the block with the given + // index. + bool containsTriples(size_t blockIndex) const { + return map_.contains(blockIndex); + } + + // Add `locatedTriples` to the `LocatedTriplesPerBlock` and return handles to + // where they were added (`LocatedTriples` is a sorted set, see above). Using + // these handles, we can easily remove the `locatedTriples` from the set again + // when we need to. // - // 1. The `getLocatedTriplesForPermutation` must not already exist in + // PRECONDITION: The `locatedTriples` must not already exist in // `LocatedTriplesPerBlock`. std::vector add( std::span locatedTriples); @@ -167,9 +173,11 @@ class LocatedTriplesPerBlock { augmentedMetadata_ = originalMetadata_; } - // Only used for testing. Return `true` iff a `LocatedTriple` with the given - // value for `shouldExist` is contained in any block. - bool containsTriple(const IdTriple<0>& triple, bool shouldExist) const; + // Return `true` iff the given triple is one of the located triples with the + // given status (inserted or deleted). + // + // NOTE: Only used for testing. + bool isLocatedTriple(const IdTriple<0>& triple, bool isInsertion) const; // This operator is only for debugging and testing. It returns a // human-readable representation. diff --git a/src/index/Permutation.cpp b/src/index/Permutation.cpp index c331c3a296..01994cf7cc 100644 --- a/src/index/Permutation.cpp +++ b/src/index/Permutation.cpp @@ -81,6 +81,16 @@ size_t Permutation::getResultSizeOfScan( getLocatedTriplesForPermutation(locatedTriplesSnapshot)); } +// _____________________________________________________________________ +std::pair Permutation::getSizeEstimateForScan( + const ScanSpecification& scanSpec, + const LocatedTriplesSnapshot& locatedTriplesSnapshot) const { + const auto& p = getActualPermutation(scanSpec); + return p.reader().getSizeEstimateForScan( + scanSpec, p.meta_.blockData(), + getLocatedTriplesForPermutation(locatedTriplesSnapshot)); +} + // ____________________________________________________________________________ IdTable Permutation::getDistinctCol1IdsAndCounts( Id col0Id, const CancellationHandle& cancellationHandle, diff --git a/src/index/Permutation.h b/src/index/Permutation.h index 894516e438..5b49ae0b80 100644 --- a/src/index/Permutation.h +++ b/src/index/Permutation.h @@ -119,12 +119,20 @@ class Permutation { const ScanSpecification& scanSpec, const LocatedTriplesSnapshot& locatedTriplesSnapshot) const; - /// Similar to the previous `scan` function, but only get the size of the - /// result + // Get the exact size of the result of a scan, taking into account the + // given located triples. This requires an exact location of the delta triples + // within the respective blocks. size_t getResultSizeOfScan( const ScanSpecification& scanSpec, const LocatedTriplesSnapshot& locatedTriplesSnapshot) const; + // Get a lower and upper bound for the size of the result of a scan, taking + // into account the given `deltaTriples`. For this call, it is enough that + // each delta triple know to which block it belongs. + std::pair getSizeEstimateForScan( + const ScanSpecification& scanSpec, + const LocatedTriplesSnapshot& locatedTriplesSnapshot) const; + // _______________________________________________________ void setKbName(const string& name) { meta_.setName(name); } diff --git a/test/CompressedRelationsTest.cpp b/test/CompressedRelationsTest.cpp index 10bae9be07..6db70c83b4 100644 --- a/test/CompressedRelationsTest.cpp +++ b/test/CompressedRelationsTest.cpp @@ -66,17 +66,30 @@ size_t getNumColumns(const std::vector& vec) { void checkThatTablesAreEqual(const auto& expected, const IdTable& actual, source_location l = source_location::current()) { auto trace = generateLocationTrace(l); - if (!expected.empty()) { - ASSERT_EQ(getNumColumns(expected), actual.numColumns()); - } - if (actual.numRows() != expected.size()) { - LOG(WARN) << actual.numRows() << "vs " << expected.size() << std::endl; - LOG(WARN) << "mismatch" << std::endl; + + VectorTable exp; + for (const auto& row : expected) { + // exp.emplace_back(row.begin(), row.end()); + exp.emplace_back(); + for (auto& el : row) { + exp.back().push_back(el); + } } - ASSERT_EQ(actual.numRows(), expected.size()); - for (size_t i = 0; i < actual.numRows(); ++i) { - for (size_t j = 0; j < actual.numColumns(); ++j) { - ASSERT_EQ(V(expected[i][j]), actual(i, j)); + EXPECT_THAT(actual, matchesIdTableFromVector(exp)); +} + +// If the `inputs` have no graph column (because the corresponding tests don't +// care about named graphs), add a constant dummy graph column, such that the +// assertions inside `CompressedRelation.cpp` (which always expect a graph +// column) work. +auto addGraphColumnIfNecessary(std::vector& inputs) { + size_t numColumns = getNumColumns(inputs) + 1; + if (numColumns == 3) { + ++numColumns; + for (auto& input : inputs) { + for (auto& row : input.col1And2_) { + row.push_back(103496581); + } } } } @@ -104,17 +117,8 @@ compressedRelationTestWriteCompressedRelations( })); // First create the on-disk permutation. + addGraphColumnIfNecessary(inputs); size_t numColumns = getNumColumns(inputs) + 1; - // If the input has no graph info, add a dummy graph value to all inputs, - // such that the assertions work. - if (numColumns == 3) { - ++numColumns; - for (auto& input : inputs) { - for (auto& row : input.col1And2_) { - row.push_back(103496581); - } - } - } AD_CORRECTNESS_CHECK(numColumns >= 4); CompressedRelationWriter writer{numColumns, ad_utility::File{filename, "w"}, blocksize}; @@ -187,6 +191,46 @@ auto makeCleanup(std::string filename) { [filename = std::move(filename)] { ad_utility::deleteFile(filename); }); } +// From the `inputs` delete each triple with probability `locatedProbab` and +// add it to a vector of `IdTriple`s which can then be used to build a +// `LocatedTriples` object. Return the remaining triples and the (not-yet) +// located triples. +std::tuple, std::vector>> +makeLocatedTriplesFromPartOfInput(float locatedProbab, + const std::vector& inputs) { + std::vector> locatedTriples; + std::vector result; + ad_utility::RandomDoubleGenerator randomGenerator(0.0, 1.0); + auto gen = [&randomGenerator, &locatedProbab]() { + auto r = randomGenerator(); + return locatedProbab == 1.0f || r < locatedProbab; + }; + + auto addLocated = [&locatedTriples](Id col0, const auto& otherCols) { + locatedTriples.push_back(IdTriple<>{ + {col0, V(otherCols.at(0)), V(otherCols.at(1)), V(otherCols.at(2))}}); + }; + + for (const auto& input : inputs) { + auto col0 = V(input.col0_); + result.emplace_back(input.col0_); + auto& row = result.back().col1And2_; + for (const auto& otherCols : input.col1And2_) { + AD_CORRECTNESS_CHECK(otherCols.size() >= 3); + auto isLocated = gen(); + if (isLocated) { + addLocated(col0, otherCols); + } else { + row.push_back(otherCols); + } + } + if (row.empty()) { + result.pop_back(); + } + } + return {std::move(result), std::move(locatedTriples)}; +} + // Write the relations specified by the `inputs` to a compressed permutation at // `filename`. Return the created metadata for blocks and large relations, as // well as a `CompressedRelationReader`. These are exactly the datastructures @@ -209,12 +253,30 @@ auto writeAndOpenRelations(const std::vector& inputs, // a unique name for the required temporary files and for the implicit cache // of the `CompressedRelationMetaData`. `blocksize` is the size of the blocks // in which the permutation will be compressed and stored on disk. -void testCompressedRelations(const auto& inputs, std::string testCaseName, - ad_utility::MemorySize blocksize) { +void testCompressedRelations(const auto& inputsOriginalBeforeCopy, + std::string testCaseName, + ad_utility::MemorySize blocksize, + float locatedTriplesProbability = 0.5) { + auto inputs = inputsOriginalBeforeCopy; + addGraphColumnIfNecessary(inputs); + auto [inputsWithoutLocated, locatedTriplesInput] = + makeLocatedTriplesFromPartOfInput(locatedTriplesProbability, inputs); + DeltaTriples deltaTriples{ad_utility::testing::getQec()->getIndex()}; auto filename = testCaseName + ".dat"; auto cleanup = makeCleanup(filename); - auto [blocks, metaData, readerPtr] = - writeAndOpenRelations(inputs, filename, blocksize); + auto [blocksOriginal, metaData, readerPtr] = + writeAndOpenRelations(inputsWithoutLocated, filename, blocksize); + auto handle = std::make_shared>(); + // deltaTriples.insertTriples(handle, std::move(locatedTriplesInput)); + // auto locatedTriples = + // deltaTriples.getLocatedTriplesPerBlock(Permutation::SPO); + auto locatedTriples = LocatedTriplesPerBlock{}; + auto loc = LocatedTriple::locateTriplesInPermutation( + locatedTriplesInput, blocksOriginal, {0, 1, 2}, true, handle); + locatedTriples.add(loc); + locatedTriples.setOriginalMetadata(blocksOriginal); + locatedTriples.updateAugmentedMetadata(); + auto blocks = locatedTriples.getAugmentedMetadata(); auto& reader = *readerPtr; auto cancellationHandle = @@ -225,18 +287,33 @@ void testCompressedRelations(const auto& inputs, std::string testCaseName, std::vector additionalColumns; std::ranges::copy(std::views::iota(3ul, getNumColumns(inputs) + 1), std::back_inserter(additionalColumns)); - for (size_t i = 0; i < metaData.size(); ++i) { - const auto& m = metaData[i]; - ASSERT_EQ(V(inputs[i].col0_), m.col0Id_); - ASSERT_EQ(inputs[i].col1And2_.size(), m.numRows_); - // The number of distinct elements in `col1` was passed in as `i + 1` for - // testing purposes, so this is the expected multiplicity. - ASSERT_FLOAT_EQ(m.numRows_ / static_cast(i + 1), - m.multiplicityCol1_); + auto getMetadata = [&, &metaData = metaData](size_t i) { + Id col0 = V(inputs[i].col0_); + auto it = std::ranges::lower_bound(metaData, col0, {}, + &CompressedRelationMetadata::col0Id_); + if (it != metaData.end() && it->col0Id_ == col0) { + return *it; + } + return reader.getMetadataForSmallRelation(blocks, col0, locatedTriples) + .value(); + }; + for (size_t i = 0; i < inputs.size(); ++i) { + // The metadata does not include the located triples, so we can only test it + // if there are no located triples. + if (locatedTriplesProbability == 0) { + const auto& m = getMetadata(i); + ASSERT_EQ(V(inputs[i].col0_), m.col0Id_); + ASSERT_EQ(inputs[i].col1And2_.size(), m.numRows_); + // The number of distinct elements in `col1` was passed in as `i + 1` for + // testing purposes, so this is the expected multiplicity. + ASSERT_FLOAT_EQ(m.numRows_ / static_cast(i + 1), + m.multiplicityCol1_); + } + // Scan for all distinct `col0` and check that we get the expected result. - ScanSpecification scanSpec{metaData[i].col0Id_, std::nullopt, std::nullopt}; + ScanSpecification scanSpec{V(inputs[i].col0_), std::nullopt, std::nullopt}; IdTable table = reader.scan(scanSpec, blocks, additionalColumns, - cancellationHandle, emptyLocatedTriples); + cancellationHandle, locatedTriples); const auto& col1And2 = inputs[i].col1And2_; checkThatTablesAreEqual(col1And2, table); table.clear(); @@ -246,7 +323,7 @@ void testCompressedRelations(const auto& inputs, std::string testCaseName, for (const auto& limitOffset : limitOffsetClauses) { IdTable table = reader.scan(scanSpec, blocks, additionalColumns, cancellationHandle, - emptyLocatedTriples, limitOffset); + locatedTriples, limitOffset); auto col1And2 = inputs[i].col1And2_; col1And2.resize(limitOffset.upperBound(col1And2.size())); col1And2.erase( @@ -256,7 +333,7 @@ void testCompressedRelations(const auto& inputs, std::string testCaseName, } for (const auto& block : reader.lazyScan(scanSpec, blocks, additionalColumns, - cancellationHandle, emptyLocatedTriples)) { + cancellationHandle, locatedTriples)) { table.insertAtEnd(block.begin(), block.end()); } checkThatTablesAreEqual(col1And2, table); @@ -268,20 +345,19 @@ void testCompressedRelations(const auto& inputs, std::string testCaseName, std::vector> col3; auto scanAndCheck = [&]() { - ScanSpecification scanSpec{metaData[i].col0Id_, V(lastCol1Id), + ScanSpecification scanSpec{V(inputs[i].col0_), V(lastCol1Id), std::nullopt}; - auto size = - reader.getResultSizeOfScan(scanSpec, blocks, emptyLocatedTriples); + auto size = reader.getResultSizeOfScan(scanSpec, blocks, locatedTriples); IdTable tableWidthOne = reader.scan(scanSpec, blocks, Permutation::ColumnIndicesRef{}, - cancellationHandle, emptyLocatedTriples); + cancellationHandle, locatedTriples); ASSERT_EQ(tableWidthOne.numColumns(), 1); EXPECT_EQ(size, tableWidthOne.numRows()); checkThatTablesAreEqual(col3, tableWidthOne); tableWidthOne.clear(); for (const auto& block : reader.lazyScan(scanSpec, blocks, Permutation::ColumnIndices{}, - cancellationHandle, emptyLocatedTriples)) { + cancellationHandle, locatedTriples)) { tableWidthOne.insertAtEnd(block.begin(), block.end()); } checkThatTablesAreEqual(col3, tableWidthOne); @@ -306,10 +382,14 @@ void testCompressedRelations(const auto& inputs, std::string testCaseName, // size, powers of two and odd), to find subtle rounding bugs when creating the // blocks. void testWithDifferentBlockSizes(const std::vector& inputs, - std::string testCaseName) { - testCompressedRelations(inputs, testCaseName, 19_B); - testCompressedRelations(inputs, testCaseName, 237_B); - testCompressedRelations(inputs, testCaseName, 4096_B); + std::string testCaseName, + float locatedTriplesProbability = 0.5) { + testCompressedRelations(inputs, testCaseName, 19_B, + locatedTriplesProbability); + testCompressedRelations(inputs, testCaseName, 237_B, + locatedTriplesProbability); + testCompressedRelations(inputs, testCaseName, 4096_B, + locatedTriplesProbability); } } // namespace @@ -478,7 +558,9 @@ TEST(CompressedRelationWriter, AdditionalColumns) { row.push_back(row.at(1) * 42); } } - testWithDifferentBlockSizes(inputs, "mixedSizes"); + // The additional columns don't yet work properly with located triples / + // SPARQL UPDATE, so we have to disable the + testWithDifferentBlockSizes(inputs, "mixedSizes", 0.0); } TEST(CompressedRelationWriter, MultiplicityCornerCases) { @@ -688,8 +770,7 @@ TEST(CompressedRelationReader, filterDuplicatesAndGraphs) { graphs.emplace(); graphs->insert(ValueId::makeFromVocabIndex(VocabIndex::make(1))); graphs->insert(ValueId::makeFromVocabIndex(VocabIndex::make(2))); - f.graphColumn_ = 1; - f.deleteGraphColumn_ = false; + f = Filter{graphs, 1, false}; EXPECT_TRUE(f.postprocessBlock(table, metadata)); EXPECT_THAT(table, matchesIdTableFromVector({{3, 1}, {3, 2}})); @@ -709,10 +790,9 @@ TEST(CompressedRelationReader, makeCanBeSkippedForBlock) { CompressedBlockMetadata metadata{ {{}, 0, {V(16), V(0), V(0), g}, {V(38), V(4), V(12), g}, {}, false}, 0}; - using Graphs = ScanSpecification::Graphs; - Graphs graphs = std::nullopt; - auto filter = - CompressedRelationReader::FilterDuplicatesAndGraphs{graphs, 0, false}; + auto filter = CompressedRelationReader::FilterDuplicatesAndGraphs{ + std::nullopt, 0, false}; + auto& graphs = filter.desiredGraphs_; // No information about the contained blocks, and no graph filter specified, // so we cannot skip. EXPECT_FALSE(filter.canBlockBeSkipped(metadata)); diff --git a/test/DeltaTriplesTest.cpp b/test/DeltaTriplesTest.cpp index cb481b43b8..10a4792d29 100644 --- a/test/DeltaTriplesTest.cpp +++ b/test/DeltaTriplesTest.cpp @@ -371,12 +371,15 @@ TEST_F(DeltaTriplesTest, DeltaTriplesManager) { // or deleted (`false`). const auto& locatedSPO = beforeUpdate->getLocatedTriplesForPermutation(Permutation::SPO); - EXPECT_FALSE(locatedSPO.containsTriple(triplesToInsert.at(1), true)); - EXPECT_FALSE(locatedSPO.containsTriple(triplesToInsert.at(1), false)); - EXPECT_FALSE(locatedSPO.containsTriple(triplesToInsert.at(2), true)); - EXPECT_FALSE(locatedSPO.containsTriple(triplesToInsert.at(2), false)); - EXPECT_FALSE(locatedSPO.containsTriple(triplesToDelete.at(2), true)); - EXPECT_FALSE(locatedSPO.containsTriple(triplesToDelete.at(2), false)); + EXPECT_FALSE(locatedSPO.isLocatedTriple(triplesToInsert.at(1), true)); + EXPECT_FALSE( + locatedSPO.isLocatedTriple(triplesToInsert.at(1), false)); + EXPECT_FALSE(locatedSPO.isLocatedTriple(triplesToInsert.at(2), true)); + EXPECT_FALSE( + locatedSPO.isLocatedTriple(triplesToInsert.at(2), false)); + EXPECT_FALSE(locatedSPO.isLocatedTriple(triplesToDelete.at(2), true)); + EXPECT_FALSE( + locatedSPO.isLocatedTriple(triplesToDelete.at(2), false)); } { // Check for several of the thread-exclusive triples that they are @@ -385,13 +388,13 @@ TEST_F(DeltaTriplesTest, DeltaTriplesManager) { auto p = deltaTriplesManager.getCurrentSnapshot(); const auto& locatedSPO = p->getLocatedTriplesForPermutation(Permutation::SPO); - EXPECT_TRUE(locatedSPO.containsTriple(triplesToInsert.at(1), true)); + EXPECT_TRUE(locatedSPO.isLocatedTriple(triplesToInsert.at(1), true)); // This triple is exclusive to the thread and is inserted and then - // immediately deleted again. The `DeltaTriples` thus only store it as - // deleted. It might be contained in the original input, hence we + // immediately deleted again. The `DeltaTriples` thus only store it + // as deleted. It might be contained in the original input, hence we // cannot simply drop it. - EXPECT_TRUE(locatedSPO.containsTriple(triplesToInsert.at(2), false)); - EXPECT_TRUE(locatedSPO.containsTriple(triplesToDelete.at(2), false)); + EXPECT_TRUE(locatedSPO.isLocatedTriple(triplesToInsert.at(2), false)); + EXPECT_TRUE(locatedSPO.isLocatedTriple(triplesToDelete.at(2), false)); } } } diff --git a/test/engine/IndexScanTest.cpp b/test/engine/IndexScanTest.cpp index 58091931cd..7a0383390b 100644 --- a/test/engine/IndexScanTest.cpp +++ b/test/engine/IndexScanTest.cpp @@ -47,7 +47,11 @@ void testLazyScan(Permutation::IdTableGenerator partialLazyScanResult, if (limitOffset.isUnconstrained()) { EXPECT_EQ(numBlocks, partialLazyScanResult.details().numBlocksRead_); - EXPECT_EQ(lazyScanRes.size(), + // The number of read elements might be a bit larger than the final result + // size, because the first and/or last block might be incomplete, meaning + // that they have to be completely read, but only partially contribute to + // the result. + EXPECT_LE(lazyScanRes.size(), partialLazyScanResult.details().numElementsRead_); } @@ -171,7 +175,6 @@ void testLazyScanWithColumnThrows( TEST(IndexScan, lazyScanForJoinOfTwoScans) { SparqlTriple xpy{Tc{Var{"?x"}}, "

", Tc{Var{"?y"}}}; SparqlTriple xqz{Tc{Var{"?x"}}, "", Tc{Var{"?z"}}}; - /* { // In the tests we have a blocksize of two triples per block, and a new // block is started for a new relation. That explains the spacing of the @@ -196,7 +199,6 @@ TEST(IndexScan, lazyScanForJoinOfTwoScans) { // graph), so both lazy scans are empty. testLazyScanForJoinOfTwoScans(kg, xpy, xqz, {}, {}); } - */ { // No triple for relation (which does appear in the knowledge graph, but // not as a predicate), so both lazy scans are empty. diff --git a/test/util/GTestHelpers.h b/test/util/GTestHelpers.h index 161c6c3064..62a097c18d 100644 --- a/test/util/GTestHelpers.h +++ b/test/util/GTestHelpers.h @@ -1,7 +1,7 @@ -// Copyright 2022, University of Freiburg, -// Chair of Algorithms and Data Structures. -// Authors: Julian Mundhahs (mundhahj@informatik.uni-freiburg.de) -// Johannes Kalmbach (kalmbach@cs.uni-freiburg.de) +// Copyright 2022, University of Freiburg, +// Chair of Algorithms and Data Structures. +// Authors: Julian Mundhahs (mundhahj@informatik.uni-freiburg.de) +// Johannes Kalmbach (kalmbach@cs.uni-freiburg.de) #pragma once @@ -156,4 +156,9 @@ class CopyShield { bool operator==(const T& other) const requires std::equality_comparable { return *pointer_ == other; } + + friend std::ostream& operator<<(std::ostream& os, const CopyShield& s) { + os << *s.pointer_; + return os; + } }; From 45cc1b460ca0d1b5c7019e83e449d16acf6ff7a7 Mon Sep 17 00:00:00 2001 From: realHannes Date: Mon, 18 Nov 2024 20:23:57 +0100 Subject: [PATCH 55/81] changes w.r.t. comments from #1573 --- src/engine/Filter.cpp | 26 +++-- src/engine/Filter.h | 6 +- src/engine/IndexScan.cpp | 94 +++++++++++++------ src/engine/IndexScan.h | 40 +++++--- src/engine/Operation.cpp | 18 ++-- src/engine/Operation.h | 31 +++--- src/engine/QueryExecutionTree.cpp | 2 +- src/engine/QueryExecutionTree.h | 12 +-- .../PrefilterExpressionIndex.cpp | 17 ++++ .../PrefilterExpressionIndex.h | 8 ++ test/PrefilterExpressionIndexTest.cpp | 21 +++-- 11 files changed, 185 insertions(+), 90 deletions(-) diff --git a/src/engine/Filter.cpp b/src/engine/Filter.cpp index be8009b617..ed9f3ac88b 100644 --- a/src/engine/Filter.cpp +++ b/src/engine/Filter.cpp @@ -28,7 +28,7 @@ Filter::Filter(QueryExecutionContext* qec, : Operation(qec), _subtree(std::move(subtree)), _expression{std::move(expression)} { - setPrefilterExpressionForIndexScanChildren(); + setPrefilterExpressionForDirectIndexScanChild(); } // _____________________________________________________________________________ @@ -45,14 +45,26 @@ string Filter::getDescriptor() const { } //______________________________________________________________________________ -void Filter::setPrefilterExpressionForIndexScanChildren() { - const std::vector& prefilterVec = +void Filter::setPrefilterExpressionForDirectIndexScanChild() { + std::vector prefilterPairs = _expression.getPrefilterExpressionForMetadata(); - this->forAllDescendants([&prefilterVec](const QueryExecutionTree* ptr) { - if (ptr) { - ptr->setPrefilterExpression(prefilterVec); + std::vector relevantPairs; + relevantPairs.reserve(prefilterPairs.size()); + VariableToColumnMap varToColMap = _subtree->getVariableColumns(); + // Add all the PrefilterVariable values whose Variable value is + // contained in the VariableToColumnMap. This is done to avoid that certain + // subqueries filter out too much. + for (auto& prefilterPair : prefilterPairs) { + if (varToColMap.find(prefilterPair.second) != varToColMap.end()) { + relevantPairs.emplace_back(std::move(prefilterPair)); } - }); + } + auto optNewSubTree = + _subtree->getRootOperation()->setPrefilterExprGetUpdatedQetPtr( + std::move(relevantPairs)); + if (optNewSubTree.has_value()) { + _subtree = std::move(optNewSubTree.value()); + } } // _____________________________________________________________________________ diff --git a/src/engine/Filter.h b/src/engine/Filter.h index a475758e0f..4dd1933fc5 100644 --- a/src/engine/Filter.h +++ b/src/engine/Filter.h @@ -59,7 +59,11 @@ class Filter : public Operation { return _subtree->getVariableColumns(); } - void setPrefilterExpressionForIndexScanChildren(); + // This method is directly called by the constructor. + // It sets the appropriate `` pair for each + // `IndexScan` child by invoking `setPrefilterExpression` on all descendants + // in the `QueryExecutionTree`. + void setPrefilterExpressionForDirectIndexScanChild(); ProtoResult computeResult(bool requestLaziness) override; diff --git a/src/engine/IndexScan.cpp b/src/engine/IndexScan.cpp index 2040d3d8de..a45ba069a7 100644 --- a/src/engine/IndexScan.cpp +++ b/src/engine/IndexScan.cpp @@ -18,14 +18,14 @@ using std::string; // _____________________________________________________________________________ IndexScan::IndexScan(QueryExecutionContext* qec, Permutation::Enum permutation, const SparqlTripleSimple& triple, Graphs graphsToFilter, - PrefilterIndexPairs prefilters) + PrefilterIndexPair prefilter) : Operation(qec), permutation_(permutation), subject_(triple.s_), predicate_(triple.p_), object_(triple.o_), graphsToFilter_{std::move(graphsToFilter)}, - prefilters_{std::move(prefilters)}, + prefilter_{std::move(prefilter)}, numVariables_(static_cast(subject_.isVariable()) + static_cast(predicate_.isVariable()) + static_cast(object_.isVariable())) { @@ -54,9 +54,26 @@ IndexScan::IndexScan(QueryExecutionContext* qec, Permutation::Enum permutation, // _____________________________________________________________________________ IndexScan::IndexScan(QueryExecutionContext* qec, Permutation::Enum permutation, const SparqlTriple& triple, Graphs graphsToFilter, - PrefilterIndexPairs prefilters) + PrefilterIndexPair prefilter) : IndexScan(qec, permutation, triple.getSimple(), std::move(graphsToFilter), - std::move(prefilters)) {} + std::move(prefilter)) {} + +// _____________________________________________________________________________ +IndexScan::IndexScan(QueryExecutionContext* qec, Permutation::Enum permutation, + const TripleComponent& s, const TripleComponent& p, + const TripleComponent& o, + const std::vector& additionalColumns, + const std::vector& additionalVariables, + Graphs graphsToFilter, PrefilterIndexPair prefilter) + : Operation(qec), + permutation_(permutation), + subject_(s), + predicate_(p), + object_(o), + graphsToFilter_(std::move(graphsToFilter)), + prefilter_(std::move(prefilter)), + additionalColumns_(additionalColumns), + additionalVariables_(additionalVariables) {} // _____________________________________________________________________________ string IndexScan::getCacheKeyImpl() const { @@ -119,21 +136,34 @@ vector IndexScan::resultSortedOn() const { } // _____________________________________________________________________________ -void IndexScan::setPrefilterExpression( +std::optional> +IndexScan::setPrefilterExprGetUpdatedQetPtr( const std::vector& prefilterVariablePairs) { - const std::vector& sortedColumns = resultSortedOn(); - VariableToColumnMap varToColMap = computeVariableToColumnMap(); + // The column index of the first sorted column. + const ColumnIndex sortedIdx = 0; + if (numVariables_ < 1) { + return std::nullopt; + } - const auto addPrefilterIfSorted = [&](const PrefilterVariablePair& pair) { - const Variable& variable = pair.second; - if (varToColMap.find(variable) != varToColMap.end()) { - const ColumnIndex colIdx = varToColMap[variable].columnIndex_; - if (std::ranges::find(sortedColumns, colIdx) != sortedColumns.end()) { - prefilters_.push_back(std::make_pair(pair.first->clone(), colIdx)); - } + VariableToColumnMap varToColMap = computeVariableToColumnMap(); + // Search for a Variable key-value given the sortedIdx (index of first sorted + // column) in the VariableToColumnMap. + auto mapIt = + std::ranges::find_if(varToColMap, [&sortedIdx](const auto& keyValuePair) { + return keyValuePair.second.columnIndex_ == sortedIdx; + }); + if (mapIt != varToColMap.end()) { + // Check if the previously found Variable (key-value from varToColMap) + // matches with a pair. + auto itPairs = std::ranges::find_if( + prefilterVariablePairs, + [&mapIt](const auto& pair) { return pair.second == mapIt->first; }); + if (itPairs != prefilterVariablePairs.end()) { + return makeCopyWithAddedPrefilters( + std::make_pair(itPairs->first->clone(), sortedIdx)); } - }; - std::ranges::for_each(prefilterVariablePairs, addPrefilterIfSorted); + } + return std::nullopt; } // _____________________________________________________________________________ @@ -155,12 +185,23 @@ VariableToColumnMap IndexScan::computeVariableToColumnMap() const { return variableToColumnMap; } +//______________________________________________________________________________ +std::shared_ptr IndexScan::makeCopyWithAddedPrefilters( + PrefilterIndexPair prefilter) { + return ad_utility::makeExecutionTree( + getExecutionContext(), permutation_, subject_, predicate_, object_, + additionalColumns_, additionalVariables_, std::move(graphsToFilter_), + std::move(prefilter)); +} + //______________________________________________________________________________ std::vector IndexScan::applyFilterBlockMetadata( - std::vector&& blocks) const { - std::ranges::for_each(prefilters_, [&blocks](const PrefilterIndexPair& pair) { - pair.first->evaluate(blocks, pair.second); - }); + const std::vector blocks) const { + if (prefilter_.has_value()) { + auto& prefilterIndexPair = prefilter_.value(); + return prefilterExpressions::detail::evaluatePrefilterExpressionOnMetadata( + prefilterIndexPair.first->clone(), blocks, prefilterIndexPair.second); + } return blocks; }; @@ -178,7 +219,7 @@ Result::Generator IndexScan::chunkedIndexScan() const { } // _____________________________________________________________________________ -IdTable IndexScan::completeIndexScan() const { +IdTable IndexScan::materializedIndexScan() const { // Get the blocks. auto metadata = getMetadataForScan(); auto blockSpan = @@ -189,7 +230,7 @@ IdTable IndexScan::completeIndexScan() const { ? std::optional{applyFilterBlockMetadata( {blockSpan.begin(), blockSpan.end()})} : std::nullopt; - // Create IdTable, fill it with content by performing scan(). + // Create the IdTable and fill it with content by performing scan(). using enum Permutation::Enum; IdTable idTable{getExecutionContext()->getAllocator()}; idTable.setNumColumns(numVariables_); @@ -211,7 +252,7 @@ ProtoResult IndexScan::computeResult(bool requestLaziness) { if (requestLaziness) { return {chunkedIndexScan(), resultSortedOn()}; } - return {completeIndexScan(), getResultSortedOn(), LocalVocab{}}; + return {materializedIndexScan(), getResultSortedOn(), LocalVocab{}}; } // _____________________________________________________________________________ @@ -287,7 +328,7 @@ ScanSpecificationAsTripleComponent IndexScan::getScanSpecificationTc() const { // _____________________________________________________________________________ Permutation::IdTableGenerator IndexScan::getLazyScan( - std::vector&& blocks) const { + std::vector blocks) const { // If there is a LIMIT or OFFSET clause that constrains the scan // (which can happen with an explicit subquery), we cannot use the prefiltered // blocks, as we currently have no mechanism to include limits and offsets @@ -355,8 +396,7 @@ IndexScan::lazyScanForJoinOfTwoScans(const IndexScan& s1, const IndexScan& s2) { auto [blocks1, blocks2] = CompressedRelationReader::getBlocksForJoin( metaBlocks1.value(), metaBlocks2.value()); - std::array result{s1.getLazyScan(std::move(blocks1)), - s2.getLazyScan(std::move(blocks2))}; + std::array result{s1.getLazyScan(blocks1), s2.getLazyScan(blocks2)}; result[0].details().numBlocksAll_ = metaBlocks1.value().blockMetadata_.size(); result[1].details().numBlocksAll_ = metaBlocks2.value().blockMetadata_.size(); return result; @@ -377,7 +417,7 @@ Permutation::IdTableGenerator IndexScan::lazyScanForJoinOfColumnWithScan( auto blocks = CompressedRelationReader::getBlocksForJoin(joinColumn, metaBlocks1.value()); - auto result = getLazyScan(std::move(blocks)); + auto result = getLazyScan(blocks); result.details().numBlocksAll_ = metaBlocks1.value().blockMetadata_.size(); return result; } diff --git a/src/engine/IndexScan.h b/src/engine/IndexScan.h index c5efb552f1..ee89dd5aa9 100644 --- a/src/engine/IndexScan.h +++ b/src/engine/IndexScan.h @@ -12,12 +12,10 @@ class SparqlTripleSimple; class IndexScan final : public Operation { using Graphs = ScanSpecificationAsTripleComponent::Graphs; - // Pair containing a `PrefilterExpression` with `ColumnIndex` (eval. index) - using PrefilterIndexPair = - std::pair, - ColumnIndex>; - // Vector with `PrefilterIndexPair` values. - using PrefilterIndexPairs = std::vector; + // Optional pair containing a `PrefilterExpression` with `ColumnIndex` (eval. + // index) + using PrefilterIndexPair = std::optional, ColumnIndex>>; private: Permutation::Enum permutation_; @@ -25,7 +23,7 @@ class IndexScan final : public Operation { TripleComponent predicate_; TripleComponent object_; Graphs graphsToFilter_; - PrefilterIndexPairs prefilters_; + PrefilterIndexPair prefilter_; size_t numVariables_; size_t sizeEstimate_; bool sizeEstimateIsExact_; @@ -40,11 +38,18 @@ class IndexScan final : public Operation { public: IndexScan(QueryExecutionContext* qec, Permutation::Enum permutation, const SparqlTriple& triple, Graphs graphsToFilter = std::nullopt, - PrefilterIndexPairs prefilters = {}); + PrefilterIndexPair prefilter = std::nullopt); IndexScan(QueryExecutionContext* qec, Permutation::Enum permutation, const SparqlTripleSimple& triple, Graphs graphsToFilter = std::nullopt, - PrefilterIndexPairs prefilters = {}); + PrefilterIndexPair prefilter = std::nullopt); + // Constructor to simplify copy creation of an `IndexScan`. + IndexScan(QueryExecutionContext* qec, Permutation::Enum permutation, + const TripleComponent& s, const TripleComponent& p, + const TripleComponent& o, + const std::vector& additionalColumns, + const std::vector& additionalVariables, + Graphs graphsToFilter, PrefilterIndexPair prefilter); ~IndexScan() override = default; @@ -66,9 +71,11 @@ class IndexScan final : public Operation { vector resultSortedOn() const override; - // Set `PrefilterExpression`s. - void setPrefilterExpression(const std::vector& - prefilterVariablePairs) override; + // Set `PrefilterExpression`s and return updated `QueryExecutionTree` pointer + // if necessary. + std::optional> + setPrefilterExprGetUpdatedQetPtr(const std::vector& + prefilterVariablePairs) override; size_t numVariables() const { return numVariables_; } @@ -151,19 +158,22 @@ class IndexScan final : public Operation { VariableToColumnMap computeVariableToColumnMap() const override; + std::shared_ptr makeCopyWithAddedPrefilters( + PrefilterIndexPair prefilter); + // Filter relevant `CompressedBlockMetadata` blocks by applying the // `PrefilterExpression`s from `prefilters_`. std::vector applyFilterBlockMetadata( - std::vector&& blocks) const; + const std::vector blocks) const; // Return the (lazy) `IdTable` for this `IndexScan` in chunks. Result::Generator chunkedIndexScan() const; // Get the `IdTable` for this `IndexScan` in one piece. - IdTable completeIndexScan() const; + IdTable materializedIndexScan() const; // Helper functions for the public `getLazyScanFor...` methods and // `chunkedIndexScan` (see above). Permutation::IdTableGenerator getLazyScan( - std::vector&& blocks) const; + std::vector blocks) const; std::optional getMetadataForScan() const; }; diff --git a/src/engine/Operation.cpp b/src/engine/Operation.cpp index 21a00cb458..27bb450c6a 100644 --- a/src/engine/Operation.cpp +++ b/src/engine/Operation.cpp @@ -12,36 +12,30 @@ using namespace std::chrono_literals; //______________________________________________________________________________ template -void Operation::forAllDescendantsImpl(F&& f) { +void Operation::forAllDescendantsImpl(F f) { static_assert( std::is_same_v>); for (auto ptr : getChildren()) { if (ptr) { - std::forward(f)(ptr); - ptr->forAllDescendants(std::forward(f)); + f(ptr); + ptr->forAllDescendants(f); } } } //______________________________________________________________________________ template -void Operation::forAllDescendantsImpl(F&& f) const { +void Operation::forAllDescendantsImpl(F f) const { static_assert( std::is_same_v>); for (auto ptr : getChildren()) { if (ptr) { - std::forward(f)(ptr); - ptr->forAllDescendants(std::forward(f)); + f(ptr); + ptr->forAllDescendants(f); } } } -// _____________________________________________________________________________ -void Operation::forAllDescendants( - std::function&& func) { - forAllDescendantsImpl(std::move(func)); -} - // _____________________________________________________________________________ vector Operation::collectWarnings() const { vector res = getWarnings(); diff --git a/src/engine/Operation.h b/src/engine/Operation.h index 8e642ed442..bda63db8b4 100644 --- a/src/engine/Operation.h +++ b/src/engine/Operation.h @@ -78,21 +78,22 @@ class Operation { } // Set `PrefilterExpression`s (for `IndexScan`). - virtual void setPrefilterExpression( - const std::vector&){ - // The prefiltering procedure is implemented by applying the - // PrefilterExpressions directly on the CompressedBlockMetadata. - // This is currently done while performing the result computation for - // IndexScan. - // (1) Overwrite this method for the derived IndexScan class. - // (2) The default method for all other derived classes is implemented - // here, no PrefilterExpressions need to be set. + // The prefiltering procedure is implemented by applying the + // PrefilterExpressions directly on the CompressedBlockMetadata. + // This is currently done while performing the result computation for + // IndexScan. + // (1) Overwrite this method for the derived `IndexScan` class. Given + // the resulting changes w.r.t. `IndexScan`, we also have to return an updated + // `QueryExecutionTree` pointer. + // (2) The default method for all other derived classes is implemented here, + // no PrefilterExpressions need to be set. Given that no changes occur for an + // `Operation` object here, return std::nullopt to indicate that nothing has + // changed. + virtual std::optional> + setPrefilterExprGetUpdatedQetPtr(const std::vector&) { + return std::nullopt; }; - // Apply the provided void function for all descendants. The given function - // must be invocable with a `const QueryExecutionTree*` object. - void forAllDescendants(std::function&& func); - // Get a unique, not ambiguous string representation for a subtree. // This should act like an ID for each subtree. // Calls `getCacheKeyImpl` and adds the information about the `LIMIT` clause. @@ -374,11 +375,11 @@ class Operation { // Recursively call a function on all children. template - void forAllDescendantsImpl(F&& f); + void forAllDescendantsImpl(F f); // Recursively call a function on all children. template - void forAllDescendantsImpl(F&& f) const; + void forAllDescendantsImpl(F f) const; // Holds a precomputed Result of this operation if it is the sibling of a // Service operation. diff --git a/src/engine/QueryExecutionTree.cpp b/src/engine/QueryExecutionTree.cpp index 6f499b5ef8..2f42cf51b2 100644 --- a/src/engine/QueryExecutionTree.cpp +++ b/src/engine/QueryExecutionTree.cpp @@ -99,7 +99,7 @@ size_t QueryExecutionTree::getSizeEstimate() { void QueryExecutionTree::setPrefilterExpression( const std::vector& prefilterVec) const { AD_CONTRACT_CHECK(rootOperation_); - rootOperation_->setPrefilterExpression(prefilterVec); + rootOperation_->setPrefilterExprGetUpdatedQetPtr(prefilterVec); } // _____________________________________________________________________________ diff --git a/src/engine/QueryExecutionTree.h b/src/engine/QueryExecutionTree.h index c08864e3aa..d2cc452b1d 100644 --- a/src/engine/QueryExecutionTree.h +++ b/src/engine/QueryExecutionTree.h @@ -111,26 +111,26 @@ class QueryExecutionTree { } template - void forAllDescendants(F&& f) { + void forAllDescendants(F f) { static_assert( std::is_same_v>); for (auto ptr : rootOperation_->getChildren()) { if (ptr) { - std::forward(f)(ptr); - ptr->forAllDescendants(std::forward(f)); + f(ptr); + ptr->forAllDescendants(f); } } } template - void forAllDescendants(F&& f) const { + void forAllDescendants(F f) const { static_assert( std::is_same_v>); for (auto ptr : rootOperation_->getChildren()) { if (ptr) { - std::forward(f)(ptr); - ptr->forAllDescendants(std::forward(f)); + f(ptr); + ptr->forAllDescendants(f); } } } diff --git a/src/engine/sparqlExpressions/PrefilterExpressionIndex.cpp b/src/engine/sparqlExpressions/PrefilterExpressionIndex.cpp index b7a7c2fd0e..5e5b73cbd1 100644 --- a/src/engine/sparqlExpressions/PrefilterExpressionIndex.cpp +++ b/src/engine/sparqlExpressions/PrefilterExpressionIndex.cpp @@ -417,6 +417,23 @@ template class LogicalExpression; template class LogicalExpression; namespace detail { +//______________________________________________________________________________ +std::vector evaluatePrefilterExpressionOnMetadata( + const std::unique_ptr prefilterExpr, + const std::vector& blocks, const size_t evaluationColumn) { + if (blocks.size() <= 2) { + return blocks; + } + BlockMetadata firstBlock = blocks.front(); + BlockMetadata lastBlock = blocks.back(); + std::vector blocksAdjusted(blocks.begin() + 1, + blocks.end() - 1); + blocksAdjusted = prefilterExpr->evaluate(blocksAdjusted, evaluationColumn); + blocksAdjusted.insert(blocksAdjusted.begin(), firstBlock); + blocksAdjusted.push_back(lastBlock); + return blocksAdjusted; +} + //______________________________________________________________________________ void checkPropertiesForPrefilterConstruction( const std::vector& vec) { diff --git a/src/engine/sparqlExpressions/PrefilterExpressionIndex.h b/src/engine/sparqlExpressions/PrefilterExpressionIndex.h index a5ca2c8d8d..4100d0fe74 100644 --- a/src/engine/sparqlExpressions/PrefilterExpressionIndex.h +++ b/src/engine/sparqlExpressions/PrefilterExpressionIndex.h @@ -206,6 +206,14 @@ using OrExpression = prefilterExpressions::LogicalExpression< prefilterExpressions::LogicalOperator::OR>; namespace detail { +//______________________________________________________________________________ +// Helper function to perform the evaluation for the provided +// `PrefilterExpression` on the given `BlockMetadata` values, while taking into +// account possible incomplete blocks. +std::vector evaluatePrefilterExpressionOnMetadata( + const std::unique_ptr prefilterExpr, + const std::vector& blocks, const size_t evaluationColumn); + //______________________________________________________________________________ // Pair containing a `PrefilterExpression` and its corresponding `Variable`. using PrefilterExprVariablePair = diff --git a/test/PrefilterExpressionIndexTest.cpp b/test/PrefilterExpressionIndexTest.cpp index a941afab6a..82d3386fb6 100644 --- a/test/PrefilterExpressionIndexTest.cpp +++ b/test/PrefilterExpressionIndexTest.cpp @@ -84,8 +84,10 @@ class PrefilterExpressionOnMetadataTest : public ::testing::Test { const BlockMetadata b24 = makeBlock(DateId(DateParser, "2024-10-08"), BlankNodeId(10)); - // All blocks that contain mixed (ValueId) types over column 0 - const std::vector mixedBlocks = {b2, b4, b11, b18, b22, b24}; + // All blocks that contain mixed (ValueId) types over column 0, + // or possibly incomplete ones. + const std::vector mixedAndPossiblyIncompleteBlocks = { + b1, b2, b4, b11, b18, b22, b24}; // Ordered and unique vector with BlockMetadata const std::vector blocks = { @@ -142,14 +144,18 @@ class PrefilterExpressionOnMetadataTest : public ::testing::Test { auto makeTest(std::unique_ptr expr, std::vector&& expected) { std::vector expectedAdjusted; - // This is for convenience, we automatically insert all mixed blocks - // which must be always returned. + // This is for convenience, we automatically insert all mixed and possibly + // incomplete blocks which must be always returned. std::ranges::set_union( - expected, mixedBlocks, std::back_inserter(expectedAdjusted), + expected, mixedAndPossiblyIncompleteBlocks, + std::back_inserter(expectedAdjusted), [](const BlockMetadata& b1, const BlockMetadata& b2) { return b1.blockIndex_ < b2.blockIndex_; }); - ASSERT_EQ(expr->evaluate(blocks, 0), expectedAdjusted); + ASSERT_EQ( + prefilterExpressions::detail::evaluatePrefilterExpressionOnMetadata( + std::move(expr), blocks, 0), + expectedAdjusted); } }; @@ -525,6 +531,9 @@ TEST_F(PrefilterExpressionOnMetadataTest, testWithOneBlockMetadataValue) { EXPECT_EQ(expr->evaluate(input, 0), input); EXPECT_EQ(expr->evaluate(input, 1), std::vector{}); EXPECT_EQ(expr->evaluate(input, 2), std::vector{}); + EXPECT_EQ(prefilterExpressions::detail::evaluatePrefilterExpressionOnMetadata( + std::move(expr), input, 0), + input); } //______________________________________________________________________________ From 0b30ad3e3d6eb7ba8ae8b90bb05f3e28b0d9a72e Mon Sep 17 00:00:00 2001 From: realHannes Date: Wed, 20 Nov 2024 18:19:09 +0100 Subject: [PATCH 56/81] updates from code review (1) --- src/engine/Filter.cpp | 14 +-- src/engine/IndexScan.cpp | 95 +++++++++++-------- src/engine/IndexScan.h | 28 +++--- src/engine/Operation.cpp | 8 +- src/engine/Operation.h | 7 +- src/engine/QueryExecutionTree.cpp | 19 +++- src/engine/QueryExecutionTree.h | 10 +- .../PrefilterExpressionIndex.cpp | 74 ++++++++++----- .../PrefilterExpressionIndex.h | 45 +++++---- test/PrefilterExpressionIndexTest.cpp | 27 +++--- 10 files changed, 194 insertions(+), 133 deletions(-) diff --git a/src/engine/Filter.cpp b/src/engine/Filter.cpp index ed9f3ac88b..85773254e5 100644 --- a/src/engine/Filter.cpp +++ b/src/engine/Filter.cpp @@ -48,20 +48,8 @@ string Filter::getDescriptor() const { void Filter::setPrefilterExpressionForDirectIndexScanChild() { std::vector prefilterPairs = _expression.getPrefilterExpressionForMetadata(); - std::vector relevantPairs; - relevantPairs.reserve(prefilterPairs.size()); - VariableToColumnMap varToColMap = _subtree->getVariableColumns(); - // Add all the PrefilterVariable values whose Variable value is - // contained in the VariableToColumnMap. This is done to avoid that certain - // subqueries filter out too much. - for (auto& prefilterPair : prefilterPairs) { - if (varToColMap.find(prefilterPair.second) != varToColMap.end()) { - relevantPairs.emplace_back(std::move(prefilterPair)); - } - } auto optNewSubTree = - _subtree->getRootOperation()->setPrefilterExprGetUpdatedQetPtr( - std::move(relevantPairs)); + _subtree->setPrefilterExprGetUpdatedQetPtr(std::move(prefilterPairs)); if (optNewSubTree.has_value()) { _subtree = std::move(optNewSubTree.value()); } diff --git a/src/engine/IndexScan.cpp b/src/engine/IndexScan.cpp index a45ba069a7..948504d63c 100644 --- a/src/engine/IndexScan.cpp +++ b/src/engine/IndexScan.cpp @@ -62,8 +62,8 @@ IndexScan::IndexScan(QueryExecutionContext* qec, Permutation::Enum permutation, IndexScan::IndexScan(QueryExecutionContext* qec, Permutation::Enum permutation, const TripleComponent& s, const TripleComponent& p, const TripleComponent& o, - const std::vector& additionalColumns, - const std::vector& additionalVariables, + std::vector additionalColumns, + std::vector additionalVariables, Graphs graphsToFilter, PrefilterIndexPair prefilter) : Operation(qec), permutation_(permutation), @@ -72,8 +72,8 @@ IndexScan::IndexScan(QueryExecutionContext* qec, Permutation::Enum permutation, object_(o), graphsToFilter_(std::move(graphsToFilter)), prefilter_(std::move(prefilter)), - additionalColumns_(additionalColumns), - additionalVariables_(additionalVariables) {} + additionalColumns_(std::move(additionalColumns)), + additionalVariables_(std::move(additionalVariables)) {} // _____________________________________________________________________________ string IndexScan::getCacheKeyImpl() const { @@ -109,6 +109,12 @@ string IndexScan::getCacheKeyImpl() const { os << "\nFiltered by Graphs:"; os << absl::StrJoin(graphIdVec, " "); } + if (prefilter_.has_value()) { + auto& [prefilterExpr, columnIdx] = prefilter_.value(); + os << "Added PrefiterExpression: \n"; + os << *prefilterExpr; + os << "\n Applied on: " << columnIdx << "."; + } return std::move(os).str(); } @@ -138,7 +144,7 @@ vector IndexScan::resultSortedOn() const { // _____________________________________________________________________________ std::optional> IndexScan::setPrefilterExprGetUpdatedQetPtr( - const std::vector& prefilterVariablePairs) { + std::vector prefilterVariablePairs) { // The column index of the first sorted column. const ColumnIndex sortedIdx = 0; if (numVariables_ < 1) { @@ -148,10 +154,9 @@ IndexScan::setPrefilterExprGetUpdatedQetPtr( VariableToColumnMap varToColMap = computeVariableToColumnMap(); // Search for a Variable key-value given the sortedIdx (index of first sorted // column) in the VariableToColumnMap. - auto mapIt = - std::ranges::find_if(varToColMap, [&sortedIdx](const auto& keyValuePair) { - return keyValuePair.second.columnIndex_ == sortedIdx; - }); + auto mapIt = std::ranges::find_if(varToColMap, [](const auto& keyValuePair) { + return keyValuePair.second.columnIndex_ == sortedIdx; + }); if (mapIt != varToColMap.end()) { // Check if the previously found Variable (key-value from varToColMap) // matches with a pair. @@ -187,33 +192,21 @@ VariableToColumnMap IndexScan::computeVariableToColumnMap() const { //______________________________________________________________________________ std::shared_ptr IndexScan::makeCopyWithAddedPrefilters( - PrefilterIndexPair prefilter) { + PrefilterIndexPair prefilter) const { return ad_utility::makeExecutionTree( getExecutionContext(), permutation_, subject_, predicate_, object_, - additionalColumns_, additionalVariables_, std::move(graphsToFilter_), + additionalColumns_, additionalVariables_, graphsToFilter_, std::move(prefilter)); } -//______________________________________________________________________________ -std::vector IndexScan::applyFilterBlockMetadata( - const std::vector blocks) const { - if (prefilter_.has_value()) { - auto& prefilterIndexPair = prefilter_.value(); - return prefilterExpressions::detail::evaluatePrefilterExpressionOnMetadata( - prefilterIndexPair.first->clone(), blocks, prefilterIndexPair.second); - } - return blocks; -}; - // _____________________________________________________________________________ Result::Generator IndexScan::chunkedIndexScan() const { - auto metadata = getMetadataForScan(); - if (!metadata.has_value()) { + auto optBlockSpan = getOptionalBlockSpan(); + if (!optBlockSpan.has_value()) { co_return; } - auto blocksSpan = - CompressedRelationReader::getBlocksFromMetadata(metadata.value()); - for (IdTable& idTable : getLazyScan({blocksSpan.begin(), blocksSpan.end()})) { + const auto& blockSpan = optBlockSpan.value(); + for (IdTable& idTable : getLazyScan({blockSpan.begin(), blockSpan.end()})) { co_yield {std::move(idTable), LocalVocab{}}; } } @@ -221,15 +214,13 @@ Result::Generator IndexScan::chunkedIndexScan() const { // _____________________________________________________________________________ IdTable IndexScan::materializedIndexScan() const { // Get the blocks. - auto metadata = getMetadataForScan(); - auto blockSpan = - metadata.has_value() - ? CompressedRelationReader::getBlocksFromMetadata(metadata.value()) - : std::span{}; - auto optFilteredBlocks = getLimit().isUnconstrained() - ? std::optional{applyFilterBlockMetadata( - {blockSpan.begin(), blockSpan.end()})} - : std::nullopt; + auto optBlockSpan = getOptionalBlockSpan(); + std::optional> optFilteredBlocks = {}; + if (optBlockSpan.has_value()) { + const auto& blockSpan = optBlockSpan.value(); + optFilteredBlocks = + getOptionalPrefilteredBlocks({blockSpan.begin(), blockSpan.end()}); + } // Create the IdTable and fill it with content by performing scan(). using enum Permutation::Enum; IdTable idTable{getExecutionContext()->getAllocator()}; @@ -326,6 +317,33 @@ ScanSpecificationAsTripleComponent IndexScan::getScanSpecificationTc() const { graphsToFilter_}; } +// _____________________________________________________________________________ +std::optional> +IndexScan::getOptionalBlockSpan() const { + auto metadata = getMetadataForScan(); + if (metadata.has_value()) { + return CompressedRelationReader::getBlocksFromMetadata(metadata.value()); + } + return std::nullopt; +} + +// _____________________________________________________________________________ +std::optional> +IndexScan::getOptionalPrefilteredBlocks( + std::vector blocks) const { + if (!getLimit().isUnconstrained()) { + return std::nullopt; + } + + if (prefilter_.has_value()) { + // Apply the prefilter on given blocks. + auto& [prefilterExpr, columnIndex] = prefilter_.value(); + return prefilterExpr->evaluate(blocks, columnIndex); + } else { + return std::move(blocks); + } +} + // _____________________________________________________________________________ Permutation::IdTableGenerator IndexScan::getLazyScan( std::vector blocks) const { @@ -333,10 +351,7 @@ Permutation::IdTableGenerator IndexScan::getLazyScan( // (which can happen with an explicit subquery), we cannot use the prefiltered // blocks, as we currently have no mechanism to include limits and offsets // into the prefiltering (`std::nullopt` means `scan all blocks`). - auto actualBlocks = - getLimit().isUnconstrained() - ? std::optional{applyFilterBlockMetadata(std::move(blocks))} - : std::nullopt; + auto actualBlocks = getOptionalPrefilteredBlocks(std::move(blocks)); return getIndex() .getImpl() .getPermutation(permutation()) diff --git a/src/engine/IndexScan.h b/src/engine/IndexScan.h index ee89dd5aa9..9a42c866f7 100644 --- a/src/engine/IndexScan.h +++ b/src/engine/IndexScan.h @@ -47,9 +47,9 @@ class IndexScan final : public Operation { IndexScan(QueryExecutionContext* qec, Permutation::Enum permutation, const TripleComponent& s, const TripleComponent& p, const TripleComponent& o, - const std::vector& additionalColumns, - const std::vector& additionalVariables, - Graphs graphsToFilter, PrefilterIndexPair prefilter); + std::vector additionalColumns, + std::vector additionalVariables, Graphs graphsToFilter, + PrefilterIndexPair prefilter); ~IndexScan() override = default; @@ -74,8 +74,8 @@ class IndexScan final : public Operation { // Set `PrefilterExpression`s and return updated `QueryExecutionTree` pointer // if necessary. std::optional> - setPrefilterExprGetUpdatedQetPtr(const std::vector& - prefilterVariablePairs) override; + setPrefilterExprGetUpdatedQetPtr( + std::vector prefilterVariablePairs) override; size_t numVariables() const { return numVariables_; } @@ -159,18 +159,24 @@ class IndexScan final : public Operation { VariableToColumnMap computeVariableToColumnMap() const override; std::shared_ptr makeCopyWithAddedPrefilters( - PrefilterIndexPair prefilter); - - // Filter relevant `CompressedBlockMetadata` blocks by applying the - // `PrefilterExpression`s from `prefilters_`. - std::vector applyFilterBlockMetadata( - const std::vector blocks) const; + PrefilterIndexPair prefilter) const; // Return the (lazy) `IdTable` for this `IndexScan` in chunks. Result::Generator chunkedIndexScan() const; // Get the `IdTable` for this `IndexScan` in one piece. IdTable materializedIndexScan() const; + // Helper to retrieve the `CompressedBlockMetadata` span for this scan. + std::optional> getOptionalBlockSpan() + const; + + // If `isUnconstrained()` yields true, return the blocks as given or the + // prefiltered blocks (if `prefilter_` has value). If `isUnconstrained()` is + // false, return `std::nullopt`. + std::optional> + getOptionalPrefilteredBlocks( + std::vector blocks) const; + // Helper functions for the public `getLazyScanFor...` methods and // `chunkedIndexScan` (see above). Permutation::IdTableGenerator getLazyScan( diff --git a/src/engine/Operation.cpp b/src/engine/Operation.cpp index 27bb450c6a..91da0ffb66 100644 --- a/src/engine/Operation.cpp +++ b/src/engine/Operation.cpp @@ -12,7 +12,7 @@ using namespace std::chrono_literals; //______________________________________________________________________________ template -void Operation::forAllDescendantsImpl(F f) { +void Operation::forAllDescendants(F f) { static_assert( std::is_same_v>); for (auto ptr : getChildren()) { @@ -25,7 +25,7 @@ void Operation::forAllDescendantsImpl(F f) { //______________________________________________________________________________ template -void Operation::forAllDescendantsImpl(F f) const { +void Operation::forAllDescendants(F f) const { static_assert( std::is_same_v>); for (auto ptr : getChildren()) { @@ -55,7 +55,7 @@ vector Operation::collectWarnings() const { void Operation::recursivelySetCancellationHandle( SharedCancellationHandle cancellationHandle) { AD_CORRECTNESS_CHECK(cancellationHandle); - forAllDescendantsImpl([&cancellationHandle](auto child) { + forAllDescendants([&cancellationHandle](auto child) { child->getRootOperation()->cancellationHandle_ = cancellationHandle; }); cancellationHandle_ = std::move(cancellationHandle); @@ -66,7 +66,7 @@ void Operation::recursivelySetCancellationHandle( void Operation::recursivelySetTimeConstraint( std::chrono::steady_clock::time_point deadline) { deadline_ = deadline; - forAllDescendantsImpl([deadline](auto child) { + forAllDescendants([deadline](auto child) { child->getRootOperation()->deadline_ = deadline; }); } diff --git a/src/engine/Operation.h b/src/engine/Operation.h index 7cacf6b1c8..4f91908e6c 100644 --- a/src/engine/Operation.h +++ b/src/engine/Operation.h @@ -96,7 +96,8 @@ class Operation { // `Operation` object here, return std::nullopt to indicate that nothing has // changed. virtual std::optional> - setPrefilterExprGetUpdatedQetPtr(const std::vector&) { + setPrefilterExprGetUpdatedQetPtr( + [[maybe_unused]] std::vector prefilterPairs) { return std::nullopt; }; @@ -381,11 +382,11 @@ class Operation { // Recursively call a function on all children. template - void forAllDescendantsImpl(F f); + void forAllDescendants(F f); // Recursively call a function on all children. template - void forAllDescendantsImpl(F f) const; + void forAllDescendants(F f) const; // Holds a precomputed Result of this operation if it is the sibling of a // Service operation. diff --git a/src/engine/QueryExecutionTree.cpp b/src/engine/QueryExecutionTree.cpp index d78eb2a0c7..f432d719d2 100644 --- a/src/engine/QueryExecutionTree.cpp +++ b/src/engine/QueryExecutionTree.cpp @@ -105,10 +105,23 @@ size_t QueryExecutionTree::getSizeEstimate() { } //_____________________________________________________________________________ -void QueryExecutionTree::setPrefilterExpression( - const std::vector& prefilterVec) const { +std::optional> +QueryExecutionTree::setPrefilterExprGetUpdatedQetPtr( + std::vector prefilterPairs) const { AD_CONTRACT_CHECK(rootOperation_); - rootOperation_->setPrefilterExprGetUpdatedQetPtr(prefilterVec); + std::vector relevantPairs; + relevantPairs.reserve(prefilterPairs.size()); + VariableToColumnMap varToColMap = getVariableColumns(); + // Add all the PrefilterVariable values whose Variable value is + // contained in the VariableToColumnMap. This is done to avoid that certain + // subqueries filter out too much. + for (auto& prefilterPair : prefilterPairs) { + if (varToColMap.find(prefilterPair.second) != varToColMap.end()) { + relevantPairs.emplace_back(std::move(prefilterPair)); + } + } + return rootOperation_->setPrefilterExprGetUpdatedQetPtr( + std::move(relevantPairs)); } // _____________________________________________________________________________ diff --git a/src/engine/QueryExecutionTree.h b/src/engine/QueryExecutionTree.h index dce982faa2..0f299c89c9 100644 --- a/src/engine/QueryExecutionTree.h +++ b/src/engine/QueryExecutionTree.h @@ -94,8 +94,14 @@ class QueryExecutionTree { return rootOperation_->getMultiplicity(col); } - void setPrefilterExpression( - const std::vector& prefilterVec) const; + // If the `rootOperation_` of this `QueryExecutionTree` is an `IndexScan` + // operation, this method adds the suitable `PrefilterExpression` to the + // `IndexScan` given a suitable `Variable` / `ColumnIndex` is public. + // If a `PrefilterExpression` was sucsessfully set, the updated + // `QueryExecutionTree` is returned. + std::optional> + setPrefilterExprGetUpdatedQetPtr( + std::vector prefilterPairs) const; size_t getDistinctEstimate(size_t col) const { return static_cast(rootOperation_->getSizeEstimate() / diff --git a/src/engine/sparqlExpressions/PrefilterExpressionIndex.cpp b/src/engine/sparqlExpressions/PrefilterExpressionIndex.cpp index 5e5b73cbd1..f936d6fc71 100644 --- a/src/engine/sparqlExpressions/PrefilterExpressionIndex.cpp +++ b/src/engine/sparqlExpressions/PrefilterExpressionIndex.cpp @@ -162,12 +162,50 @@ static std::string getLogicalOpStr(const LogicalOperator logOp) { // SECTION PREFILTER EXPRESSION (BASE CLASS) //______________________________________________________________________________ std::vector PrefilterExpression::evaluate( - const std::vector& input, size_t evaluationColumn) const { + std::vector& input, size_t evaluationColumn, + bool stripIncompleteBlocks) const { + if (!stripIncompleteBlocks) { + return evaluateAndCheckImpl(input, evaluationColumn); + } + + if (input.size() < 3) { + return input; + } + // Helper to check the given block for column consistency up to the evaluation + // column. + const auto checkBlockIsInconsistent = + [evaluationColumn](const BlockMetadata& block) { + return getMaskedTriple(block.firstTriple_, evaluationColumn) != + getMaskedTriple(block.lastTriple_, evaluationColumn); + }; + std::optional firstBlock = std::nullopt; + std::optional lastBlock = std::nullopt; + if (checkBlockIsInconsistent(input.front())) { + firstBlock = input.front(); + input.erase(input.begin()); + } + if (checkBlockIsInconsistent(input.back())) { + lastBlock = input.back(); + input.pop_back(); + } + auto result = evaluateAndCheckImpl(input, evaluationColumn); + if (firstBlock.has_value()) { + result.insert(result.begin(), firstBlock.value()); + } + if (lastBlock.has_value()) { + result.push_back(lastBlock.value()); + } + return result; +}; + +// _____________________________________________________________________________ +std::vector PrefilterExpression::evaluateAndCheckImpl( + std::vector& input, size_t evaluationColumn) const { checkEvalRequirements(input, evaluationColumn); const auto& relevantBlocks = evaluateImpl(input, evaluationColumn); checkEvalRequirements(relevantBlocks, evaluationColumn); return relevantBlocks; -}; +} // SECTION RELATIONAL OPERATIONS //______________________________________________________________________________ @@ -202,7 +240,7 @@ RelationalExpression::logicalComplement() const { //______________________________________________________________________________ template std::vector RelationalExpression::evaluateImpl( - const std::vector& input, size_t evaluationColumn) const { + std::vector& input, size_t evaluationColumn) const { using namespace valueIdComparators; std::vector valueIdsInput; // For each BlockMetadata value in vector input, we have a respective Id for @@ -317,15 +355,15 @@ LogicalExpression::logicalComplement() const { //______________________________________________________________________________ template std::vector LogicalExpression::evaluateImpl( - const std::vector& input, size_t evaluationColumn) const { + std::vector& input, size_t evaluationColumn) const { using enum LogicalOperator; if constexpr (Operation == AND) { - auto resultChild1 = child1_->evaluate(input, evaluationColumn); - return child2_->evaluate(resultChild1, evaluationColumn); + auto resultChild1 = child1_->evaluate(input, evaluationColumn, false); + return child2_->evaluate(resultChild1, evaluationColumn, false); } else { static_assert(Operation == OR); - return getSetUnion(child1_->evaluate(input, evaluationColumn), - child2_->evaluate(input, evaluationColumn)); + return getSetUnion(child1_->evaluate(input, evaluationColumn, false), + child2_->evaluate(input, evaluationColumn, false)); } }; @@ -376,8 +414,8 @@ std::unique_ptr NotExpression::logicalComplement() const { //______________________________________________________________________________ std::vector NotExpression::evaluateImpl( - const std::vector& input, size_t evaluationColumn) const { - return child_->evaluate(input, evaluationColumn); + std::vector& input, size_t evaluationColumn) const { + return child_->evaluate(input, evaluationColumn, false); }; //______________________________________________________________________________ @@ -417,22 +455,6 @@ template class LogicalExpression; template class LogicalExpression; namespace detail { -//______________________________________________________________________________ -std::vector evaluatePrefilterExpressionOnMetadata( - const std::unique_ptr prefilterExpr, - const std::vector& blocks, const size_t evaluationColumn) { - if (blocks.size() <= 2) { - return blocks; - } - BlockMetadata firstBlock = blocks.front(); - BlockMetadata lastBlock = blocks.back(); - std::vector blocksAdjusted(blocks.begin() + 1, - blocks.end() - 1); - blocksAdjusted = prefilterExpr->evaluate(blocksAdjusted, evaluationColumn); - blocksAdjusted.insert(blocksAdjusted.begin(), firstBlock); - blocksAdjusted.push_back(lastBlock); - return blocksAdjusted; -} //______________________________________________________________________________ void checkPropertiesForPrefilterConstruction( diff --git a/src/engine/sparqlExpressions/PrefilterExpressionIndex.h b/src/engine/sparqlExpressions/PrefilterExpressionIndex.h index 4100d0fe74..ea383d6df3 100644 --- a/src/engine/sparqlExpressions/PrefilterExpressionIndex.h +++ b/src/engine/sparqlExpressions/PrefilterExpressionIndex.h @@ -80,10 +80,19 @@ class PrefilterExpression { // take a look at the actual implementation for derived classes. virtual std::unique_ptr logicalComplement() const = 0; - // The respective metadata to the blocks is expected to be provided in - // a sorted order (w.r.t. the relevant column). - std::vector evaluate(const std::vector& input, - size_t evaluationColumn) const; + // Expects that the provided `BlockMetadata` vector adheres to the following + // conditions: + // (1) unqiueness of blocks + // (2) sorted (order) + // (3) consistent column values + // To indicate that the possibly incomplete first and last block should + // be handled appropriately, the `stripIncompleteBlocks` flag is set to + // `true`. The flag value shouldn't be changed in general, because + // `evaluate()` only removes the respective block if it is conditionally + // (inconsistent columns) necessary. + std::vector evaluate(std::vector& input, + size_t evaluationColumn, + bool stripIncompleteBlocks = true) const; // Format for debugging friend std::ostream& operator<<(std::ostream& str, @@ -93,9 +102,20 @@ class PrefilterExpression { } private: + // Performs the following conditional checks on the provided `BlockMetadata` + // values: + // (1) unqiueness of blocks + // (2) sorted (order) + // (3) consistent column values + // This function subsequently invokes the `evaluateImpl` method and + // checks the corresponding result for those conditions again. + // If a respective condition is violated, the function performing the checks + // will throw a `std::runtime_error`. + std::vector evaluateAndCheckImpl( + std::vector& input, size_t evaluationColumn) const; + virtual std::vector evaluateImpl( - const std::vector& input, - size_t evaluationColumn) const = 0; + std::vector& input, size_t evaluationColumn) const = 0; }; //______________________________________________________________________________ @@ -124,7 +144,7 @@ class RelationalExpression : public PrefilterExpression { private: std::vector evaluateImpl( - const std::vector& input, + std::vector& input, size_t evaluationColumn) const override; }; @@ -154,7 +174,7 @@ class LogicalExpression : public PrefilterExpression { private: std::vector evaluateImpl( - const std::vector& input, + std::vector& input, size_t evaluationColumn) const override; }; @@ -179,7 +199,7 @@ class NotExpression : public PrefilterExpression { private: std::vector evaluateImpl( - const std::vector& input, + std::vector& input, size_t evaluationColumn) const override; }; @@ -206,13 +226,6 @@ using OrExpression = prefilterExpressions::LogicalExpression< prefilterExpressions::LogicalOperator::OR>; namespace detail { -//______________________________________________________________________________ -// Helper function to perform the evaluation for the provided -// `PrefilterExpression` on the given `BlockMetadata` values, while taking into -// account possible incomplete blocks. -std::vector evaluatePrefilterExpressionOnMetadata( - const std::unique_ptr prefilterExpr, - const std::vector& blocks, const size_t evaluationColumn); //______________________________________________________________________________ // Pair containing a `PrefilterExpression` and its corresponding `Variable`. diff --git a/test/PrefilterExpressionIndexTest.cpp b/test/PrefilterExpressionIndexTest.cpp index 82d3386fb6..1a678ce2a8 100644 --- a/test/PrefilterExpressionIndexTest.cpp +++ b/test/PrefilterExpressionIndexTest.cpp @@ -86,8 +86,7 @@ class PrefilterExpressionOnMetadataTest : public ::testing::Test { // All blocks that contain mixed (ValueId) types over column 0, // or possibly incomplete ones. - const std::vector mixedAndPossiblyIncompleteBlocks = { - b1, b2, b4, b11, b18, b22, b24}; + const std::vector mixedBlocks = {b2, b4, b11, b18, b22, b24}; // Ordered and unique vector with BlockMetadata const std::vector blocks = { @@ -130,7 +129,8 @@ class PrefilterExpressionOnMetadataTest : public ::testing::Test { const std::vector& input, const std::string& expected, size_t evaluationColumn = 0) { - AD_EXPECT_THROW_WITH_MESSAGE(expr->evaluate(input, evaluationColumn), + std::vector testBlocks = input; + AD_EXPECT_THROW_WITH_MESSAGE(expr->evaluate(testBlocks, evaluationColumn), ::testing::HasSubstr(expected)); } @@ -147,15 +147,12 @@ class PrefilterExpressionOnMetadataTest : public ::testing::Test { // This is for convenience, we automatically insert all mixed and possibly // incomplete blocks which must be always returned. std::ranges::set_union( - expected, mixedAndPossiblyIncompleteBlocks, - std::back_inserter(expectedAdjusted), + expected, mixedBlocks, std::back_inserter(expectedAdjusted), [](const BlockMetadata& b1, const BlockMetadata& b2) { return b1.blockIndex_ < b2.blockIndex_; }); - ASSERT_EQ( - prefilterExpressions::detail::evaluatePrefilterExpressionOnMetadata( - std::move(expr), blocks, 0), - expectedAdjusted); + std::vector testBlocks = blocks; + ASSERT_EQ(expr->evaluate(testBlocks, 0), expectedAdjusted); } }; @@ -525,15 +522,15 @@ TEST_F(PrefilterExpressionOnMetadataTest, testInputConditionCheck) { //______________________________________________________________________________ // Check for correctness given only one BlockMetadata value is provided. -TEST_F(PrefilterExpressionOnMetadataTest, testWithOneBlockMetadataValue) { +TEST_F(PrefilterExpressionOnMetadataTest, testWithFewBlockMetadataValues) { auto expr = orExpr(eq(DoubleId(-6.25)), eq(IntId(0))); std::vector input = {b16}; EXPECT_EQ(expr->evaluate(input, 0), input); - EXPECT_EQ(expr->evaluate(input, 1), std::vector{}); - EXPECT_EQ(expr->evaluate(input, 2), std::vector{}); - EXPECT_EQ(prefilterExpressions::detail::evaluatePrefilterExpressionOnMetadata( - std::move(expr), input, 0), - input); + EXPECT_EQ(expr->evaluate(input, 1), input); + EXPECT_EQ(expr->evaluate(input, 2), input); + expr = eq(DoubleId(-6.25)); + input = {b15, b16, b17}; + EXPECT_EQ(expr->evaluate(input, 0), (std::vector{b15, b16})); } //______________________________________________________________________________ From 54c00ad79eeeae517bc480ac8dcba9066f7ce3df Mon Sep 17 00:00:00 2001 From: realHannes Date: Thu, 21 Nov 2024 11:59:29 +0100 Subject: [PATCH 57/81] first empty prefilter scan fix --- src/engine/IndexScan.cpp | 37 ++++++++++++++++++--------------- src/engine/IndexScan.h | 6 ++++++ src/engine/QueryExecutionTree.h | 2 +- 3 files changed, 27 insertions(+), 18 deletions(-) diff --git a/src/engine/IndexScan.cpp b/src/engine/IndexScan.cpp index 948504d63c..876cfe5b9c 100644 --- a/src/engine/IndexScan.cpp +++ b/src/engine/IndexScan.cpp @@ -145,27 +145,15 @@ vector IndexScan::resultSortedOn() const { std::optional> IndexScan::setPrefilterExprGetUpdatedQetPtr( std::vector prefilterVariablePairs) { - // The column index of the first sorted column. - const ColumnIndex sortedIdx = 0; if (numVariables_ < 1) { return std::nullopt; } - VariableToColumnMap varToColMap = computeVariableToColumnMap(); - // Search for a Variable key-value given the sortedIdx (index of first sorted - // column) in the VariableToColumnMap. - auto mapIt = std::ranges::find_if(varToColMap, [](const auto& keyValuePair) { - return keyValuePair.second.columnIndex_ == sortedIdx; - }); - if (mapIt != varToColMap.end()) { - // Check if the previously found Variable (key-value from varToColMap) - // matches with a pair. - auto itPairs = std::ranges::find_if( - prefilterVariablePairs, - [&mapIt](const auto& pair) { return pair.second == mapIt->first; }); - if (itPairs != prefilterVariablePairs.end()) { + auto map = getVariableToSortedIndexMap(); + for (auto& [prefilterExpr, variable] : prefilterVariablePairs) { + if (map.find(variable) != map.end()) { return makeCopyWithAddedPrefilters( - std::make_pair(itPairs->first->clone(), sortedIdx)); + std::make_pair(prefilterExpr->clone(), map[variable])); } } return std::nullopt; @@ -317,6 +305,21 @@ ScanSpecificationAsTripleComponent IndexScan::getScanSpecificationTc() const { graphsToFilter_}; } +// _____________________________________________________________________________ +ad_utility::HashMap +IndexScan::getVariableToSortedIndexMap() const { + ad_utility::HashMap map; + ColumnIndex idx = 0; + for (const TripleComponent* const ptr : getPermutedTriple()) { + if (!ptr->isVariable()) { + break; + } + map[ptr->getVariable()] = idx; + idx++; + } + return map; +} + // _____________________________________________________________________________ std::optional> IndexScan::getOptionalBlockSpan() const { @@ -340,7 +343,7 @@ IndexScan::getOptionalPrefilteredBlocks( auto& [prefilterExpr, columnIndex] = prefilter_.value(); return prefilterExpr->evaluate(blocks, columnIndex); } else { - return std::move(blocks); + return blocks; } } diff --git a/src/engine/IndexScan.h b/src/engine/IndexScan.h index 9a42c866f7..9a71aaaa03 100644 --- a/src/engine/IndexScan.h +++ b/src/engine/IndexScan.h @@ -6,6 +6,7 @@ #include #include "./Operation.h" +#include "util/HashMap.h" class SparqlTriple; class SparqlTripleSimple; @@ -166,6 +167,11 @@ class IndexScan final : public Operation { // Get the `IdTable` for this `IndexScan` in one piece. IdTable materializedIndexScan() const; + // Get the mapping for all `Variable` values mapped to sorted `ColumnIndex` + // values. + ad_utility::HashMap getVariableToSortedIndexMap() + const; + // Helper to retrieve the `CompressedBlockMetadata` span for this scan. std::optional> getOptionalBlockSpan() const; diff --git a/src/engine/QueryExecutionTree.h b/src/engine/QueryExecutionTree.h index 0f299c89c9..4fd1b24cdd 100644 --- a/src/engine/QueryExecutionTree.h +++ b/src/engine/QueryExecutionTree.h @@ -97,7 +97,7 @@ class QueryExecutionTree { // If the `rootOperation_` of this `QueryExecutionTree` is an `IndexScan` // operation, this method adds the suitable `PrefilterExpression` to the // `IndexScan` given a suitable `Variable` / `ColumnIndex` is public. - // If a `PrefilterExpression` was sucsessfully set, the updated + // If a `PrefilterExpression` was successfully set, the updated // `QueryExecutionTree` is returned. std::optional> setPrefilterExprGetUpdatedQetPtr( From 118ad87f7e220ec3a42bdbf47d8dca3ea1ea273d Mon Sep 17 00:00:00 2001 From: Johannes Kalmbach Date: Thu, 21 Nov 2024 13:40:44 +0100 Subject: [PATCH 58/81] Fix the bug that always disables prefilter expressions. Signed-off-by: Johannes Kalmbach --- src/engine/IndexScan.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/engine/IndexScan.cpp b/src/engine/IndexScan.cpp index 876cfe5b9c..129be26662 100644 --- a/src/engine/IndexScan.cpp +++ b/src/engine/IndexScan.cpp @@ -311,10 +311,10 @@ IndexScan::getVariableToSortedIndexMap() const { ad_utility::HashMap map; ColumnIndex idx = 0; for (const TripleComponent* const ptr : getPermutedTriple()) { - if (!ptr->isVariable()) { + if (ptr->isVariable()) { + map[ptr->getVariable()] = idx; break; } - map[ptr->getVariable()] = idx; idx++; } return map; From 61a15ce163f5412eff655b0dc8a4000dad61f273 Mon Sep 17 00:00:00 2001 From: realHannes Date: Thu, 21 Nov 2024 15:53:50 +0100 Subject: [PATCH 59/81] select only Variable for first column index + extend constructor --- src/engine/IndexScan.cpp | 40 +++++++++++++++++++++------------------- src/engine/IndexScan.h | 10 +++++----- 2 files changed, 26 insertions(+), 24 deletions(-) diff --git a/src/engine/IndexScan.cpp b/src/engine/IndexScan.cpp index 129be26662..dc5c0c96b7 100644 --- a/src/engine/IndexScan.cpp +++ b/src/engine/IndexScan.cpp @@ -64,7 +64,8 @@ IndexScan::IndexScan(QueryExecutionContext* qec, Permutation::Enum permutation, const TripleComponent& o, std::vector additionalColumns, std::vector additionalVariables, - Graphs graphsToFilter, PrefilterIndexPair prefilter) + const size_t numVariables, Graphs graphsToFilter, + PrefilterIndexPair prefilter) : Operation(qec), permutation_(permutation), subject_(s), @@ -72,8 +73,11 @@ IndexScan::IndexScan(QueryExecutionContext* qec, Permutation::Enum permutation, object_(o), graphsToFilter_(std::move(graphsToFilter)), prefilter_(std::move(prefilter)), + numVariables_(numVariables), additionalColumns_(std::move(additionalColumns)), - additionalVariables_(std::move(additionalVariables)) {} + additionalVariables_(std::move(additionalVariables)) { + std::tie(sizeEstimateIsExact_, sizeEstimate_) = computeSizeEstimate(); +} // _____________________________________________________________________________ string IndexScan::getCacheKeyImpl() const { @@ -145,15 +149,15 @@ vector IndexScan::resultSortedOn() const { std::optional> IndexScan::setPrefilterExprGetUpdatedQetPtr( std::vector prefilterVariablePairs) { - if (numVariables_ < 1) { + auto optSortedVarColIdxPair = getFirstSortedtVariableWithColumnIndex(); + if (!optSortedVarColIdxPair.has_value()) { return std::nullopt; } - - auto map = getVariableToSortedIndexMap(); + const auto& [sortedVar, colIdx] = optSortedVarColIdxPair.value(); for (auto& [prefilterExpr, variable] : prefilterVariablePairs) { - if (map.find(variable) != map.end()) { + if (sortedVar == variable) { return makeCopyWithAddedPrefilters( - std::make_pair(prefilterExpr->clone(), map[variable])); + std::make_pair(prefilterExpr->clone(), colIdx)); } } return std::nullopt; @@ -183,7 +187,7 @@ std::shared_ptr IndexScan::makeCopyWithAddedPrefilters( PrefilterIndexPair prefilter) const { return ad_utility::makeExecutionTree( getExecutionContext(), permutation_, subject_, predicate_, object_, - additionalColumns_, additionalVariables_, graphsToFilter_, + additionalColumns_, additionalVariables_, numVariables_, graphsToFilter_, std::move(prefilter)); } @@ -306,18 +310,16 @@ ScanSpecificationAsTripleComponent IndexScan::getScanSpecificationTc() const { } // _____________________________________________________________________________ -ad_utility::HashMap -IndexScan::getVariableToSortedIndexMap() const { - ad_utility::HashMap map; - ColumnIndex idx = 0; - for (const TripleComponent* const ptr : getPermutedTriple()) { - if (ptr->isVariable()) { - map[ptr->getVariable()] = idx; - break; - } - idx++; +std::optional> +IndexScan::getFirstSortedtVariableWithColumnIndex() const { + if (numVariables_ < 1) { + return std::nullopt; } - return map; + const auto& permutedTriple = getPermutedTriple(); + size_t colIdx = 3 - numVariables_; + const auto& tripleComp = permutedTriple.at(colIdx); + AD_CORRECTNESS_CHECK(tripleComp->isVariable()); + return std::make_pair(tripleComp->getVariable(), colIdx); } // _____________________________________________________________________________ diff --git a/src/engine/IndexScan.h b/src/engine/IndexScan.h index 9a71aaaa03..5c04ab472c 100644 --- a/src/engine/IndexScan.h +++ b/src/engine/IndexScan.h @@ -49,7 +49,8 @@ class IndexScan final : public Operation { const TripleComponent& s, const TripleComponent& p, const TripleComponent& o, std::vector additionalColumns, - std::vector additionalVariables, Graphs graphsToFilter, + std::vector additionalVariables, + const size_t numVariables, Graphs graphsToFilter, PrefilterIndexPair prefilter); ~IndexScan() override = default; @@ -167,10 +168,9 @@ class IndexScan final : public Operation { // Get the `IdTable` for this `IndexScan` in one piece. IdTable materializedIndexScan() const; - // Get the mapping for all `Variable` values mapped to sorted `ColumnIndex` - // values. - ad_utility::HashMap getVariableToSortedIndexMap() - const; + // Get the first sorted 'Variable' with corresponding `ColumnIndex`. + std::optional> + getFirstSortedtVariableWithColumnIndex() const; // Helper to retrieve the `CompressedBlockMetadata` span for this scan. std::optional> getOptionalBlockSpan() From 6ef031be7549a95e384849df29146c8e20ac6d80 Mon Sep 17 00:00:00 2001 From: realHannes Date: Thu, 21 Nov 2024 16:11:56 +0100 Subject: [PATCH 60/81] set multiplicities in IndexScan constructor --- src/engine/IndexScan.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/engine/IndexScan.cpp b/src/engine/IndexScan.cpp index dc5c0c96b7..d7a1c67a15 100644 --- a/src/engine/IndexScan.cpp +++ b/src/engine/IndexScan.cpp @@ -77,6 +77,7 @@ IndexScan::IndexScan(QueryExecutionContext* qec, Permutation::Enum permutation, additionalColumns_(std::move(additionalColumns)), additionalVariables_(std::move(additionalVariables)) { std::tie(sizeEstimateIsExact_, sizeEstimate_) = computeSizeEstimate(); + determineMultiplicities(); } // _____________________________________________________________________________ @@ -343,6 +344,7 @@ IndexScan::getOptionalPrefilteredBlocks( if (prefilter_.has_value()) { // Apply the prefilter on given blocks. auto& [prefilterExpr, columnIndex] = prefilter_.value(); + std::cout << "Use prefiltering" << std::endl; return prefilterExpr->evaluate(blocks, columnIndex); } else { return blocks; From bef821104e89a334c3826325072536c0c3ad56f9 Mon Sep 17 00:00:00 2001 From: realHannes Date: Sat, 23 Nov 2024 19:16:51 +0100 Subject: [PATCH 61/81] changes for review (1) --- src/engine/IndexScan.cpp | 125 +++++++++--------- src/engine/IndexScan.h | 33 +++-- .../PrefilterExpressionIndex.cpp | 8 +- .../PrefilterExpressionIndex.h | 30 ++--- src/index/Permutation.cpp | 10 +- src/index/Permutation.h | 8 +- 6 files changed, 120 insertions(+), 94 deletions(-) diff --git a/src/engine/IndexScan.cpp b/src/engine/IndexScan.cpp index d7a1c67a15..0278a37212 100644 --- a/src/engine/IndexScan.cpp +++ b/src/engine/IndexScan.cpp @@ -15,6 +15,17 @@ using std::string; +// _____________________________________________________________________________ +// Return the number of `Variables` given the `TripleComponent` values for +// `subject_`, `prediate` and `object`. +static size_t getNumberOfVariables(const TripleComponent& subject, + const TripleComponent& predicate, + const TripleComponent& object) { + return static_cast(subject.isVariable()) + + static_cast(predicate.isVariable()) + + static_cast(object.isVariable()); +} + // _____________________________________________________________________________ IndexScan::IndexScan(QueryExecutionContext* qec, Permutation::Enum permutation, const SparqlTripleSimple& triple, Graphs graphsToFilter, @@ -26,9 +37,7 @@ IndexScan::IndexScan(QueryExecutionContext* qec, Permutation::Enum permutation, object_(triple.o_), graphsToFilter_{std::move(graphsToFilter)}, prefilter_{std::move(prefilter)}, - numVariables_(static_cast(subject_.isVariable()) + - static_cast(predicate_.isVariable()) + - static_cast(object_.isVariable())) { + numVariables_(getNumberOfVariables(subject_, predicate_, object_)) { // We previously had `nullptr`s here in unit tests. This is no longer // necessary nor allowed. AD_CONTRACT_CHECK(qec != nullptr); @@ -64,8 +73,7 @@ IndexScan::IndexScan(QueryExecutionContext* qec, Permutation::Enum permutation, const TripleComponent& o, std::vector additionalColumns, std::vector additionalVariables, - const size_t numVariables, Graphs graphsToFilter, - PrefilterIndexPair prefilter) + Graphs graphsToFilter, PrefilterIndexPair prefilter) : Operation(qec), permutation_(permutation), subject_(s), @@ -73,7 +81,7 @@ IndexScan::IndexScan(QueryExecutionContext* qec, Permutation::Enum permutation, object_(o), graphsToFilter_(std::move(graphsToFilter)), prefilter_(std::move(prefilter)), - numVariables_(numVariables), + numVariables_(getNumberOfVariables(subject_, predicate_, object_)), additionalColumns_(std::move(additionalColumns)), additionalVariables_(std::move(additionalVariables)) { std::tie(sizeEstimateIsExact_, sizeEstimate_) = computeSizeEstimate(); @@ -150,16 +158,17 @@ vector IndexScan::resultSortedOn() const { std::optional> IndexScan::setPrefilterExprGetUpdatedQetPtr( std::vector prefilterVariablePairs) { - auto optSortedVarColIdxPair = getFirstSortedtVariableWithColumnIndex(); + auto optSortedVarColIdxPair = + getSortedVariableAndMetadataColumnIndexForPrefiltering(); if (!optSortedVarColIdxPair.has_value()) { return std::nullopt; } const auto& [sortedVar, colIdx] = optSortedVarColIdxPair.value(); - for (auto& [prefilterExpr, variable] : prefilterVariablePairs) { - if (sortedVar == variable) { - return makeCopyWithAddedPrefilters( - std::make_pair(prefilterExpr->clone(), colIdx)); - } + auto it = + std::ranges::find(prefilterVariablePairs, sortedVar, ad_utility::second); + if (it != prefilterVariablePairs.end()) { + return makeCopyWithAddedPrefilters( + std::make_pair(it->first->clone(), colIdx)); } return std::nullopt; } @@ -188,17 +197,19 @@ std::shared_ptr IndexScan::makeCopyWithAddedPrefilters( PrefilterIndexPair prefilter) const { return ad_utility::makeExecutionTree( getExecutionContext(), permutation_, subject_, predicate_, object_, - additionalColumns_, additionalVariables_, numVariables_, graphsToFilter_, + additionalColumns_, additionalVariables_, graphsToFilter_, std::move(prefilter)); } // _____________________________________________________________________________ Result::Generator IndexScan::chunkedIndexScan() const { - auto optBlockSpan = getOptionalBlockSpan(); + auto optBlockSpan = getBlockMetadata(); if (!optBlockSpan.has_value()) { co_return; } const auto& blockSpan = optBlockSpan.value(); + // Note: Given a `PrefilterIndexPair` is available, the corresponding + // prefiltering will be applied in `getLazyScan`. for (IdTable& idTable : getLazyScan({blockSpan.begin(), blockSpan.end()})) { co_yield {std::move(idTable), LocalVocab{}}; } @@ -206,24 +217,10 @@ Result::Generator IndexScan::chunkedIndexScan() const { // _____________________________________________________________________________ IdTable IndexScan::materializedIndexScan() const { - // Get the blocks. - auto optBlockSpan = getOptionalBlockSpan(); - std::optional> optFilteredBlocks = {}; - if (optBlockSpan.has_value()) { - const auto& blockSpan = optBlockSpan.value(); - optFilteredBlocks = - getOptionalPrefilteredBlocks({blockSpan.begin(), blockSpan.end()}); - } - // Create the IdTable and fill it with content by performing scan(). - using enum Permutation::Enum; - IdTable idTable{getExecutionContext()->getAllocator()}; - idTable.setNumColumns(numVariables_); - const auto& index = _executionContext->getIndex(); - idTable = index.getImpl() - .getPermutation(permutation_) - .scan(getScanSpecification(), additionalColumns(), - cancellationHandle_, locatedTriplesSnapshot(), getLimit(), - optFilteredBlocks); + IdTable idTable = getScanPermutation().scan( + getScanSpecification(), additionalColumns(), cancellationHandle_, + locatedTriplesSnapshot(), getLimit(), + getBlockMetadataOptionallyPrefiltered()); AD_CORRECTNESS_CHECK(idTable.numColumns() == getResultWidth()); LOG(DEBUG) << "IndexScan result computation done.\n"; checkCancellation(); @@ -239,22 +236,26 @@ ProtoResult IndexScan::computeResult(bool requestLaziness) { return {materializedIndexScan(), getResultSortedOn(), LocalVocab{}}; } +// _____________________________________________________________________________ +const Permutation& IndexScan::getScanPermutation() const { + return getIndex().getImpl().getPermutation(permutation_); +} + // _____________________________________________________________________________ std::pair IndexScan::computeSizeEstimate() const { AD_CORRECTNESS_CHECK(_executionContext); - auto [lower, upper] = getIndex() - .getImpl() - .getPermutation(permutation()) - .getSizeEstimateForScan(getScanSpecification(), - locatedTriplesSnapshot()); + auto [lower, upper] = getScanPermutation().getSizeEstimateForScan( + getScanSpecification(), locatedTriplesSnapshot(), + getBlockMetadataOptionallyPrefiltered()); return {lower == upper, std::midpoint(lower, upper)}; } // _____________________________________________________________________________ size_t IndexScan::getExactSize() const { AD_CORRECTNESS_CHECK(_executionContext); - return getIndex().getResultSizeOfScan(getScanSpecification(), permutation_, - locatedTriplesSnapshot()); + return getScanPermutation().getResultSizeOfScan( + getScanSpecification(), locatedTriplesSnapshot(), + getBlockMetadataOptionallyPrefiltered()); } // _____________________________________________________________________________ @@ -312,7 +313,7 @@ ScanSpecificationAsTripleComponent IndexScan::getScanSpecificationTc() const { // _____________________________________________________________________________ std::optional> -IndexScan::getFirstSortedtVariableWithColumnIndex() const { +IndexScan::getSortedVariableAndMetadataColumnIndexForPrefiltering() const { if (numVariables_ < 1) { return std::nullopt; } @@ -325,7 +326,7 @@ IndexScan::getFirstSortedtVariableWithColumnIndex() const { // _____________________________________________________________________________ std::optional> -IndexScan::getOptionalBlockSpan() const { +IndexScan::getBlockMetadata() const { auto metadata = getMetadataForScan(); if (metadata.has_value()) { return CompressedRelationReader::getBlocksFromMetadata(metadata.value()); @@ -335,44 +336,48 @@ IndexScan::getOptionalBlockSpan() const { // _____________________________________________________________________________ std::optional> -IndexScan::getOptionalPrefilteredBlocks( - std::vector blocks) const { - if (!getLimit().isUnconstrained()) { - return std::nullopt; +IndexScan::getBlockMetadataOptionallyPrefiltered() const { + auto optBlockSpan = getBlockMetadata(); + std::optional> optBlocks = std::nullopt; + if (optBlockSpan.has_value()) { + const auto& blockSpan = optBlockSpan.value(); + optBlocks = {blockSpan.begin(), blockSpan.end()}; + applyPefilterIfPossible(optBlocks.value()); } + return optBlocks; +} - if (prefilter_.has_value()) { +// _____________________________________________________________________________ +void IndexScan::applyPefilterIfPossible( + std::vector& blocks) const { + if (prefilter_.has_value() && getLimit().isUnconstrained()) { // Apply the prefilter on given blocks. auto& [prefilterExpr, columnIndex] = prefilter_.value(); - std::cout << "Use prefiltering" << std::endl; - return prefilterExpr->evaluate(blocks, columnIndex); - } else { - return blocks; + blocks = prefilterExpr->evaluate(blocks, columnIndex); } } // _____________________________________________________________________________ Permutation::IdTableGenerator IndexScan::getLazyScan( std::vector blocks) const { + applyPefilterIfPossible(blocks); // If there is a LIMIT or OFFSET clause that constrains the scan // (which can happen with an explicit subquery), we cannot use the prefiltered // blocks, as we currently have no mechanism to include limits and offsets // into the prefiltering (`std::nullopt` means `scan all blocks`). - auto actualBlocks = getOptionalPrefilteredBlocks(std::move(blocks)); - return getIndex() - .getImpl() - .getPermutation(permutation()) - .lazyScan(getScanSpecification(), std::move(actualBlocks), - additionalColumns(), cancellationHandle_, - locatedTriplesSnapshot(), getLimit()); + auto filteredBlocks = getLimit().isUnconstrained() + ? std::optional(std::move(blocks)) + : std::nullopt; + return getScanPermutation().lazyScan(getScanSpecification(), filteredBlocks, + additionalColumns(), cancellationHandle_, + locatedTriplesSnapshot(), getLimit()); }; // _____________________________________________________________________________ std::optional IndexScan::getMetadataForScan() const { - const auto& index = getExecutionContext()->getIndex().getImpl(); - return index.getPermutation(permutation()) - .getMetadataAndBlocks(getScanSpecification(), locatedTriplesSnapshot()); + return getScanPermutation().getMetadataAndBlocks(getScanSpecification(), + locatedTriplesSnapshot()); }; // _____________________________________________________________________________ diff --git a/src/engine/IndexScan.h b/src/engine/IndexScan.h index 5c04ab472c..3f482ac9b6 100644 --- a/src/engine/IndexScan.h +++ b/src/engine/IndexScan.h @@ -49,8 +49,7 @@ class IndexScan final : public Operation { const TripleComponent& s, const TripleComponent& p, const TripleComponent& o, std::vector additionalColumns, - std::vector additionalVariables, - const size_t numVariables, Graphs graphsToFilter, + std::vector additionalVariables, Graphs graphsToFilter, PrefilterIndexPair prefilter); ~IndexScan() override = default; @@ -150,6 +149,10 @@ class IndexScan final : public Operation { vector getChildren() override { return {}; } + // Retrieve the `Permutation` entity for the `Permutation::Enum` value of this + // `IndexScan`. + const Permutation& getScanPermutation() const; + // Compute the size estimate of the index scan, taking delta triples (from // the `queryExecutionContext_`) into account. The `bool` is true iff the // estimate is exact. If not, the estimate is the mean of the lower and upper @@ -160,6 +163,10 @@ class IndexScan final : public Operation { VariableToColumnMap computeVariableToColumnMap() const override; + // Return an updated QueryExecutionTree containing the new IndexScan that is a + // copy of this (`IndexScan`), but with added corresponding + // `PrefilterExpression` (`PrefilterIndexPair`). This method is called in the + // implementation part of `setPrefilterExprGetUpdatedQetPtr()`. std::shared_ptr makeCopyWithAddedPrefilters( PrefilterIndexPair prefilter) const; @@ -168,20 +175,28 @@ class IndexScan final : public Operation { // Get the `IdTable` for this `IndexScan` in one piece. IdTable materializedIndexScan() const; - // Get the first sorted 'Variable' with corresponding `ColumnIndex`. + // Returns the first sorted 'Variable' with corresponding `ColumnIndex`. If + // `numVariables_` is 0, `std::nullopt` is returned. + // The returned `ColumnIndex` corresponds to the `CompressedBlockMetadata` + // blocks, NOT to a `ColumnIndex` of the resulting `IdTable`. std::optional> - getFirstSortedtVariableWithColumnIndex() const; + getSortedVariableAndMetadataColumnIndexForPrefiltering() const; - // Helper to retrieve the `CompressedBlockMetadata` span for this scan. - std::optional> getOptionalBlockSpan() + // Retrieve all the relevant `CompressedBlockMetadata` for this scan without + // applying any additional pre-filter procedure. + std::optional> getBlockMetadata() const; + // This method retrieves all relevant `CompressedBlockMetadata` and performs + // the pre-filtering procedure given a `PrefilterIndexPair` is available. + std::optional> + getBlockMetadataOptionallyPrefiltered() const; + // If `isUnconstrained()` yields true, return the blocks as given or the // prefiltered blocks (if `prefilter_` has value). If `isUnconstrained()` is // false, return `std::nullopt`. - std::optional> - getOptionalPrefilteredBlocks( - std::vector blocks) const; + void applyPefilterIfPossible( + std::vector& blocks) const; // Helper functions for the public `getLazyScanFor...` methods and // `chunkedIndexScan` (see above). diff --git a/src/engine/sparqlExpressions/PrefilterExpressionIndex.cpp b/src/engine/sparqlExpressions/PrefilterExpressionIndex.cpp index f936d6fc71..9ceaa2e01c 100644 --- a/src/engine/sparqlExpressions/PrefilterExpressionIndex.cpp +++ b/src/engine/sparqlExpressions/PrefilterExpressionIndex.cpp @@ -188,14 +188,14 @@ std::vector PrefilterExpression::evaluate( lastBlock = input.back(); input.pop_back(); } - auto result = evaluateAndCheckImpl(input, evaluationColumn); + input = evaluateAndCheckImpl(input, evaluationColumn); if (firstBlock.has_value()) { - result.insert(result.begin(), firstBlock.value()); + input.insert(input.begin(), firstBlock.value()); } if (lastBlock.has_value()) { - result.push_back(lastBlock.value()); + input.push_back(lastBlock.value()); } - return result; + return input; }; // _____________________________________________________________________________ diff --git a/src/engine/sparqlExpressions/PrefilterExpressionIndex.h b/src/engine/sparqlExpressions/PrefilterExpressionIndex.h index ea383d6df3..5656014aab 100644 --- a/src/engine/sparqlExpressions/PrefilterExpressionIndex.h +++ b/src/engine/sparqlExpressions/PrefilterExpressionIndex.h @@ -61,12 +61,12 @@ class PrefilterExpression { // Format content for debugging. virtual std::string asString(size_t depth) const = 0; - // Needed for implementing the `NotExpression`. This method is required, - // because we logically operate on `BlockMetadata` values which define ranges - // given the `ValueIds` from last and first triple. - // E.g. the `BlockMetadata` that defines the range [IntId(0),... IntId(5)], - // should be considered relevant for the expression `?x >= IntId(3)`, but also - // for expression `!(?x >= IntId(3))`. Thus we can't retrieve the negation by + // This method is required for implementing the `NotExpression`. This method + // is required, because we logically operate on `BlockMetadata` values which + // define ranges given the `ValueIds` from last and first triple. E.g. the + // `BlockMetadata` that defines the range [IntId(0),... IntId(5)], should be + // considered relevant for the expression `?x >= IntId(3)`, but also for + // expression `!(?x >= IntId(3))`. Thus we can't retrieve the negation by // simply taking the complementing set of `BlockMetadata`, instead we // retrieve it by directly negating/complementing the child expression itself. // Every derived class can return it's respective logical complement @@ -80,16 +80,16 @@ class PrefilterExpression { // take a look at the actual implementation for derived classes. virtual std::unique_ptr logicalComplement() const = 0; - // Expects that the provided `BlockMetadata` vector adheres to the following - // conditions: + // It's expected that the provided `BlockMetadata` vector adheres to the + // following conditions: // (1) unqiueness of blocks // (2) sorted (order) - // (3) consistent column values - // To indicate that the possibly incomplete first and last block should - // be handled appropriately, the `stripIncompleteBlocks` flag is set to - // `true`. The flag value shouldn't be changed in general, because - // `evaluate()` only removes the respective block if it is conditionally - // (inconsistent columns) necessary. + // (3) Constant values for all columns `< evaluationColumn` + // To indicate that the possibly incomplete first and last block should be + // handled appropriately, the `stripIncompleteBlocks` flag is set to `true`. + // The flag value shouldn't be changed in general, because `evaluate()` only + // removes the respective block if it is conditionally (inconsistent columns) + // necessary. std::vector evaluate(std::vector& input, size_t evaluationColumn, bool stripIncompleteBlocks = true) const; @@ -106,7 +106,7 @@ class PrefilterExpression { // values: // (1) unqiueness of blocks // (2) sorted (order) - // (3) consistent column values + // (3) Constant values for all columns `< evaluationColumn` // This function subsequently invokes the `evaluateImpl` method and // checks the corresponding result for those conditions again. // If a respective condition is violated, the function performing the checks diff --git a/src/index/Permutation.cpp b/src/index/Permutation.cpp index 01994cf7cc..3e5d9c990e 100644 --- a/src/index/Permutation.cpp +++ b/src/index/Permutation.cpp @@ -74,20 +74,22 @@ IdTable Permutation::scan( // _____________________________________________________________________ size_t Permutation::getResultSizeOfScan( const ScanSpecification& scanSpec, - const LocatedTriplesSnapshot& locatedTriplesSnapshot) const { + const LocatedTriplesSnapshot& locatedTriplesSnapshot, + std::optional> blocks) const { const auto& p = getActualPermutation(scanSpec); return p.reader().getResultSizeOfScan( - scanSpec, p.meta_.blockData(), + scanSpec, blocks.has_value() ? blocks.value() : p.meta_.blockData(), getLocatedTriplesForPermutation(locatedTriplesSnapshot)); } // _____________________________________________________________________ std::pair Permutation::getSizeEstimateForScan( const ScanSpecification& scanSpec, - const LocatedTriplesSnapshot& locatedTriplesSnapshot) const { + const LocatedTriplesSnapshot& locatedTriplesSnapshot, + std::optional> blocks) const { const auto& p = getActualPermutation(scanSpec); return p.reader().getSizeEstimateForScan( - scanSpec, p.meta_.blockData(), + scanSpec, blocks.has_value() ? blocks.value() : p.meta_.blockData(), getLocatedTriplesForPermutation(locatedTriplesSnapshot)); } diff --git a/src/index/Permutation.h b/src/index/Permutation.h index 5b49ae0b80..ff72c75016 100644 --- a/src/index/Permutation.h +++ b/src/index/Permutation.h @@ -124,14 +124,18 @@ class Permutation { // within the respective blocks. size_t getResultSizeOfScan( const ScanSpecification& scanSpec, - const LocatedTriplesSnapshot& locatedTriplesSnapshot) const; + const LocatedTriplesSnapshot& locatedTriplesSnapshot, + std::optional> blocks = + std::nullopt) const; // Get a lower and upper bound for the size of the result of a scan, taking // into account the given `deltaTriples`. For this call, it is enough that // each delta triple know to which block it belongs. std::pair getSizeEstimateForScan( const ScanSpecification& scanSpec, - const LocatedTriplesSnapshot& locatedTriplesSnapshot) const; + const LocatedTriplesSnapshot& locatedTriplesSnapshot, + std::optional> blocks = + std::nullopt) const; // _______________________________________________________ void setKbName(const string& name) { meta_.setName(name); } From 80d54df4fd07737f3b57b89bcb114a10782c473f Mon Sep 17 00:00:00 2001 From: realHannes Date: Sun, 24 Nov 2024 19:35:01 +0100 Subject: [PATCH 62/81] changes for review (2) --- src/engine/Filter.cpp | 8 +- src/engine/Filter.h | 12 +- src/engine/IndexScan.cpp | 2 +- src/engine/IndexScan.h | 10 +- src/engine/Operation.h | 22 +- src/engine/QueryExecutionTree.cpp | 22 +- src/engine/QueryExecutionTree.h | 11 +- .../PrefilterExpressionIndex.cpp | 58 ++-- .../PrefilterExpressionIndex.h | 12 +- test/PrefilterExpressionIndexTest.cpp | 265 +++++++++++------- 10 files changed, 242 insertions(+), 180 deletions(-) diff --git a/src/engine/Filter.cpp b/src/engine/Filter.cpp index 85773254e5..0bcbd09962 100644 --- a/src/engine/Filter.cpp +++ b/src/engine/Filter.cpp @@ -28,7 +28,7 @@ Filter::Filter(QueryExecutionContext* qec, : Operation(qec), _subtree(std::move(subtree)), _expression{std::move(expression)} { - setPrefilterExpressionForDirectIndexScanChild(); + setPrefilterExpressionForChildren(); } // _____________________________________________________________________________ @@ -45,11 +45,11 @@ string Filter::getDescriptor() const { } //______________________________________________________________________________ -void Filter::setPrefilterExpressionForDirectIndexScanChild() { +void Filter::setPrefilterExpressionForChildren() { std::vector prefilterPairs = _expression.getPrefilterExpressionForMetadata(); - auto optNewSubTree = - _subtree->setPrefilterExprGetUpdatedQetPtr(std::move(prefilterPairs)); + auto optNewSubTree = _subtree->setPrefilterGetUpdatedQueryExecutionTree( + std::move(prefilterPairs)); if (optNewSubTree.has_value()) { _subtree = std::move(optNewSubTree.value()); } diff --git a/src/engine/Filter.h b/src/engine/Filter.h index 4dd1933fc5..9a845ab18d 100644 --- a/src/engine/Filter.h +++ b/src/engine/Filter.h @@ -59,11 +59,13 @@ class Filter : public Operation { return _subtree->getVariableColumns(); } - // This method is directly called by the constructor. - // It sets the appropriate `` pair for each - // `IndexScan` child by invoking `setPrefilterExpression` on all descendants - // in the `QueryExecutionTree`. - void setPrefilterExpressionForDirectIndexScanChild(); + // The method is directly invoked with the construction of this `Filter` + // object. Its implementation retrieves pairs + // from the corresponding `SparqlExpression` and calls + // `QueryExecutionTree::setPrefilterGetUpdatedQueryExecutionTree()` on those + // pair values. If necessary the `QueryExecutionTree` for this entity will be + // updated. + void setPrefilterExpressionForChildren(); ProtoResult computeResult(bool requestLaziness) override; diff --git a/src/engine/IndexScan.cpp b/src/engine/IndexScan.cpp index 0278a37212..ab567c08c6 100644 --- a/src/engine/IndexScan.cpp +++ b/src/engine/IndexScan.cpp @@ -156,7 +156,7 @@ vector IndexScan::resultSortedOn() const { // _____________________________________________________________________________ std::optional> -IndexScan::setPrefilterExprGetUpdatedQetPtr( +IndexScan::setPrefilterGetUpdatedQueryExecutionTree( std::vector prefilterVariablePairs) { auto optSortedVarColIdxPair = getSortedVariableAndMetadataColumnIndexForPrefiltering(); diff --git a/src/engine/IndexScan.h b/src/engine/IndexScan.h index 3f482ac9b6..15195a141a 100644 --- a/src/engine/IndexScan.h +++ b/src/engine/IndexScan.h @@ -75,7 +75,7 @@ class IndexScan final : public Operation { // Set `PrefilterExpression`s and return updated `QueryExecutionTree` pointer // if necessary. std::optional> - setPrefilterExprGetUpdatedQetPtr( + setPrefilterGetUpdatedQueryExecutionTree( std::vector prefilterVariablePairs) override; size_t numVariables() const { return numVariables_; } @@ -163,10 +163,10 @@ class IndexScan final : public Operation { VariableToColumnMap computeVariableToColumnMap() const override; - // Return an updated QueryExecutionTree containing the new IndexScan that is a - // copy of this (`IndexScan`), but with added corresponding + // Return an updated QueryExecutionTree containing the new IndexScan which is + // a copy of this (`IndexScan`), but with added corresponding // `PrefilterExpression` (`PrefilterIndexPair`). This method is called in the - // implementation part of `setPrefilterExprGetUpdatedQetPtr()`. + // implementation part of `setPrefilterGetUpdatedQueryExecutionTree()`. std::shared_ptr makeCopyWithAddedPrefilters( PrefilterIndexPair prefilter) const; @@ -178,7 +178,7 @@ class IndexScan final : public Operation { // Returns the first sorted 'Variable' with corresponding `ColumnIndex`. If // `numVariables_` is 0, `std::nullopt` is returned. // The returned `ColumnIndex` corresponds to the `CompressedBlockMetadata` - // blocks, NOT to a `ColumnIndex` of the resulting `IdTable`. + // blocks, NOT to a column of the resulting `IdTable`. std::optional> getSortedVariableAndMetadataColumnIndexForPrefiltering() const; diff --git a/src/engine/Operation.h b/src/engine/Operation.h index 4f91908e6c..50da76951b 100644 --- a/src/engine/Operation.h +++ b/src/engine/Operation.h @@ -83,20 +83,16 @@ class Operation { return _executionContext->locatedTriplesSnapshot(); } - // Set `PrefilterExpression`s (for `IndexScan`). - // The prefiltering procedure is implemented by applying the - // PrefilterExpressions directly on the CompressedBlockMetadata. - // This is currently done while performing the result computation for - // IndexScan. - // (1) Overwrite this method for the derived `IndexScan` class. Given - // the resulting changes w.r.t. `IndexScan`, we also have to return an updated - // `QueryExecutionTree` pointer. - // (2) The default method for all other derived classes is implemented here, - // no PrefilterExpressions need to be set. Given that no changes occur for an - // `Operation` object here, return std::nullopt to indicate that nothing has - // changed. + // Get an updated `QueryExecutionTree` that applies as many of the given + // `PrefilterExpression`s over `IndexScan` as possible. Returns `nullopt` + // if no `PrefilterExpression` is applicable and thus the `QueryExecutionTree` + // is not changed. + // Note: The default implementation always returns `nullopt` while this + // function is currently only overridden for `IndexScan`. In the future also + // other operations could pass on the `PrefilterExpressions` to the + // `IndexScan` in their subtree. virtual std::optional> - setPrefilterExprGetUpdatedQetPtr( + setPrefilterGetUpdatedQueryExecutionTree( [[maybe_unused]] std::vector prefilterPairs) { return std::nullopt; }; diff --git a/src/engine/QueryExecutionTree.cpp b/src/engine/QueryExecutionTree.cpp index f432d719d2..d8723f6107 100644 --- a/src/engine/QueryExecutionTree.cpp +++ b/src/engine/QueryExecutionTree.cpp @@ -106,22 +106,20 @@ size_t QueryExecutionTree::getSizeEstimate() { //_____________________________________________________________________________ std::optional> -QueryExecutionTree::setPrefilterExprGetUpdatedQetPtr( +QueryExecutionTree::setPrefilterGetUpdatedQueryExecutionTree( std::vector prefilterPairs) const { AD_CONTRACT_CHECK(rootOperation_); - std::vector relevantPairs; - relevantPairs.reserve(prefilterPairs.size()); VariableToColumnMap varToColMap = getVariableColumns(); - // Add all the PrefilterVariable values whose Variable value is - // contained in the VariableToColumnMap. This is done to avoid that certain - // subqueries filter out too much. - for (auto& prefilterPair : prefilterPairs) { - if (varToColMap.find(prefilterPair.second) != varToColMap.end()) { - relevantPairs.emplace_back(std::move(prefilterPair)); - } + std::erase_if(prefilterPairs, [&varToColMap](const auto& pair) { + return !varToColMap.contains(pair.second); + }); + + if (prefilterPairs.empty()) { + return std::nullopt; + } else { + return rootOperation_->setPrefilterGetUpdatedQueryExecutionTree( + std::move(prefilterPairs)); } - return rootOperation_->setPrefilterExprGetUpdatedQetPtr( - std::move(relevantPairs)); } // _____________________________________________________________________________ diff --git a/src/engine/QueryExecutionTree.h b/src/engine/QueryExecutionTree.h index 4fd1b24cdd..0eac785f16 100644 --- a/src/engine/QueryExecutionTree.h +++ b/src/engine/QueryExecutionTree.h @@ -94,13 +94,12 @@ class QueryExecutionTree { return rootOperation_->getMultiplicity(col); } - // If the `rootOperation_` of this `QueryExecutionTree` is an `IndexScan` - // operation, this method adds the suitable `PrefilterExpression` to the - // `IndexScan` given a suitable `Variable` / `ColumnIndex` is public. - // If a `PrefilterExpression` was successfully set, the updated - // `QueryExecutionTree` is returned. + // The implementation of this method calls + // `Operation::setPrefilterGetUpdatedQueryExecutionTree()` for the root + // operation. Only `` pairs are passed, where + // the corresponding `Variable` is visible in the `VariableToColumnMap`. std::optional> - setPrefilterExprGetUpdatedQetPtr( + setPrefilterGetUpdatedQueryExecutionTree( std::vector prefilterPairs) const; size_t getDistinctEstimate(size_t col) const { diff --git a/src/engine/sparqlExpressions/PrefilterExpressionIndex.cpp b/src/engine/sparqlExpressions/PrefilterExpressionIndex.cpp index 9ceaa2e01c..2bcd66f19b 100644 --- a/src/engine/sparqlExpressions/PrefilterExpressionIndex.cpp +++ b/src/engine/sparqlExpressions/PrefilterExpressionIndex.cpp @@ -51,9 +51,17 @@ static auto getMaskedTriple(const BlockMetadata::PermutedTriple& triple, } }; +//______________________________________________________________________________ +// Ckeck for constant values in all columns `< evaluationColumn` +static bool checkBlockIsInconsistent(const BlockMetadata& block, + size_t evaluationColumn) { + return getMaskedTriple(block.firstTriple_, evaluationColumn) != + getMaskedTriple(block.lastTriple_, evaluationColumn); +} + //______________________________________________________________________________ // Check required conditions. -static void checkEvalRequirements(const std::vector& input, +static void checkEvalRequirements(std::span input, size_t evaluationColumn) { const auto throwRuntimeError = [](const std::string& errorMessage) { throw std::runtime_error(errorMessage); @@ -88,12 +96,10 @@ static void checkEvalRequirements(const std::vector& input, // `b1` and `b2` up to the evaluation are inconsistent. const auto checkColumnConsistency = [evaluationColumn](const BlockMetadata& b1, const BlockMetadata& b2) { - const auto& b1Last = getMaskedTriple(b1.lastTriple_, evaluationColumn); - const auto& b2First = - getMaskedTriple(b2.firstTriple_, evaluationColumn); - return getMaskedTriple(b1.firstTriple_, evaluationColumn) != b1Last || - b1Last != b2First || - b2First != getMaskedTriple(b2.lastTriple_, evaluationColumn); + return checkBlockIsInconsistent(b1, evaluationColumn) || + getMaskedTriple(b1.lastTriple_, evaluationColumn) != + getMaskedTriple(b2.firstTriple_, evaluationColumn) || + checkBlockIsInconsistent(b2, evaluationColumn); }; if (auto it = std::ranges::adjacent_find(input, checkColumnConsistency); it != input.end()) { @@ -162,45 +168,39 @@ static std::string getLogicalOpStr(const LogicalOperator logOp) { // SECTION PREFILTER EXPRESSION (BASE CLASS) //______________________________________________________________________________ std::vector PrefilterExpression::evaluate( - std::vector& input, size_t evaluationColumn, + std::span input, size_t evaluationColumn, bool stripIncompleteBlocks) const { if (!stripIncompleteBlocks) { return evaluateAndCheckImpl(input, evaluationColumn); } - if (input.size() < 3) { - return input; + return std::vector(input.begin(), input.end()); } - // Helper to check the given block for column consistency up to the evaluation - // column. - const auto checkBlockIsInconsistent = - [evaluationColumn](const BlockMetadata& block) { - return getMaskedTriple(block.firstTriple_, evaluationColumn) != - getMaskedTriple(block.lastTriple_, evaluationColumn); - }; + std::optional firstBlock = std::nullopt; std::optional lastBlock = std::nullopt; - if (checkBlockIsInconsistent(input.front())) { + if (checkBlockIsInconsistent(input.front(), evaluationColumn)) { firstBlock = input.front(); - input.erase(input.begin()); + input = input.subspan(1); } - if (checkBlockIsInconsistent(input.back())) { + if (checkBlockIsInconsistent(input.back(), evaluationColumn)) { lastBlock = input.back(); - input.pop_back(); + input = input.subspan(0, input.size() - 1); } - input = evaluateAndCheckImpl(input, evaluationColumn); + + auto result = evaluateAndCheckImpl(input, evaluationColumn); if (firstBlock.has_value()) { - input.insert(input.begin(), firstBlock.value()); + result.insert(result.begin(), firstBlock.value()); } if (lastBlock.has_value()) { - input.push_back(lastBlock.value()); + result.push_back(lastBlock.value()); } - return input; + return result; }; // _____________________________________________________________________________ std::vector PrefilterExpression::evaluateAndCheckImpl( - std::vector& input, size_t evaluationColumn) const { + std::span input, size_t evaluationColumn) const { checkEvalRequirements(input, evaluationColumn); const auto& relevantBlocks = evaluateImpl(input, evaluationColumn); checkEvalRequirements(relevantBlocks, evaluationColumn); @@ -240,7 +240,7 @@ RelationalExpression::logicalComplement() const { //______________________________________________________________________________ template std::vector RelationalExpression::evaluateImpl( - std::vector& input, size_t evaluationColumn) const { + std::span input, size_t evaluationColumn) const { using namespace valueIdComparators; std::vector valueIdsInput; // For each BlockMetadata value in vector input, we have a respective Id for @@ -355,7 +355,7 @@ LogicalExpression::logicalComplement() const { //______________________________________________________________________________ template std::vector LogicalExpression::evaluateImpl( - std::vector& input, size_t evaluationColumn) const { + std::span input, size_t evaluationColumn) const { using enum LogicalOperator; if constexpr (Operation == AND) { auto resultChild1 = child1_->evaluate(input, evaluationColumn, false); @@ -414,7 +414,7 @@ std::unique_ptr NotExpression::logicalComplement() const { //______________________________________________________________________________ std::vector NotExpression::evaluateImpl( - std::vector& input, size_t evaluationColumn) const { + std::span input, size_t evaluationColumn) const { return child_->evaluate(input, evaluationColumn, false); }; diff --git a/src/engine/sparqlExpressions/PrefilterExpressionIndex.h b/src/engine/sparqlExpressions/PrefilterExpressionIndex.h index 5656014aab..8b73a720ca 100644 --- a/src/engine/sparqlExpressions/PrefilterExpressionIndex.h +++ b/src/engine/sparqlExpressions/PrefilterExpressionIndex.h @@ -90,7 +90,7 @@ class PrefilterExpression { // The flag value shouldn't be changed in general, because `evaluate()` only // removes the respective block if it is conditionally (inconsistent columns) // necessary. - std::vector evaluate(std::vector& input, + std::vector evaluate(std::span input, size_t evaluationColumn, bool stripIncompleteBlocks = true) const; @@ -112,10 +112,10 @@ class PrefilterExpression { // If a respective condition is violated, the function performing the checks // will throw a `std::runtime_error`. std::vector evaluateAndCheckImpl( - std::vector& input, size_t evaluationColumn) const; + std::span input, size_t evaluationColumn) const; virtual std::vector evaluateImpl( - std::vector& input, size_t evaluationColumn) const = 0; + std::span input, size_t evaluationColumn) const = 0; }; //______________________________________________________________________________ @@ -144,7 +144,7 @@ class RelationalExpression : public PrefilterExpression { private: std::vector evaluateImpl( - std::vector& input, + std::span input, size_t evaluationColumn) const override; }; @@ -174,7 +174,7 @@ class LogicalExpression : public PrefilterExpression { private: std::vector evaluateImpl( - std::vector& input, + std::span input, size_t evaluationColumn) const override; }; @@ -199,7 +199,7 @@ class NotExpression : public PrefilterExpression { private: std::vector evaluateImpl( - std::vector& input, + std::span input, size_t evaluationColumn) const override; }; diff --git a/test/PrefilterExpressionIndexTest.cpp b/test/PrefilterExpressionIndexTest.cpp index 1a678ce2a8..1af72ed896 100644 --- a/test/PrefilterExpressionIndexTest.cpp +++ b/test/PrefilterExpressionIndexTest.cpp @@ -36,10 +36,16 @@ Order ValueIds for (signed) doubles values - [0.0... max, -0.0... -max] Order ValueIds for Vocab and LocalVocab values given the vocabulary with indices (up to N) - [VocabId(0), .... VocabId(N)] -COLUMN 1 and COLUMN 2 contain fixed values, this is a necessary condition +COLUMN 0 and COLUMN 1 contain fixed values, this is a necessary condition that is also checked during the pre-filtering procedure. The actual evaluation -column (we filter w.r.t. values of COLUMN 0) contains mixed types. +column (we filter w.r.t. values of COLUMN 2) contains mixed types. */ + +// Fixed column ValueIds for our test-metadata. +const Id VocabId10 = VocabId(10); +const Id DoubleId33 = DoubleId(33); +const Id GraphId = VocabId(0); + //______________________________________________________________________________ class PrefilterExpressionOnMetadataTest : public ::testing::Test { public: @@ -50,17 +56,18 @@ class PrefilterExpressionOnMetadataTest : public ::testing::Test { const Id trueId = BoolId(true); const Id referenceDateEqual = DateId(DateParser, "2000-01-01"); - // Fixed column ValueIds - const Id VocabId10 = VocabId(10); - const Id DoubleId33 = DoubleId(33); - const Id GraphId = VocabId(0); - // Define BlockMetadata const BlockMetadata b1 = makeBlock(undef, undef); + const BlockMetadata bFirstIncomplete = + makeBlock(undef, undef, IntId(10), IntId(10), IntId(10), IntId(11)); const BlockMetadata b2 = makeBlock(undef, falseId); const BlockMetadata b3 = makeBlock(falseId, falseId); const BlockMetadata b4 = makeBlock(trueId, IntId(0)); + const BlockMetadata b4Incomplete = makeBlock( + trueId, IntId(0), VocabId(10), DoubleId(33), VocabId(11), DoubleId(33)); const BlockMetadata b5 = makeBlock(IntId(0), IntId(0)); + const BlockMetadata b5Incomplete = makeBlock( + IntId(0), IntId(0), VocabId(10), DoubleId(33), VocabId(10), DoubleId(34)); const BlockMetadata b6 = makeBlock(IntId(0), IntId(5)); const BlockMetadata b7 = makeBlock(IntId(5), IntId(6)); const BlockMetadata b8 = makeBlock(IntId(8), IntId(9)); @@ -83,16 +90,49 @@ class PrefilterExpressionOnMetadataTest : public ::testing::Test { DateId(DateParser, "2000-01-01")); const BlockMetadata b24 = makeBlock(DateId(DateParser, "2024-10-08"), BlankNodeId(10)); + const BlockMetadata bLastIncomplete = makeBlock( + DateId(DateParser, "2024-10-08"), DateId(DateParser, "2025-10-08"), + VocabId(0), VocabId(0), VocabId(1), VocabId(0)); - // All blocks that contain mixed (ValueId) types over column 0, + // All blocks that contain mixed (ValueId) types over column 2, // or possibly incomplete ones. const std::vector mixedBlocks = {b2, b4, b11, b18, b22, b24}; + // All blocks that contain mixed types over column 2 + the first and last + // incomplete block. + const std::vector mixedAndIncompleteBlocks = { + bFirstIncomplete, b2, b4, b11, b18, b22, bLastIncomplete}; + // Ordered and unique vector with BlockMetadata const std::vector blocks = { b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18, b19, b20, b21, b22, b23, b24}; + const std::vector blocksIncomplete = {bFirstIncomplete, + b2, + b3, + b4, + b5, + b6, + b7, + b8, + b9, + b10, + b11, + b12, + b13, + b14, + b15, + b16, + b17, + b18, + b19, + b20, + b21, + b22, + b23, + bLastIncomplete}; + const std::vector blocksInvalidOrder1 = { b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18, b19, b20, b21, b22, b24, b23}; @@ -101,24 +141,32 @@ class PrefilterExpressionOnMetadataTest : public ::testing::Test { b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b14, b10, b15, b16, b17, b18, b19, b20, b21, b22, b23, b24}; - const std::vector blocksWithDuplicate1 = { - b1, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, - b13, b14, b15, b16, b17, b18, b19, b20, b21, b22, b23, b24}; + const std::vector blocksWithDuplicate1 = {b1, b1, b2, + b3, b4, b5}; + + const std::vector blocksWithDuplicate2 = {b1, b2, b3, + b23, b24, b24}; - const std::vector blocksWithDuplicate2 = { - b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, - b14, b15, b16, b17, b18, b19, b20, b21, b22, b23, b24, b24}; + const std::vector blocksInconsistent1 = { + b1, b2, b3, b4Incomplete, b5, b6, b7}; + + const std::vector blocksInconsistent2 = { + b1, b2, b3, b4, b5Incomplete, b6, b7}; // Function to create BlockMetadata - const BlockMetadata makeBlock(const ValueId& firstId, const ValueId& lastId) { - assert(firstId <= lastId); + const BlockMetadata makeBlock(const ValueId& first2Id, const ValueId& last2Id, + const ValueId& first0Id = VocabId10, + const ValueId& first1Id = DoubleId33, + const ValueId& last0Id = VocabId10, + const ValueId& last1Id = DoubleId33) { + assert(first2Id <= last2Id); static size_t blockIdx = 0; ++blockIdx; return {{{}, 0, // COLUMN 0 | COLUMN 1 | COLUMN 2 - {firstId, VocabId10, DoubleId33, GraphId}, // firstTriple - {lastId, VocabId10, DoubleId33, GraphId}, // lastTriple + {first0Id, first1Id, first2Id, GraphId}, // firstTriple + {last0Id, last1Id, last2Id, GraphId}, // lastTriple {}, false}, blockIdx}; @@ -128,7 +176,7 @@ class PrefilterExpressionOnMetadataTest : public ::testing::Test { auto makeTestErrorCheck(std::unique_ptr expr, const std::vector& input, const std::string& expected, - size_t evaluationColumn = 0) { + size_t evaluationColumn = 2) { std::vector testBlocks = input; AD_EXPECT_THROW_WITH_MESSAGE(expr->evaluate(testBlocks, evaluationColumn), ::testing::HasSubstr(expected)); @@ -142,17 +190,20 @@ class PrefilterExpressionOnMetadataTest : public ::testing::Test { // Check that the provided expression prefilters the correct blocks. auto makeTest(std::unique_ptr expr, - std::vector&& expected) { + std::vector&& expected, + bool useBlocksIncomplete = false) { std::vector expectedAdjusted; // This is for convenience, we automatically insert all mixed and possibly // incomplete blocks which must be always returned. std::ranges::set_union( - expected, mixedBlocks, std::back_inserter(expectedAdjusted), + expected, useBlocksIncomplete ? mixedAndIncompleteBlocks : mixedBlocks, + std::back_inserter(expectedAdjusted), [](const BlockMetadata& b1, const BlockMetadata& b2) { return b1.blockIndex_ < b2.blockIndex_; }); - std::vector testBlocks = blocks; - ASSERT_EQ(expr->evaluate(testBlocks, 0), expectedAdjusted); + std::vector testBlocks = + useBlocksIncomplete ? blocksIncomplete : blocks; + ASSERT_EQ(expr->evaluate(testBlocks, 2), expectedAdjusted); } }; @@ -161,16 +212,16 @@ class PrefilterExpressionOnMetadataTest : public ::testing::Test { //______________________________________________________________________________ TEST_F(PrefilterExpressionOnMetadataTest, testBlockFormatForDebugging) { EXPECT_EQ( - "#BlockMetadata\n(first) Triple: I:0 V:10 D:33.000000 V:0\n(last) " - "Triple: I:0 V:10 D:33.000000 V:0\nnum. rows: 0.\n", + "#BlockMetadata\n(first) Triple: V:10 D:33.000000 I:0 V:0\n(last) " + "Triple: V:10 D:33.000000 I:0 V:0\nnum. rows: 0.\n", (std::stringstream() << b5).str()); EXPECT_EQ( - "#BlockMetadata\n(first) Triple: I:-4 V:10 D:33.000000 V:0\n(last) " - "Triple: D:2.000000 V:10 D:33.000000 V:0\nnum. rows: 0.\n", + "#BlockMetadata\n(first) Triple: V:10 D:33.000000 I:-4 V:0\n(last) " + "Triple: V:10 D:33.000000 D:2.000000 V:0\nnum. rows: 0.\n", (std::stringstream() << b11).str()); EXPECT_EQ( - "#BlockMetadata\n(first) Triple: V:14 V:10 D:33.000000 V:0\n(last) " - "Triple: V:17 V:10 D:33.000000 V:0\nnum. rows: 0.\n", + "#BlockMetadata\n(first) Triple: V:10 D:33.000000 V:14 V:0\n(last) " + "Triple: V:10 D:33.000000 V:17 V:0\nnum. rows: 0.\n", (std::stringstream() << b21).str()); } @@ -181,23 +232,23 @@ TEST_F(PrefilterExpressionOnMetadataTest, testBlockFormatForDebugging) { // datatypes to the expected result. TEST_F(PrefilterExpressionOnMetadataTest, testLessThanExpressions) { makeTest(lt(IntId(5)), - {b5, b6, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18}); + {b5, b6, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18}, true); makeTest(lt(IntId(-12)), {b18}); - makeTest(lt(IntId(0)), {b9, b10, b15, b16, b17, b18}); + makeTest(lt(IntId(0)), {b9, b10, b15, b16, b17, b18}, true); makeTest(lt(DoubleId(-14.01)), {b18}); - makeTest(lt(DoubleId(-11.22)), {b17, b18}); + makeTest(lt(DoubleId(-11.22)), {b17, b18}, true); makeTest(lt(DoubleId(-4.121)), {b9, b15, b16, b17, b18}); makeTest(lt(VocabId(0)), {b18}); makeTest(lt(VocabId(12)), {b18, b19}); - makeTest(lt(VocabId(14)), {b18, b19}); + makeTest(lt(VocabId(14)), {b18, b19}, true); makeTest(lt(VocabId(16)), {b18, b19, b20, b21}); makeTest(lt(IntId(100)), {b5, b6, b7, b8, b9, b10, b12, b13, b14, b15, b16, b17, b18}); makeTest(lt(undef), {}); - makeTest(lt(falseId), {}); + makeTest(lt(falseId), {}, true); makeTest(lt(trueId), {b2, b3}); makeTest(lt(referenceDate1), {}); - makeTest(lt(referenceDateEqual), {b22}); + makeTest(lt(referenceDateEqual), {b22}, true); makeTest(lt(referenceDate2), {b22, b23, b24}); makeTest(lt(BlankNodeId(11)), {b24}); } @@ -210,20 +261,20 @@ TEST_F(PrefilterExpressionOnMetadataTest, testLessEqualExpressions) { makeTest(le(IntId(0)), {b5, b6, b9, b10, b11, b15, b16, b17, b18}); makeTest(le(IntId(-6)), {b9, b11, b15, b16, b17, b18}); makeTest(le(IntId(7)), - {b5, b6, b7, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18}); + {b5, b6, b7, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18}, true); makeTest(le(IntId(-9)), {b9, b11, b17, b18}); makeTest(le(DoubleId(-9.131)), {b9, b11, b17, b18}); makeTest(le(DoubleId(1.1415)), {b5, b6, b9, b10, b11, b15, b16, b17, b18}); makeTest(le(DoubleId(3.1415)), {b5, b6, b9, b10, b11, b12, b15, b16, b17, b18}); - makeTest(le(DoubleId(-11.99999999999999)), {b17, b18}); + makeTest(le(DoubleId(-11.99999999999999)), {b17, b18}, true); makeTest(le(DoubleId(-14.03)), {b18}); makeTest(le(VocabId(0)), {b18}); makeTest(le(VocabId(11)), {b18, b19}); - makeTest(le(VocabId(14)), {b18, b19, b20, b21}); + makeTest(le(VocabId(14)), {b18, b19, b20, b21}, true); makeTest(le(undef), {}); makeTest(le(falseId), {b2, b3}); - makeTest(le(trueId), {b2, b3, b4}); + makeTest(le(trueId), {b2, b3, b4}, true); makeTest(le(referenceDateEqual), {b22, b23}); makeTest(le(BlankNodeId(11)), {b24}); } @@ -234,26 +285,26 @@ TEST_F(PrefilterExpressionOnMetadataTest, testLessEqualExpressions) { // datatypes to the expected result. TEST_F(PrefilterExpressionOnMetadataTest, testGreaterThanExpression) { makeTest(gt(DoubleId(5.5375)), {b7, b8, b11, b14, b18}); - makeTest(gt(DoubleId(9.9994)), {b14}); + makeTest(gt(DoubleId(9.9994)), {b14}, true); makeTest(gt(IntId(-5)), {b5, b6, b7, b8, b10, b11, b12, b13, b14, b15}); makeTest(gt(DoubleId(-5.5375)), - {b5, b6, b7, b8, b10, b11, b12, b13, b14, b15}); + {b5, b6, b7, b8, b10, b11, b12, b13, b14, b15}, true); makeTest(gt(DoubleId(-6.2499999)), - {b5, b6, b7, b8, b10, b11, b12, b13, b14, b15}); + {b5, b6, b7, b8, b10, b11, b12, b13, b14, b15}, true); makeTest(gt(IntId(1)), {b6, b7, b8, b11, b12, b13, b14}); - makeTest(gt(IntId(3)), {b6, b7, b8, b11, b13, b14}); + makeTest(gt(IntId(3)), {b6, b7, b8, b11, b13, b14}, true); makeTest(gt(IntId(4)), {b6, b7, b8, b11, b14}); makeTest(gt(IntId(-4)), {b5, b6, b7, b8, b11, b12, b13, b14, b15}); makeTest(gt(IntId(33)), {}); makeTest(gt(VocabId(22)), {b22}); - makeTest(gt(VocabId(14)), {b21, b22}); + makeTest(gt(VocabId(14)), {b21, b22}, true); makeTest(gt(VocabId(12)), {b19, b20, b21, b22}); - makeTest(gt(undef), {}); - makeTest(gt(falseId), {b4}); + makeTest(gt(undef), {}, true); + makeTest(gt(falseId), {b4}, true); makeTest(gt(trueId), {}); makeTest(gt(referenceDateEqual), {b24}); - makeTest(gt(referenceDate1), {b22, b23, b24}); - makeTest(gt(referenceDate2), {b24}); + makeTest(gt(referenceDate1), {b22, b23, bLastIncomplete}, true); + makeTest(gt(referenceDate2), {bLastIncomplete}, true); } //______________________________________________________________________________ @@ -263,8 +314,8 @@ TEST_F(PrefilterExpressionOnMetadataTest, testGreaterThanExpression) { TEST_F(PrefilterExpressionOnMetadataTest, testGreaterEqualExpression) { makeTest(ge(IntId(0)), {b5, b6, b7, b8, b11, b12, b13, b14}); makeTest(ge(IntId(8)), {b8, b11, b14}); - makeTest(ge(DoubleId(9.98)), {b11, b14}); - makeTest(ge(IntId(-3)), {b5, b6, b7, b8, b11, b12, b13, b14, b15}); + makeTest(ge(DoubleId(9.98)), {b11, b14}, true); + makeTest(ge(IntId(-3)), {b5, b6, b7, b8, b11, b12, b13, b14, b15}, true); makeTest(ge(IntId(-10)), {b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16}); makeTest(ge(DoubleId(-3.1415)), {b5, b6, b7, b8, b11, b12, b13, b14, b15}); @@ -275,11 +326,11 @@ TEST_F(PrefilterExpressionOnMetadataTest, testGreaterEqualExpression) { {b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18}); makeTest(ge(DoubleId(7.999999)), {b8, b11, b14}); makeTest(ge(DoubleId(10.0001)), {}); - makeTest(ge(VocabId(14)), {b18, b19, b20, b21, b22}); + makeTest(ge(VocabId(14)), {b18, b19, b20, b21, b22}, true); makeTest(ge(VocabId(10)), {b18, b19, b20, b21, b22}); makeTest(ge(VocabId(17)), {b18, b21, b22}); - makeTest(ge(undef), {}); - makeTest(ge(falseId), {b2, b3, b4}); + makeTest(ge(undef), {}, true); + makeTest(ge(falseId), {b2, b3, b4}, true); makeTest(ge(trueId), {b4}); makeTest(ge(referenceDateEqual), {b23, b24}); } @@ -290,25 +341,25 @@ TEST_F(PrefilterExpressionOnMetadataTest, testGreaterEqualExpression) { // datatypes to the expected result. TEST_F(PrefilterExpressionOnMetadataTest, testEqualExpression) { makeTest(eq(IntId(0)), {b4, b5, b6, b11}); - makeTest(eq(IntId(5)), {b6, b7, b11, b14}); + makeTest(eq(IntId(5)), {b6, b7, b11, b14}, true); makeTest(eq(IntId(22)), {}); makeTest(eq(IntId(-10)), {b9, b11, b18}); makeTest(eq(DoubleId(-6.25)), {b15, b16}); - makeTest(eq(IntId(-11)), {b17}); + makeTest(eq(IntId(-11)), {b17}, true); makeTest(eq(DoubleId(-14.02)), {b18}); makeTest(eq(DoubleId(-0.001)), {b11}); - makeTest(eq(DoubleId(0)), {b4, b5, b6, b11}); - makeTest(eq(IntId(2)), {b6, b11, b12}); + makeTest(eq(DoubleId(0)), {b4, b5, b6, b11}, true); + makeTest(eq(IntId(2)), {b6, b11, b12}, true); makeTest(eq(DoubleId(5.5)), {b7, b11, b14}); makeTest(eq(DoubleId(1.5)), {b6, b11}); makeTest(eq(VocabId(1)), {b18}); - makeTest(eq(VocabId(14)), {b18, b19, b20, b21}); + makeTest(eq(VocabId(14)), {b18, b19, b20, b21}, true); makeTest(eq(VocabId(11)), {b18, b19}); makeTest(eq(VocabId(17)), {b18, b21}); - makeTest(eq(IntId(-4)), {b10, b11, b15}); + makeTest(eq(IntId(-4)), {b10, b11, b15}, true); makeTest(eq(trueId), {b4}); makeTest(eq(referenceDate1), {b22}); - makeTest(eq(referenceDateEqual), {b23}); + makeTest(eq(referenceDateEqual), {b23}, true); makeTest(eq(referenceDate2), {}); } @@ -318,25 +369,28 @@ TEST_F(PrefilterExpressionOnMetadataTest, testEqualExpression) { // datatypes to the expected result. TEST_F(PrefilterExpressionOnMetadataTest, testNotEqualExpression) { makeTest(neq(DoubleId(0.00)), - {b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18}); + {b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18}, true); makeTest(neq(IntId(-4)), {b5, b6, b7, b8, b9, b11, b12, b13, b14, b15, b16, b17, b18}); makeTest(neq(DoubleId(0.001)), - {b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18}); + {b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18}, + true); makeTest(neq(IntId(2)), {b5, b6, b7, b8, b9, b10, b11, b13, b14, b15, b16, b17, b18}); makeTest(neq(DoubleId(-6.2500)), {b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b17, b18}); makeTest(neq(IntId(5)), - {b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18}); + {b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18}, + true); makeTest(neq(DoubleId(-101.23)), - {b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18}); + {b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18}, + true); makeTest(neq(VocabId(0)), {b19, b20, b21, b22}); - makeTest(neq(VocabId(7)), {b18, b19, b20, b21, b22}); + makeTest(neq(VocabId(7)), {b18, b19, b20, b21, b22}, true); makeTest(neq(VocabId(14)), {b18, b19, b21, b22}); makeTest(neq(VocabId(17)), {b18, b19, b20, b21, b22}); makeTest(neq(undef), {}); - makeTest(neq(falseId), {b4}); + makeTest(neq(falseId), {b4}, true); makeTest(neq(referenceDateEqual), {b22, b24}); makeTest(neq(referenceDate1), {b22, b23, b24}); } @@ -348,25 +402,27 @@ TEST_F(PrefilterExpressionOnMetadataTest, testNotEqualExpression) { // datatypes to the expected result. TEST_F(PrefilterExpressionOnMetadataTest, testAndExpression) { makeTest(andExpr(ge(VocabId(10)), gt(VocabId(10))), {b19, b20, b21, b22}); - makeTest(andExpr(ge(VocabId(10)), ge(VocabId(10))), {b19, b20, b21, b22}); + makeTest(andExpr(ge(VocabId(10)), ge(VocabId(10))), {b19, b20, b21, b22}, + true); makeTest(andExpr(ge(VocabId(12)), gt(VocabId(17))), {b22}); - makeTest(andExpr(ge(VocabId(12)), gt(VocabId(17))), {b22}); - makeTest(andExpr(ge(VocabId(10)), lt(VocabId(14))), {b19}); + makeTest(andExpr(ge(VocabId(12)), gt(VocabId(17))), {b22}, true); + makeTest(andExpr(ge(VocabId(10)), lt(VocabId(14))), {b19}, true); makeTest(andExpr(le(VocabId(0)), lt(VocabId(10))), {b18}); makeTest(andExpr(le(VocabId(17)), lt(VocabId(17))), {b18, b19, b20, b21}); makeTest(andExpr(ge(DoubleId(-6.25)), lt(IntId(-7))), {}); makeTest(andExpr(gt(DoubleId(-6.25)), lt(DoubleId(-6.25))), {}); makeTest(andExpr(gt(IntId(0)), lt(IntId(0))), {}); - makeTest(andExpr(gt(IntId(-10)), lt(DoubleId(0))), {b9, b10, b11, b15, b16}); + makeTest(andExpr(gt(IntId(-10)), lt(DoubleId(0))), {b9, b10, b11, b15, b16}, + true); makeTest(andExpr(gt(IntId(0)), eq(DoubleId(0))), {b6, b11}); - makeTest(andExpr(ge(IntId(0)), eq(IntId(0))), {b5, b6, b11}); - makeTest(andExpr(gt(DoubleId(-34.23)), ge(DoubleId(15.1))), {}); + makeTest(andExpr(ge(IntId(0)), eq(IntId(0))), {b5, b6, b11}, true); + makeTest(andExpr(gt(DoubleId(-34.23)), ge(DoubleId(15.1))), {}, true); makeTest(andExpr(lt(IntId(0)), le(DoubleId(-4))), {b9, b10, b11, b15, b16, b17, b18}); makeTest(andExpr(neq(IntId(0)), neq(IntId(-4))), {b6, b7, b8, b9, b11, b12, b13, b14, b15, b16, b17, b18}); makeTest(andExpr(neq(DoubleId(-3.141)), eq(DoubleId(4.5))), - {b6, b11, b14, b18}); + {b6, b11, b14, b18}, true); makeTest(andExpr(neq(DoubleId(-6.25)), lt(IntId(0))), {b9, b10, b11, b15, b17, b18}); makeTest(andExpr(le(DoubleId(-4)), ge(DoubleId(1))), {}); @@ -374,7 +430,7 @@ TEST_F(PrefilterExpressionOnMetadataTest, testAndExpression) { makeTest(andExpr(andExpr(le(IntId(10)), gt(DoubleId(0))), eq(undef)), {}); makeTest(andExpr(gt(referenceDate1), le(IntId(10))), {}); makeTest(andExpr(gt(IntId(4)), andExpr(gt(DoubleId(8)), lt(IntId(10)))), - {b8, b14}); + {b8, b14}, true); makeTest(andExpr(eq(IntId(0)), andExpr(lt(IntId(-20)), gt(IntId(30)))), {}); makeTest(andExpr(eq(IntId(0)), andExpr(le(IntId(0)), ge(IntId(0)))), {b4, b5, b6, b11}); @@ -391,19 +447,19 @@ TEST_F(PrefilterExpressionOnMetadataTest, testOrExpression) { makeTest(orExpr(lt(DoubleId(-5.95)), eq(VocabId(14))), {b9, b15, b16, b17, b18, b19, b20, b21}); makeTest(orExpr(eq(DoubleId(0)), neq(VocabId(14))), - {b5, b6, b11, b18, b19, b21}); + {b5, b6, b11, b18, b19, b21}, true); makeTest(orExpr(eq(DoubleId(0)), eq(DoubleId(-6.25))), - {b5, b6, b11, b15, b16, b18}); + {b5, b6, b11, b15, b16, b18}, true); makeTest(orExpr(gt(undef), le(IntId(-6))), {b9, b15, b16, b17, b18}); makeTest(orExpr(le(trueId), gt(referenceDate1)), {b2, b3, b4, b22, b23, b24}); makeTest(orExpr(eq(IntId(0)), orExpr(lt(IntId(-10)), gt(IntId(8)))), - {b5, b6, b8, b11, b14, b17, b18}); + {b5, b6, b8, b11, b14, b17, b18}, true); makeTest(orExpr(gt(referenceDate2), eq(trueId)), {b4}); makeTest(orExpr(eq(VocabId(17)), orExpr(lt(VocabId(0)), gt(VocabId(20)))), - {b21, b22}); + {b21, b22}, true); makeTest(orExpr(eq(undef), gt(referenceDateEqual)), {b24}); makeTest(orExpr(gt(IntId(8)), gt(DoubleId(22.1))), {b8, b14}); - makeTest(orExpr(lt(DoubleId(-8.25)), le(IntId(-10))), {b9, b17, b18}); + makeTest(orExpr(lt(DoubleId(-8.25)), le(IntId(-10))), {b9, b17, b18}, true); makeTest(orExpr(eq(IntId(0)), neq(DoubleId(0.25))), {b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18}); makeTest(orExpr(gt(referenceDate1), orExpr(gt(trueId), eq(IntId(0)))), @@ -413,8 +469,8 @@ TEST_F(PrefilterExpressionOnMetadataTest, testOrExpression) { makeTest(orExpr(orExpr(eq(IntId(0)), eq(IntId(5))), orExpr(eq(DoubleId(-6.25)), lt(DoubleId(-12)))), {b4, b5, b6, b7, b11, b14, b15, b16, b18}); - makeTest(orExpr(le(trueId), gt(falseId)), {b2, b3, b4}); - makeTest(orExpr(eq(VocabId(0)), eq(DoubleId(0.25))), {b6, b11, b18}); + makeTest(orExpr(le(trueId), gt(falseId)), {b2, b3, b4}, true); + makeTest(orExpr(eq(VocabId(0)), eq(DoubleId(0.25))), {b6, b11, b18}, true); } //______________________________________________________________________________ @@ -422,29 +478,31 @@ TEST_F(PrefilterExpressionOnMetadataTest, testOrExpression) { // Note: the `makeTest` function automatically adds the blocks with mixed // datatypes to the expected result. TEST_F(PrefilterExpressionOnMetadataTest, testNotExpression) { - makeTest(notExpr(eq(VocabId(2))), {b18, b19, b20, b21, b22}); + makeTest(notExpr(eq(VocabId(2))), {b18, b19, b20, b21, b22}, true); makeTest(notExpr(eq(VocabId(14))), {b18, b19, b21, b22}); - makeTest(notExpr(neq(VocabId(14))), {b19, b20, b21}); + makeTest(notExpr(neq(VocabId(14))), {b19, b20, b21}, true); makeTest(notExpr(gt(VocabId(2))), {b18}); makeTest(notExpr(lt(DoubleId(-14.01))), {b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18}); makeTest(notExpr(ge(DoubleId(-14.01))), {b18}); - makeTest(notExpr(gt(DoubleId(-4.00))), {b9, b10, b11, b15, b16, b17, b18}); + makeTest(notExpr(gt(DoubleId(-4.00))), {b9, b10, b11, b15, b16, b17, b18}, + true); makeTest(notExpr(ge(DoubleId(-24.4))), {b18}); makeTest(notExpr(gt(referenceDate2)), {b22, b23}); makeTest(notExpr(le(trueId)), {}); - makeTest(notExpr(le(IntId(0))), {b6, b7, b8, b11, b12, b13, b14}); + makeTest(notExpr(le(IntId(0))), {b6, b7, b8, b11, b12, b13, b14}, true); makeTest(notExpr(gt(undef)), {}); makeTest(notExpr(eq(DoubleId(-6.25))), {b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b17, b18}); makeTest(notExpr(neq(DoubleId(4))), {b6, b11, b13, b14, b18}); makeTest(notExpr(gt(DoubleId(0))), - {b4, b5, b6, b9, b10, b11, b15, b16, b17, b18}); - makeTest(notExpr(notExpr(eq(IntId(0)))), {b4, b5, b6, b11}); + {b4, b5, b6, b9, b10, b11, b15, b16, b17, b18}, true); + makeTest(notExpr(notExpr(eq(IntId(0)))), {b4, b5, b6, b11}, true); makeTest(notExpr(notExpr(neq(DoubleId(-6.25)))), {b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b17, b18}); makeTest(notExpr(notExpr(lt(VocabId(10)))), {b18}); - makeTest(notExpr(notExpr(ge(DoubleId(3.99)))), {b6, b7, b8, b11, b13, b14}); + makeTest(notExpr(notExpr(ge(DoubleId(3.99)))), {b6, b7, b8, b11, b13, b14}, + true); makeTest(notExpr(andExpr(le(IntId(0)), ge(IntId(0)))), {b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18}); makeTest(notExpr(andExpr(neq(IntId(-10)), neq(DoubleId(-14.02)))), {b9, b18}); @@ -455,12 +513,13 @@ TEST_F(PrefilterExpressionOnMetadataTest, testNotExpression) { notExpr(andExpr(lt(DoubleId(-7)), ge(IntId(6)))), {b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18}); makeTest(notExpr(orExpr(le(IntId(0)), ge(DoubleId(6)))), - {b6, b7, b11, b12, b13, b14}); + {b6, b7, b11, b12, b13, b14}, true); makeTest(notExpr(orExpr(ge(DoubleId(0)), gt(IntId(-10)))), - {b9, b11, b17, b18}); + {b9, b11, b17, b18}, true); makeTest(notExpr(orExpr(lt(VocabId(10)), gt(VocabId(10)))), {b19}); - makeTest(notExpr(orExpr(lt(DoubleId(-4)), gt(IntId(-4)))), {b10, b11, b15}); - makeTest(notExpr(orExpr(gt(IntId(-42)), ge(VocabId(0)))), {b11}); + makeTest(notExpr(orExpr(lt(DoubleId(-4)), gt(IntId(-4)))), {b10, b11, b15}, + true); + makeTest(notExpr(orExpr(gt(IntId(-42)), ge(VocabId(0)))), {b11}, true); makeTest(notExpr(orExpr(ge(VocabId(14)), gt(VocabId(15)))), {b18, b19}); } @@ -473,26 +532,27 @@ TEST_F(PrefilterExpressionOnMetadataTest, makeTest(andExpr(notExpr(gt(DoubleId(-14.01))), lt(IntId(0))), {b18}); makeTest( orExpr(andExpr(gt(DoubleId(8.25)), le(IntId(10))), eq(DoubleId(-6.25))), - {b8, b14, b15, b16}); + {b8, b14, b15, b16}, true); makeTest( orExpr(andExpr(gt(DoubleId(8.25)), le(IntId(10))), lt(DoubleId(-6.25))), {b8, b9, b14, b17, b18}); makeTest(andExpr(orExpr(ge(trueId), le(falseId)), eq(referenceDate1)), {}); - makeTest(andExpr(eq(IntId(0)), orExpr(lt(IntId(-11)), le(IntId(-12)))), {}); + makeTest(andExpr(eq(IntId(0)), orExpr(lt(IntId(-11)), le(IntId(-12)))), {}, + true); makeTest( andExpr(eq(DoubleId(-4)), orExpr(gt(IntId(-4)), lt(DoubleId(-1.25)))), {b10, b11, b15}); makeTest(orExpr(notExpr(andExpr(lt(IntId(10)), gt(IntId(5)))), eq(IntId(0))), {b4, b5, b6, b7, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18}); makeTest(andExpr(orExpr(gt(VocabId(16)), le(VocabId(5))), gt(DoubleId(7.25))), - {}); + {}, true); makeTest(andExpr(lt(falseId), orExpr(lt(IntId(10)), gt(DoubleId(17.25)))), {}); makeTest( orExpr(andExpr(gt(VocabId(16)), ge(VocabId(17))), gt(DoubleId(7.25))), {b8, b14, b18, b21, b22}); makeTest(orExpr(eq(trueId), andExpr(gt(referenceDate1), lt(referenceDate2))), - {b4, b22, b23}); + {b4, b22, b23}, true); } //______________________________________________________________________________ @@ -509,12 +569,12 @@ TEST_F(PrefilterExpressionOnMetadataTest, testInputConditionCheck) { blocksInvalidOrder2, "The blocks must be provided in sorted order."); makeTestErrorCheck( - gt(DoubleId(2)), blocks, + gt(DoubleId(2)), blocksInconsistent1, "The values in the columns up to the evaluation column must be " "consistent.", 1); makeTestErrorCheck( - gt(DoubleId(2)), blocks, + gt(DoubleId(2)), blocksInconsistent2, "The values in the columns up to the evaluation column must be " "consistent.", 2); @@ -530,7 +590,9 @@ TEST_F(PrefilterExpressionOnMetadataTest, testWithFewBlockMetadataValues) { EXPECT_EQ(expr->evaluate(input, 2), input); expr = eq(DoubleId(-6.25)); input = {b15, b16, b17}; - EXPECT_EQ(expr->evaluate(input, 0), (std::vector{b15, b16})); + EXPECT_EQ(expr->evaluate(input, 2), (std::vector{b15, b16})); + EXPECT_EQ(expr->evaluate(input, 1), std::vector{}); + EXPECT_EQ(expr->evaluate(input, 0), std::vector{}); } //______________________________________________________________________________ @@ -586,6 +648,11 @@ TEST(PrefilterExpressionExpressionOnMetadataTest, auto expr = lt(IntId(10)); EXPECT_EQ((std::stringstream() << *expr).str(), "Prefilter RelationalExpression\nValueId: I:10\n.\n"); + expr = orExpr(eq(VocabId(0)), eq(VocabId(10))); + EXPECT_EQ((std::stringstream() << *expr).str(), + "Prefilter LogicalExpression\nchild1 {Prefilter " + "RelationalExpression\nValueId: V:0\n}child2 {Prefilter " + "RelationalExpression\nValueId: V:10\n}\n.\n"); expr = neq(DoubleId(8.21)); EXPECT_EQ((std::stringstream() << *expr).str(), "Prefilter RelationalExpression\nValueId: D:8.210000\n.\n"); From 25eb41864511712c8f6e6bc6bb2bb3efc874497f Mon Sep 17 00:00:00 2001 From: realHannes Date: Sun, 24 Nov 2024 19:38:46 +0100 Subject: [PATCH 63/81] correction for Codespell --- src/engine/sparqlExpressions/PrefilterExpressionIndex.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/sparqlExpressions/PrefilterExpressionIndex.cpp b/src/engine/sparqlExpressions/PrefilterExpressionIndex.cpp index 2bcd66f19b..eb00fd466f 100644 --- a/src/engine/sparqlExpressions/PrefilterExpressionIndex.cpp +++ b/src/engine/sparqlExpressions/PrefilterExpressionIndex.cpp @@ -52,7 +52,7 @@ static auto getMaskedTriple(const BlockMetadata::PermutedTriple& triple, }; //______________________________________________________________________________ -// Ckeck for constant values in all columns `< evaluationColumn` +// Check for constant values in all columns `< evaluationColumn` static bool checkBlockIsInconsistent(const BlockMetadata& block, size_t evaluationColumn) { return getMaskedTriple(block.firstTriple_, evaluationColumn) != From 83f236389f683a53e42d6a36e01f73c1d921f5a6 Mon Sep 17 00:00:00 2001 From: realHannes Date: Wed, 27 Nov 2024 11:02:39 +0100 Subject: [PATCH 64/81] add testing --- src/engine/IndexScan.cpp | 2 +- test/FilterTest.cpp | 61 ++++ ...lterExpressionFromSparqlExpressionTest.cpp | 85 +----- test/PrefilterExpressionTestHelpers.h | 107 +++++++ test/engine/IndexScanTest.cpp | 284 ++++++++++++++++++ 5 files changed, 456 insertions(+), 83 deletions(-) diff --git a/src/engine/IndexScan.cpp b/src/engine/IndexScan.cpp index ab567c08c6..18c7399271 100644 --- a/src/engine/IndexScan.cpp +++ b/src/engine/IndexScan.cpp @@ -126,7 +126,7 @@ string IndexScan::getCacheKeyImpl() const { auto& [prefilterExpr, columnIdx] = prefilter_.value(); os << "Added PrefiterExpression: \n"; os << *prefilterExpr; - os << "\n Applied on: " << columnIdx << "."; + os << "\nApplied on column: " << columnIdx << "."; } return std::move(os).str(); } diff --git a/test/FilterTest.cpp b/test/FilterTest.cpp index e683503548..6b2940007e 100644 --- a/test/FilterTest.cpp +++ b/test/FilterTest.cpp @@ -4,9 +4,12 @@ #include +#include "./PrefilterExpressionTestHelpers.h" #include "engine/Filter.h" #include "engine/ValuesForTesting.h" #include "engine/sparqlExpressions/LiteralExpression.h" +#include "engine/sparqlExpressions/RelationalExpressions.h" +#include "engine/sparqlExpressions/SparqlExpression.h" #include "util/IdTableHelpers.h" #include "util/IndexTestHelpers.h" @@ -29,6 +32,31 @@ std::vector toVector(Result::Generator generator) { } return result; } + +using namespace sparqlExpression; +using namespace prefilterExpressions; + +void checkSetPrefilterExpressionVariablePair( + QueryExecutionContext* qec, const Permutation::Enum& permutation, + SparqlTriple triple, std::unique_ptr sparqlExpr, + std::unique_ptr prefilterExpr, ColumnIndex columnIdx, + bool prefilterIsApplicable) { + Filter filter{ + qec, + ad_utility::makeExecutionTree(qec, permutation, triple), + {std::move(sparqlExpr), "Expression ?x"}}; + std::stringstream os; + os << "Added PrefiterExpression: \n"; + os << *prefilterExpr; + os << "\nApplied on column: " << columnIdx << "."; + if (prefilterIsApplicable) { + EXPECT_THAT(filter.getCacheKey(), ::testing::HasSubstr(os.str())); + } else { + EXPECT_THAT(filter.getCacheKey(), + ::testing::Not(::testing::HasSubstr(os.str()))); + } +} + } // namespace // _____________________________________________________________________________ @@ -134,3 +162,36 @@ TEST(Filter, result->idTable(), makeIdTableFromVector({{true}, {true}, {true}, {true}, {true}}, asBool)); } + +// _____________________________________________________________________________ +TEST(Filter, verifySetPrefilterExpressionVariablePairForIndexScanChild) { + using namespace makeFilterExpression; + using namespace makeSparqlExpression; + using namespace ad_utility::testing; + std::string kg = "

22.5 ."; + QueryExecutionContext* qec = ad_utility::testing::getQec(kg); + // For the following tests a pair should be + // assigned to the IndexScan child (prefiltering is possible) with Filter + // construction. + checkSetPrefilterExpressionVariablePair( + qec, Permutation::POS, {Variable{"?x"}, "

", Variable{"?z"}}, + ltSprql(Variable{"?z"}, IntId(10)), lt(IntId(10)), 1, true); + checkSetPrefilterExpressionVariablePair( + qec, Permutation::POS, {Variable{"?x"}, "

", Variable{"?z"}}, + andSprqlExpr(neqSprql(Variable{"?z"}, IntId(10)), + gtSprql(Variable{"?y"}, DoubleId(0))), + neq(IntId(10)), 1, true); + checkSetPrefilterExpressionVariablePair( + qec, Permutation::PSO, + {makeSparqlExpression::Iri::fromIriref(""), "

", Variable{"?z"}}, + eqSprql(Variable{"?z"}, DoubleId(22.5)), eq(DoubleId(22.5)), 2, true); + + // We expect that no pair is assigned + // (no prefilter procedure applicable) with Filter construction. + checkSetPrefilterExpressionVariablePair( + qec, Permutation::PSO, {Variable{"?x"}, "

", Variable{"?z"}}, + eqSprql(Variable{"?z"}, DoubleId(22.5)), eq(DoubleId(22.5)), 1, false); + checkSetPrefilterExpressionVariablePair( + qec, Permutation::POS, {Variable{"?x"}, "

", Variable{"?z"}}, + gtSprql(Variable{"?x"}, VocabId(10)), gt(VocabId(10)), 1, false); +} diff --git a/test/GetPrefilterExpressionFromSparqlExpressionTest.cpp b/test/GetPrefilterExpressionFromSparqlExpressionTest.cpp index 8a476db000..d154f856c5 100644 --- a/test/GetPrefilterExpressionFromSparqlExpressionTest.cpp +++ b/test/GetPrefilterExpressionFromSparqlExpressionTest.cpp @@ -13,80 +13,13 @@ using ad_utility::testing::IntId; using ad_utility::testing::UndefId; using ad_utility::testing::VocabId; -using Literal = ad_utility::triple_component::Literal; -using Iri = ad_utility::triple_component::Iri; -using RelValues = std::variant; +using namespace makeSparqlExpression; +using namespace makeFilterExpression; +using namespace makeFilterExpression::filterHelper; namespace { - using namespace sparqlExpression; -namespace makeSparqlExpression { - -//______________________________________________________________________________ -const auto makeLiteralSparqlExpr = - [](const auto& child) -> std::unique_ptr { - using T = std::decay_t; - if constexpr (std::is_same_v) { - return std::make_unique(child); - } else if constexpr (std::is_same_v) { - return std::make_unique(child); - } else if constexpr (std::is_same_v) { - return std::make_unique(child); - } else if constexpr (std::is_same_v) { - return std::make_unique(child); - } else { - throw std::runtime_error( - "Can't create a LiteralExpression from provided (input) type."); - } -}; - -//______________________________________________________________________________ -auto getExpr = [](const auto& variantVal) -> std::unique_ptr { - return makeLiteralSparqlExpr(variantVal); -}; - -//______________________________________________________________________________ -template -std::unique_ptr makeRelationalSparqlExprImpl( - const RelValues& child0, const RelValues& child1) { - return std::make_unique(std::array{ - std::visit(getExpr, child0), std::visit(getExpr, child1)}); -}; - -//______________________________________________________________________________ -std::unique_ptr makeStringStartsWithSparqlExpression( - const RelValues& child0, const RelValues& child1) { - return makeStrStartsExpression(std::visit(getExpr, child0), - std::visit(getExpr, child1)); -}; - -//______________________________________________________________________________ -// LESS THAN (`<`, `SparqlExpression`) -constexpr auto ltSprql = &makeRelationalSparqlExprImpl; -// LESS EQUAL (`<=`, `SparqlExpression`) -constexpr auto leSprql = &makeRelationalSparqlExprImpl; -// EQUAL (`==`, `SparqlExpression`) -constexpr auto eqSprql = &makeRelationalSparqlExprImpl; -// NOT EQUAL (`!=`, `SparqlExpression`) -constexpr auto neqSprql = &makeRelationalSparqlExprImpl; -// GREATER EQUAL (`>=`, `SparqlExpression`) -constexpr auto geSprql = &makeRelationalSparqlExprImpl; -// GREATER THAN (`>`, `SparqlExpression`) -constexpr auto gtSprql = &makeRelationalSparqlExprImpl; -// AND (`&&`, `SparqlExpression`) -constexpr auto andSprqlExpr = &makeAndExpression; -// OR (`||`, `SparqlExpression`) -constexpr auto orSprqlExpr = &makeOrExpression; -// NOT (`!`, `SparqlExpression`) -constexpr auto notSprqlExpr = &makeUnaryNegateExpression; - -//______________________________________________________________________________ -// Create SparqlExpression `STRSTARTS`. -constexpr auto strStartsSprql = &makeStringStartsWithSparqlExpression; - -} // namespace makeSparqlExpression - //______________________________________________________________________________ // make `Literal` const auto L = [](const std::string& content) -> Literal { @@ -149,20 +82,8 @@ const auto evalAndEqualityCheck = std::move(prefilterVarPair)); }; -//______________________________________________________________________________ -// Construct a `PAIR` with the given `PrefilterExpression` and `Variable` value. -auto pr = - [](std::unique_ptr expr, - const Variable& var) -> sparqlExpression::PrefilterExprVariablePair { - return {std::move(expr), var}; -}; - } // namespace -using namespace makeSparqlExpression; -using namespace makeFilterExpression; -using namespace makeSparqlExpression; - //______________________________________________________________________________ // Test coverage for the default implementation of // getPrefilterExpressionForMetadata. diff --git a/test/PrefilterExpressionTestHelpers.h b/test/PrefilterExpressionTestHelpers.h index d148e8dc57..934470bf7a 100644 --- a/test/PrefilterExpressionTestHelpers.h +++ b/test/PrefilterExpressionTestHelpers.h @@ -67,4 +67,111 @@ constexpr auto orExpr = logExpr; // NOT (`!`) constexpr auto notExpr = notPrefilterExpression; +namespace filterHelper { +//______________________________________________________________________________ +// Construct a `PAIR` with the given `PrefilterExpression` and `Variable` value. +auto pr = + [](std::unique_ptr expr, + const Variable& var) -> sparqlExpression::PrefilterExprVariablePair { + return {std::move(expr), var}; +}; + +//______________________________________________________________________________ +// Create a vector containing the provided `` +// pairs. +auto makePrefilterVec = + [](std::convertible_to< + sparqlExpression::PrefilterExprVariablePair> auto&&... prefilterArgs) { + std::vector + prefilterVarPairs = {}; + if constexpr (sizeof...(prefilterArgs) > 0) { + (prefilterVarPairs.emplace_back( + std::forward( + prefilterArgs)), + ...); + } + return prefilterVarPairs; + }; + +} // namespace filterHelper + } // namespace makeFilterExpression + +namespace makeSparqlExpression { +using namespace sparqlExpression; + +namespace { +using Literal = ad_utility::triple_component::Literal; +using Iri = ad_utility::triple_component::Iri; +using RelValues = std::variant; + +//______________________________________________________________________________ +const auto makeLiteralSparqlExpr = + [](const auto& child) -> std::unique_ptr { + using T = std::decay_t; + if constexpr (std::is_same_v) { + return std::make_unique(child); + } else if constexpr (std::is_same_v) { + return std::make_unique(child); + } else if constexpr (std::is_same_v) { + return std::make_unique(child); + } else if constexpr (std::is_same_v) { + return std::make_unique(child); + } else { + throw std::runtime_error( + "Can't create a LiteralExpression from provided (input) type."); + } +}; + +//______________________________________________________________________________ +auto getExpr = [](const auto& variantVal) -> std::unique_ptr { + return makeLiteralSparqlExpr(variantVal); +}; + +//______________________________________________________________________________ +template +std::unique_ptr makeRelationalSparqlExprImpl( + const RelValues& child0, const RelValues& child1) { + return std::make_unique(std::array{ + std::visit(getExpr, child0), std::visit(getExpr, child1)}); +}; + +//______________________________________________________________________________ +std::unique_ptr makeStringStartsWithSparqlExpression( + const RelValues& child0, const RelValues& child1) { + return makeStrStartsExpression(std::visit(getExpr, child0), + std::visit(getExpr, child1)); +}; +} // namespace + +//______________________________________________________________________________ +// LESS THAN (`<`, `SparqlExpression`) +constexpr auto ltSprql = + &makeRelationalSparqlExprImpl; +// LESS EQUAL (`<=`, `SparqlExpression`) +constexpr auto leSprql = + &makeRelationalSparqlExprImpl; +// EQUAL (`==`, `SparqlExpression`) +constexpr auto eqSprql = + &makeRelationalSparqlExprImpl; +// NOT EQUAL (`!=`, `SparqlExpression`) +constexpr auto neqSprql = + &makeRelationalSparqlExprImpl; +// GREATER EQUAL (`>=`, `SparqlExpression`) +constexpr auto geSprql = + &makeRelationalSparqlExprImpl; +// GREATER THAN (`>`, `SparqlExpression`) +constexpr auto gtSprql = + &makeRelationalSparqlExprImpl; +// AND (`&&`, `SparqlExpression`) +constexpr auto andSprqlExpr = &makeAndExpression; +// OR (`||`, `SparqlExpression`) +constexpr auto orSprqlExpr = &makeOrExpression; +// NOT (`!`, `SparqlExpression`) +constexpr auto notSprqlExpr = &makeUnaryNegateExpression; + +//______________________________________________________________________________ +// Create SparqlExpression `STRSTARTS`. +constexpr auto strStartsSprql = &makeStringStartsWithSparqlExpression; + +} // namespace makeSparqlExpression diff --git a/test/engine/IndexScanTest.cpp b/test/engine/IndexScanTest.cpp index 7a0383390b..4a39605d1d 100644 --- a/test/engine/IndexScanTest.cpp +++ b/test/engine/IndexScanTest.cpp @@ -4,11 +4,15 @@ #include +#include + +#include "../test/PrefilterExpressionTestHelpers.h" #include "../util/GTestHelpers.h" #include "../util/IdTableHelpers.h" #include "../util/IndexTestHelpers.h" #include "../util/TripleComponentTestHelpers.h" #include "engine/IndexScan.h" +#include "index/IndexImpl.h" #include "parser/ParsedQuery.h" using namespace ad_utility::testing; @@ -170,6 +174,128 @@ void testLazyScanWithColumnThrows( EXPECT_ANY_THROW(makeScan()); } +// _____________________________________________________________________________ +// Helper function to retrieve the corresponding column (`Id`) values from +// the provided `CompressedBlockMetadata` data as a vector. +// Note: A complete column which corresponds to an actual `IdTable` column can +// only be extracted if the `CompressedBlockMetadata` values adhere to the +// condition `numRows_ < 3` +std::vector getColumnFromBlockMetadata( + std::span blocks, + const size_t evaluationColumn, + const std::pair& firstLastIncomplete) { + auto getIdFromBlock = + [evaluationColumn]( + const CompressedBlockMetadata::PermutedTriple& triple) { + switch (evaluationColumn) { + case 0: + return triple.col0Id_; + case 1: + return triple.col1Id_; + case 2: + return triple.col2Id_; + default: + AD_FAIL(); + } + }; + + std::vector valueIds; + valueIds.reserve(blocks.size() * 2); + std::optional incompleteLast = std::nullopt; + + // Handle possibly incomplete first/last block. + if (firstLastIncomplete.first) { + valueIds.push_back(getIdFromBlock(blocks.front().lastTriple_)); + blocks = blocks.subspan(1); + } + if (firstLastIncomplete.second) { + incompleteLast = blocks.back(); + blocks = blocks.subspan(0, blocks.size() - 1); + } + + // Add the respective evaluation column IDs for the complete blocks. + for (const auto& block : blocks) { + valueIds.push_back(getIdFromBlock(block.firstTriple_)); + if (block.numRows_ > 1) { + valueIds.push_back(getIdFromBlock(block.lastTriple_)); + } + } + + if (incompleteLast.has_value()) { + // Given the last block is incomplete, only add the ID of the first triple. + valueIds.push_back(getIdFromBlock(incompleteLast.value().firstTriple_)); + } + return valueIds; +}; + +//______________________________________________________________________________ +// Check that the `IndexScan` computes correct prefiltered `IdTable`s w.r.t. +// the applied `PrefilterExpression`. +// This is implemented by comparing the relevant `IdTable` column with the +// evaluationColumn regarding the `CompressedBlockMetadata` values yielded by +// the `PrefilterExpression`. +void testScanResultWithAddedPrefiltering( + std::shared_ptr updatedQet, + IndexScan::PrefilterVariablePair prefilterVariablePair, + const std::pair& firstLastIncomplete, + const size_t evaluationColumn, + source_location l = source_location::current()) { + auto t = generateLocationTrace(l); + auto& [expr, variable] = prefilterVariablePair; + auto resultIndex = updatedQet->getVariableColumn(variable); + auto operationPtr = updatedQet->getRootOperation(); + std::shared_ptr indexScan = + std::dynamic_pointer_cast(operationPtr); + ASSERT_TRUE(indexScan); + auto metadata = + indexScan->getIndex() + .getImpl() + .getPermutation(indexScan->permutation()) + .getMetadataAndBlocks(indexScan->getScanSpecification(), + indexScan->locatedTriplesSnapshot()); + ASSERT_TRUE(metadata.has_value()); + auto blocks = + CompressedRelationReader::getBlocksFromMetadata(metadata.value()); + auto filteredBlocks = expr->evaluate(blocks, evaluationColumn); + auto idTableFiltered = + operationPtr->computeResultOnlyForTesting().idTable().clone(); + auto columnSpan = idTableFiltered.getColumn(resultIndex); + ASSERT_EQ((std::vector{columnSpan.begin(), columnSpan.end()}), + getColumnFromBlockMetadata(filteredBlocks, evaluationColumn, + firstLastIncomplete)); +} + +//______________________________________________________________________________ +void testSetAndMakeScanWithPrefilterExpr( + const std::string& kg, const SparqlTriple& triple, + const Permutation::Enum permutation, const size_t evaluationColumn, + IndexScan::PrefilterVariablePair prefilterVariablePair, + std::pair firstLastIncomplete, + const bool prefilterCanBeSet = true, + source_location l = source_location::current()) { + auto t = generateLocationTrace(l); + auto clonedPair = std::make_pair(prefilterVariablePair.first->clone(), + prefilterVariablePair.second); + IndexScan scan{getQec(kg), permutation, triple}; + auto optUpdatedQet = scan.setPrefilterGetUpdatedQueryExecutionTree( + makeFilterExpression::filterHelper::makePrefilterVec( + std::move(prefilterVariablePair))); + if (optUpdatedQet.has_value()) { + ASSERT_TRUE(prefilterCanBeSet); + // Check that the prefiltering procedure yields the correct result given + // that the pair is correctly assigned to + // IndexScan. + testScanResultWithAddedPrefiltering(optUpdatedQet.value(), + std::move(clonedPair), + firstLastIncomplete, evaluationColumn); + } else { + // Check our predicition that the prefilter with the given + // pair is not applicable (no updated + // QueryExecutionTree is returned). + ASSERT_FALSE(prefilterCanBeSet); + } +} + } // namespace TEST(IndexScan, lazyScanForJoinOfTwoScans) { @@ -555,3 +681,161 @@ TEST(IndexScan, unlikelyToFitInCacheCalculatesSizeCorrectly) { expectMaximumCacheableSize(scan, 1, 1); } } + +// _____________________________________________________________________________ +TEST(IndexScan, getSizeEstimateAndExactSizeWithAppliedPrefilter) { + using namespace makeFilterExpression; + using namespace filterHelper; + using I = TripleComponent::Iri; + + std::string kg = + " 10.00 . 12.00 . " + "12.001 . 21.99 . 24.33 . " + " 147.32 . 189.99 . 194.67 " + "."; + auto qec = getQec(kg); + + auto assertEstimatedAndExactSize = [](IndexScan& indexScan, + IndexScan::PrefilterVariablePair pair, + const size_t estimateSize, + const size_t exactSize) { + auto optUpdatedQet = indexScan.setPrefilterGetUpdatedQueryExecutionTree( + makePrefilterVec(std::move(pair))); + ASSERT_TRUE(optUpdatedQet.has_value()); + auto updatedQet = optUpdatedQet.value(); + ASSERT_EQ(updatedQet->getSizeEstimate(), estimateSize); + std::shared_ptr scanPtr = + std::dynamic_pointer_cast(updatedQet->getRootOperation()); + ASSERT_EQ(scanPtr->getExactSize(), exactSize); + }; + + { + SparqlTriple triple{Tc{Variable{"?b"}}, "", + Tc{Variable{"?price"}}}; + IndexScan scan{qec, Permutation::POS, triple}; + assertEstimatedAndExactSize( + scan, + pr(andExpr(gt(IntId(0)), lt(DoubleId(234.35))), Variable{"?price"}), 8, + 8); + assertEstimatedAndExactSize( + scan, + pr(andExpr(gt(DoubleId(12.00)), lt(IntId(190))), Variable{"?price"}), 6, + 6); + assertEstimatedAndExactSize( + scan, pr(le(DoubleId(21.99)), Variable{"?price"}), 4, 4); + } + + { + SparqlTriple triple{I::fromIriref(""), "", + Variable{"?price"}}; + IndexScan scan{qec, Permutation::PSO, triple}; + assertEstimatedAndExactSize( + scan, pr(le(DoubleId(21.99)), Variable{"?price"}), 3, 3); + assertEstimatedAndExactSize( + scan, pr(eq(DoubleId(24.33)), Variable{"?price"}), 3, 3); + assertEstimatedAndExactSize( + scan, pr(orExpr(le(IntId(12)), gt(IntId(22))), Variable{"?price"}), 5, + 5); + } +} + +// _____________________________________________________________________________ +TEST(IndexScan, SetPrefilterVariablePairAndCheckCacheKey) { + using namespace makeFilterExpression; + using namespace filterHelper; + using V = Variable; + auto qec = getQec(" ."); + SparqlTriple triple{V{"?x"}, "", V{"?z"}}; + auto scan = IndexScan{qec, Permutation::PSO, triple}; + auto prefilterPairs = + makePrefilterVec(pr(lt(IntId(10)), V{"?a"}), pr(gt(IntId(5)), V{"?b"}), + pr(lt(IntId(5)), V{"?x"})); + auto updatedQet = + scan.setPrefilterGetUpdatedQueryExecutionTree(std::move(prefilterPairs)); + // We have a corresponding column for ?x (ColumnIndex 1), which is also the + // first sorted variable column. Thus, we expect that PrefilterExpression (< + // 5, ?x) will be set as a prefilter for this IndexScan. + auto setPrefilterExpr = lt(IntId(5)); + ColumnIndex columnIdx = 1; + std::stringstream os; + os << "Added PrefiterExpression: \n"; + os << *setPrefilterExpr; + os << "\nApplied on column: " << columnIdx << "."; + EXPECT_THAT(updatedQet.value()->getRootOperation()->getCacheKey(), + ::testing::HasSubstr(os.str())); + + // Assert that we don't set a pair for the + // second Variable. + prefilterPairs = makePrefilterVec(pr(lt(IntId(10)), V{"?a"}), + pr(gt(DoubleId(22)), V{"?z"}), + pr(gt(IntId(10)), V{"?b"})); + updatedQet = + scan.setPrefilterGetUpdatedQueryExecutionTree(std::move(prefilterPairs)); + // No PrefilterExpression should be set for this IndexScan, we don't expect a + // updated QueryExecutionTree. + EXPECT_TRUE(!updatedQet.has_value()); +} + +// _____________________________________________________________________________ +TEST(IndexScan, checkEvaluationWithPrefiltering) { + using namespace makeFilterExpression; + using namespace filterHelper; + using I = TripleComponent::Iri; + std::string kg = + " 10.00 . 12.00 . " + "12.001 . 21.99 . 24.33 . " + " 147.32 . 174.00 . 174.43 " + ". 189.99 . 194.67 ."; + SparqlTriple triple{Tc{Variable{"?x"}}, "", + Tc{Variable{"?price"}}}; + + // For the following tests, the pair is set + // and applied for the respective IndexScan. + testSetAndMakeScanWithPrefilterExpr(kg, triple, Permutation::POS, 1, + pr(ge(IntId(10)), Variable{"?price"}), + std::make_pair(false, false)); + testSetAndMakeScanWithPrefilterExpr( + kg, triple, Permutation::POS, 1, + pr(lt(DoubleId(147.32)), Variable{"?price"}), + std::make_pair(false, false)); + testSetAndMakeScanWithPrefilterExpr( + kg, triple, Permutation::POS, 1, + pr(andExpr(gt(DoubleId(12.00)), le(IntId(174))), Variable{"?price"}), + std::make_pair(false, false)); + + // For the following test, the Variable value doesn't match any of the scan + // triple Variable values. We expect that the prefilter is not applicable (=> + // set bool flag to false). + testSetAndMakeScanWithPrefilterExpr( + kg, triple, Permutation::POS, 1, + pr(andExpr(gt(DoubleId(12.00)), le(IntId(174))), Variable{"?y"}), + std::make_pair(false, false), false); + + // For the following tests, the first sorted column given the permutation + // doesn't match with the corresponding column for the Variable of the + // pair. We expect that the provided prefilter + // is not applicable (and can't be set). + testSetAndMakeScanWithPrefilterExpr( + kg, triple, Permutation::PSO, 1, + pr(andExpr(gt(DoubleId(12.00)), le(IntId(174))), Variable{"?price"}), + std::make_pair(false, false), false); + testSetAndMakeScanWithPrefilterExpr( + kg, triple, Permutation::POS, 1, + pr(andExpr(gt(VocabId(0)), lt(VocabId(100))), Variable{"?x"}), + std::make_pair(false, false), false); + + // This knowledge graph yields an incomplete first and last block. + std::string kgFirstAndLastIncomplete = + " 10.00 . 12.00 . " + "12.001 . 21.99 . 24.33 . " + " 147.32 . 189.99 . 194.67 " + "."; + triple = {Tc{I::fromIriref("")}, "", Tc{Variable{"?price"}}}; + // The following test verifies that the prefilter procedure is successfully + // applicable under the condition that the first and last block are + // potentially incomplete. + testSetAndMakeScanWithPrefilterExpr( + kgFirstAndLastIncomplete, triple, Permutation::PSO, 2, + pr(orExpr(gt(IntId(100)), le(IntId(10))), Variable{"?price"}), + std::make_pair(true, true)); +} From 15748603e97891a74bb6b7e160f4eb046f6f5a22 Mon Sep 17 00:00:00 2001 From: realHannes Date: Wed, 27 Nov 2024 12:00:39 +0100 Subject: [PATCH 65/81] FilterTest fix + codespell fix --- test/FilterTest.cpp | 25 ++++++++----------------- test/engine/IndexScanTest.cpp | 2 +- 2 files changed, 9 insertions(+), 18 deletions(-) diff --git a/test/FilterTest.cpp b/test/FilterTest.cpp index c0a3802411..e6586061ea 100644 --- a/test/FilterTest.cpp +++ b/test/FilterTest.cpp @@ -8,9 +8,8 @@ #include "engine/Filter.h" #include "engine/ValuesForTesting.h" #include "engine/sparqlExpressions/LiteralExpression.h" -#include "engine/sparqlExpressions/RelationalExpressions.h" -#include "engine/sparqlExpressions/SparqlExpression.h" #include "engine/sparqlExpressions/NaryExpression.h" +#include "engine/sparqlExpressions/SparqlExpression.h" #include "util/IdTableHelpers.h" #include "util/IndexTestHelpers.h" @@ -34,14 +33,13 @@ std::vector toVector(Result::Generator generator) { return result; } -using namespace sparqlExpression; -using namespace prefilterExpressions; - +// _____________________________________________________________________________ void checkSetPrefilterExpressionVariablePair( QueryExecutionContext* qec, const Permutation::Enum& permutation, - SparqlTriple triple, std::unique_ptr sparqlExpr, - std::unique_ptr prefilterExpr, ColumnIndex columnIdx, - bool prefilterIsApplicable) { + SparqlTriple triple, + std::unique_ptr sparqlExpr, + std::unique_ptr prefilterExpr, + ColumnIndex columnIdx, bool prefilterIsApplicable) { Filter filter{ qec, ad_utility::makeExecutionTree(qec, permutation, triple), @@ -197,13 +195,9 @@ TEST(Filter, verifySetPrefilterExpressionVariablePairForIndexScanChild) { gtSprql(Variable{"?x"}, VocabId(10)), gt(VocabId(10)), 1, false); } -template -static constexpr auto make(Args&&... args) { - return std::make_unique(AD_FWD(args)...); -} - // _____________________________________________________________________________ TEST(Filter, lazyChildMaterializedResultBinaryFilter) { + using namespace makeSparqlExpression; QueryExecutionContext* qec = ad_utility::testing::getQec(); qec->getQueryTreeCache().clearAll(); std::vector idTables; @@ -214,10 +208,7 @@ TEST(Filter, lazyChildMaterializedResultBinaryFilter) { idTables.push_back(makeIdTableFromVector({{8}, {8}}, I)); auto varX = Variable{"?x"}; - using namespace sparqlExpression; - auto expr = makeUnaryNegateExpression( - make(std::array{ - make(varX), make(I(5))})); + auto expr = notSprqlExpr(ltSprql(varX, I(5))); ValuesForTesting values{ qec, std::move(idTables), {Variable{"?x"}}, false, {0}}; diff --git a/test/engine/IndexScanTest.cpp b/test/engine/IndexScanTest.cpp index 152ad71704..6741b5b037 100644 --- a/test/engine/IndexScanTest.cpp +++ b/test/engine/IndexScanTest.cpp @@ -289,7 +289,7 @@ void testSetAndMakeScanWithPrefilterExpr( std::move(clonedPair), firstLastIncomplete, evaluationColumn); } else { - // Check our predicition that the prefilter with the given + // Check our prediction that the prefilter with the given // pair is not applicable (no updated // QueryExecutionTree is returned). ASSERT_FALSE(prefilterCanBeSet); From 9de745a33fabf2fda626efb1cc9e52b2947ad983 Mon Sep 17 00:00:00 2001 From: realHannes Date: Wed, 27 Nov 2024 16:27:13 +0100 Subject: [PATCH 66/81] simplify test in IndexScanTest --- test/engine/IndexScanTest.cpp | 205 +++++++++++----------------------- 1 file changed, 63 insertions(+), 142 deletions(-) diff --git a/test/engine/IndexScanTest.cpp b/test/engine/IndexScanTest.cpp index 6741b5b037..38ae79109b 100644 --- a/test/engine/IndexScanTest.cpp +++ b/test/engine/IndexScanTest.cpp @@ -174,127 +174,42 @@ void testLazyScanWithColumnThrows( EXPECT_ANY_THROW(makeScan()); } -// _____________________________________________________________________________ -// Helper function to retrieve the corresponding column (`Id`) values from -// the provided `CompressedBlockMetadata` data as a vector. -// Note: A complete column which corresponds to an actual `IdTable` column can -// only be extracted if the `CompressedBlockMetadata` values adhere to the -// condition `numRows_ < 3` -std::vector getColumnFromBlockMetadata( - std::span blocks, - const size_t evaluationColumn, - const std::pair& firstLastIncomplete) { - auto getIdFromBlock = - [evaluationColumn]( - const CompressedBlockMetadata::PermutedTriple& triple) { - switch (evaluationColumn) { - case 0: - return triple.col0Id_; - case 1: - return triple.col1Id_; - case 2: - return triple.col2Id_; - default: - AD_FAIL(); - } - }; - - std::vector valueIds; - valueIds.reserve(blocks.size() * 2); - std::optional incompleteLast = std::nullopt; - - // Handle possibly incomplete first/last block. - if (firstLastIncomplete.first) { - valueIds.push_back(getIdFromBlock(blocks.front().lastTriple_)); - blocks = blocks.subspan(1); - } - if (firstLastIncomplete.second) { - incompleteLast = blocks.back(); - blocks = blocks.subspan(0, blocks.size() - 1); - } - - // Add the respective evaluation column IDs for the complete blocks. - for (const auto& block : blocks) { - valueIds.push_back(getIdFromBlock(block.firstTriple_)); - if (block.numRows_ > 1) { - valueIds.push_back(getIdFromBlock(block.lastTriple_)); - } - } - - if (incompleteLast.has_value()) { - // Given the last block is incomplete, only add the ID of the first triple. - valueIds.push_back(getIdFromBlock(incompleteLast.value().firstTriple_)); - } - return valueIds; -}; - //______________________________________________________________________________ // Check that the `IndexScan` computes correct prefiltered `IdTable`s w.r.t. -// the applied `PrefilterExpression`. -// This is implemented by comparing the relevant `IdTable` column with the -// evaluationColumn regarding the `CompressedBlockMetadata` values yielded by -// the `PrefilterExpression`. -void testScanResultWithAddedPrefiltering( - std::shared_ptr updatedQet, - IndexScan::PrefilterVariablePair prefilterVariablePair, - const std::pair& firstLastIncomplete, - const size_t evaluationColumn, - source_location l = source_location::current()) { - auto t = generateLocationTrace(l); - auto& [expr, variable] = prefilterVariablePair; - auto resultIndex = updatedQet->getVariableColumn(variable); - auto operationPtr = updatedQet->getRootOperation(); - std::shared_ptr indexScan = - std::dynamic_pointer_cast(operationPtr); - ASSERT_TRUE(indexScan); - auto metadata = - indexScan->getIndex() - .getImpl() - .getPermutation(indexScan->permutation()) - .getMetadataAndBlocks(indexScan->getScanSpecification(), - indexScan->locatedTriplesSnapshot()); - ASSERT_TRUE(metadata.has_value()); - auto blocks = - CompressedRelationReader::getBlocksFromMetadata(metadata.value()); - auto filteredBlocks = expr->evaluate(blocks, evaluationColumn); - auto idTableFiltered = - operationPtr->computeResultOnlyForTesting().idTable().clone(); - auto columnSpan = idTableFiltered.getColumn(resultIndex); - ASSERT_EQ((std::vector{columnSpan.begin(), columnSpan.end()}), - getColumnFromBlockMetadata(filteredBlocks, evaluationColumn, - firstLastIncomplete)); -} - -//______________________________________________________________________________ -void testSetAndMakeScanWithPrefilterExpr( - const std::string& kg, const SparqlTriple& triple, - const Permutation::Enum permutation, const size_t evaluationColumn, - IndexScan::PrefilterVariablePair prefilterVariablePair, - std::pair firstLastIncomplete, - const bool prefilterCanBeSet = true, - source_location l = source_location::current()) { - auto t = generateLocationTrace(l); - auto clonedPair = std::make_pair(prefilterVariablePair.first->clone(), - prefilterVariablePair.second); - IndexScan scan{getQec(kg), permutation, triple}; - auto optUpdatedQet = scan.setPrefilterGetUpdatedQueryExecutionTree( - makeFilterExpression::filterHelper::makePrefilterVec( - std::move(prefilterVariablePair))); - if (optUpdatedQet.has_value()) { - ASSERT_TRUE(prefilterCanBeSet); - // Check that the prefiltering procedure yields the correct result given - // that the pair is correctly assigned to - // IndexScan. - testScanResultWithAddedPrefiltering(optUpdatedQet.value(), - std::move(clonedPair), - firstLastIncomplete, evaluationColumn); - } else { - // Check our prediction that the prefilter with the given - // pair is not applicable (no updated - // QueryExecutionTree is returned). - ASSERT_FALSE(prefilterCanBeSet); - } -} +// the applied `PrefilterExpression` given a +// pair was successfully set. For convenience we assert this for the IdTable +// column on which the `PrefilterExpression` was applied. +const auto testSetAndMakeScanWithPrefilterExpr = + [](const std::string& kg, const SparqlTriple& triple, + const Permutation::Enum permutation, IndexScan::PrefilterVariablePair pr, + const IdTable& idTableWithExpectedColumn, bool prefilterCanBeSet = true, + source_location l = source_location::current()) { + auto t = generateLocationTrace(l); + IndexScan scan{getQec(kg), permutation, triple}; + auto optUpdatedQet = scan.setPrefilterGetUpdatedQueryExecutionTree( + makeFilterExpression::filterHelper::makePrefilterVec(std::move(pr))); + if (optUpdatedQet.has_value()) { + auto updatedQet = optUpdatedQet.value(); + ASSERT_TRUE(prefilterCanBeSet); + // Check that the prefiltering procedure yields the correct result given + // that the pair is correctly assigned + // to the IndexScan. + const IdTable& idTableFiltered = updatedQet->getRootOperation() + ->computeResultOnlyForTesting() + .idTable(); + auto isColumnIdSpan = idTableFiltered.getColumn(0); + auto expectedColumnIdSpan = idTableWithExpectedColumn.getColumn(0); + ASSERT_EQ( + (std::vector{isColumnIdSpan.begin(), isColumnIdSpan.end()}), + (std::vector{expectedColumnIdSpan.begin(), + expectedColumnIdSpan.end()})); + } else { + // Check our prediction that the prefilter with the given + // pair is not applicable (no updated + // QueryExecutionTree is returned). + ASSERT_FALSE(prefilterCanBeSet); + } + }; } // namespace @@ -782,60 +697,66 @@ TEST(IndexScan, checkEvaluationWithPrefiltering) { using namespace filterHelper; using I = TripleComponent::Iri; std::string kg = - " 10.00 . 12.00 . " - "12.001 . 21.99 . 24.33 . " - " 147.32 . 174.00 . 174.43 " - ". 189.99 . 194.67 ."; + " 10 . 12 . " + "18 . 22 . 25 . " + " 147 . 174 . 174 " + ". 189 . 194 ."; SparqlTriple triple{Tc{Variable{"?x"}}, "", Tc{Variable{"?price"}}}; // For the following tests, the pair is set // and applied for the respective IndexScan. - testSetAndMakeScanWithPrefilterExpr(kg, triple, Permutation::POS, 1, - pr(ge(IntId(10)), Variable{"?price"}), - std::make_pair(false, false)); testSetAndMakeScanWithPrefilterExpr( - kg, triple, Permutation::POS, 1, + kg, triple, Permutation::POS, pr(ge(IntId(10)), Variable{"?price"}), + makeIdTableFromVector( + {{10}, {12}, {18}, {22}, {25}, {147}, {174}, {174}, {189}, {194}}, + IntId)); + testSetAndMakeScanWithPrefilterExpr( + kg, triple, Permutation::POS, pr(lt(DoubleId(147.32)), Variable{"?price"}), - std::make_pair(false, false)); + makeIdTableFromVector({{10}, {12}, {18}, {22}, {25}, {147}}, IntId)); testSetAndMakeScanWithPrefilterExpr( - kg, triple, Permutation::POS, 1, + kg, triple, Permutation::POS, pr(andExpr(gt(DoubleId(12.00)), le(IntId(174))), Variable{"?price"}), - std::make_pair(false, false)); + makeIdTableFromVector({{18}, {22}, {25}, {147}, {174}, {174}}, IntId)); // For the following test, the Variable value doesn't match any of the scan // triple Variable values. We expect that the prefilter is not applicable (=> // set bool flag to false). testSetAndMakeScanWithPrefilterExpr( - kg, triple, Permutation::POS, 1, + kg, triple, Permutation::POS, pr(andExpr(gt(DoubleId(12.00)), le(IntId(174))), Variable{"?y"}), - std::make_pair(false, false), false); + makeIdTableFromVector({{0}}, IntId), false); // For the following tests, the first sorted column given the permutation // doesn't match with the corresponding column for the Variable of the // pair. We expect that the provided prefilter // is not applicable (and can't be set). testSetAndMakeScanWithPrefilterExpr( - kg, triple, Permutation::PSO, 1, + kg, triple, Permutation::PSO, pr(andExpr(gt(DoubleId(12.00)), le(IntId(174))), Variable{"?price"}), - std::make_pair(false, false), false); + makeIdTableFromVector({{0}}, IntId), false); testSetAndMakeScanWithPrefilterExpr( - kg, triple, Permutation::POS, 1, + kg, triple, Permutation::POS, pr(andExpr(gt(VocabId(0)), lt(VocabId(100))), Variable{"?x"}), - std::make_pair(false, false), false); + makeIdTableFromVector({{0}}, IntId), false); // This knowledge graph yields an incomplete first and last block. std::string kgFirstAndLastIncomplete = - " 10.00 . 12.00 . " - "12.001 . 21.99 . 24.33 . " - " 147.32 . 189.99 . 194.67 " + " 10 . 12 . " + "18 . 22 . 25 . " + " 147 . 189 . 194 " "."; - triple = {Tc{I::fromIriref("")}, "", Tc{Variable{"?price"}}}; + triple = {I::fromIriref(""), "", Tc{Variable{"?price"}}}; // The following test verifies that the prefilter procedure is successfully // applicable under the condition that the first and last block are // potentially incomplete. testSetAndMakeScanWithPrefilterExpr( - kgFirstAndLastIncomplete, triple, Permutation::PSO, 2, + kgFirstAndLastIncomplete, triple, Permutation::PSO, pr(orExpr(gt(IntId(100)), le(IntId(10))), Variable{"?price"}), - std::make_pair(true, true)); + makeIdTableFromVector({{12}, {25}, {147}, {189}}, IntId)); + testSetAndMakeScanWithPrefilterExpr( + kgFirstAndLastIncomplete, triple, Permutation::SPO, + pr(andExpr(gt(IntId(10)), lt(IntId(194))), Variable{"?price"}), + makeIdTableFromVector({{12}, {18}, {22}, {25}, {147}, {189}}, IntId)); } From 0a7672cd44c1ef829603ca6f0acf993581df06d3 Mon Sep 17 00:00:00 2001 From: realHannes Date: Thu, 28 Nov 2024 18:35:44 +0100 Subject: [PATCH 67/81] more changes --- src/engine/Filter.cpp | 4 ++-- src/engine/Filter.h | 8 ++++---- src/engine/IndexScan.cpp | 16 +++++++++++----- src/engine/IndexScan.h | 5 +++-- src/engine/Operation.h | 3 ++- src/engine/QueryExecutionTree.cpp | 4 ++-- src/engine/QueryExecutionTree.h | 2 +- test/engine/IndexScanTest.cpp | 15 ++++++++------- 8 files changed, 33 insertions(+), 24 deletions(-) diff --git a/src/engine/Filter.cpp b/src/engine/Filter.cpp index 7e0c66b551..3de901d3e7 100644 --- a/src/engine/Filter.cpp +++ b/src/engine/Filter.cpp @@ -48,8 +48,8 @@ string Filter::getDescriptor() const { void Filter::setPrefilterExpressionForChildren() { std::vector prefilterPairs = _expression.getPrefilterExpressionForMetadata(); - auto optNewSubTree = _subtree->setPrefilterGetUpdatedQueryExecutionTree( - std::move(prefilterPairs)); + auto optNewSubTree = + _subtree->setPrefilterGetUpdatedQueryExecutionTree(prefilterPairs); if (optNewSubTree.has_value()) { _subtree = std::move(optNewSubTree.value()); } diff --git a/src/engine/Filter.h b/src/engine/Filter.h index 440751c09b..2ac01e1845 100644 --- a/src/engine/Filter.h +++ b/src/engine/Filter.h @@ -61,10 +61,10 @@ class Filter : public Operation { // The method is directly invoked with the construction of this `Filter` // object. Its implementation retrieves pairs - // from the corresponding `SparqlExpression` and calls - // `QueryExecutionTree::setPrefilterGetUpdatedQueryExecutionTree()` on those - // pair values. If necessary the `QueryExecutionTree` for this entity will be - // updated. + // from the corresponding `SparqlExpression` and uses them to call + // `QueryExecutionTree::setPrefilterGetUpdatedQueryExecutionTree()` on the + // `subtree_`. If necessary the `QueryExecutionTree` for this + // entity will be updated. void setPrefilterExpressionForChildren(); ProtoResult computeResult(bool requestLaziness) override; diff --git a/src/engine/IndexScan.cpp b/src/engine/IndexScan.cpp index 18c7399271..09d6b15de7 100644 --- a/src/engine/IndexScan.cpp +++ b/src/engine/IndexScan.cpp @@ -157,7 +157,7 @@ vector IndexScan::resultSortedOn() const { // _____________________________________________________________________________ std::optional> IndexScan::setPrefilterGetUpdatedQueryExecutionTree( - std::vector prefilterVariablePairs) { + const std::vector& prefilterVariablePairs) const { auto optSortedVarColIdxPair = getSortedVariableAndMetadataColumnIndexForPrefiltering(); if (!optSortedVarColIdxPair.has_value()) { @@ -342,15 +342,15 @@ IndexScan::getBlockMetadataOptionallyPrefiltered() const { if (optBlockSpan.has_value()) { const auto& blockSpan = optBlockSpan.value(); optBlocks = {blockSpan.begin(), blockSpan.end()}; - applyPefilterIfPossible(optBlocks.value()); + applyPrefilterIfPossible(optBlocks.value()); } return optBlocks; } // _____________________________________________________________________________ -void IndexScan::applyPefilterIfPossible( +void IndexScan::applyPrefilterIfPossible( std::vector& blocks) const { - if (prefilter_.has_value() && getLimit().isUnconstrained()) { + if (prefilter_.has_value()) { // Apply the prefilter on given blocks. auto& [prefilterExpr, columnIndex] = prefilter_.value(); blocks = prefilterExpr->evaluate(blocks, columnIndex); @@ -360,7 +360,6 @@ void IndexScan::applyPefilterIfPossible( // _____________________________________________________________________________ Permutation::IdTableGenerator IndexScan::getLazyScan( std::vector blocks) const { - applyPefilterIfPossible(blocks); // If there is a LIMIT or OFFSET clause that constrains the scan // (which can happen with an explicit subquery), we cannot use the prefiltered // blocks, as we currently have no mechanism to include limits and offsets @@ -368,6 +367,13 @@ Permutation::IdTableGenerator IndexScan::getLazyScan( auto filteredBlocks = getLimit().isUnconstrained() ? std::optional(std::move(blocks)) : std::nullopt; + if (filteredBlocks.has_value()) { + // Note: The prefilter expression applied with applyPrefilterIfPossible() + // is not related to the prefilter procedure mentioned in the comment above. + // If this IndexScan owns a pair, it can + // be applied. + applyPrefilterIfPossible(filteredBlocks.value()); + } return getScanPermutation().lazyScan(getScanSpecification(), filteredBlocks, additionalColumns(), cancellationHandle_, locatedTriplesSnapshot(), getLimit()); diff --git a/src/engine/IndexScan.h b/src/engine/IndexScan.h index 15195a141a..c5757dfe11 100644 --- a/src/engine/IndexScan.h +++ b/src/engine/IndexScan.h @@ -76,7 +76,8 @@ class IndexScan final : public Operation { // if necessary. std::optional> setPrefilterGetUpdatedQueryExecutionTree( - std::vector prefilterVariablePairs) override; + const std::vector& prefilterVariablePairs) + const override; size_t numVariables() const { return numVariables_; } @@ -195,7 +196,7 @@ class IndexScan final : public Operation { // If `isUnconstrained()` yields true, return the blocks as given or the // prefiltered blocks (if `prefilter_` has value). If `isUnconstrained()` is // false, return `std::nullopt`. - void applyPefilterIfPossible( + void applyPrefilterIfPossible( std::vector& blocks) const; // Helper functions for the public `getLazyScanFor...` methods and diff --git a/src/engine/Operation.h b/src/engine/Operation.h index 50da76951b..af5eff7350 100644 --- a/src/engine/Operation.h +++ b/src/engine/Operation.h @@ -93,7 +93,8 @@ class Operation { // `IndexScan` in their subtree. virtual std::optional> setPrefilterGetUpdatedQueryExecutionTree( - [[maybe_unused]] std::vector prefilterPairs) { + [[maybe_unused]] const std::vector& prefilterPairs) + const { return std::nullopt; }; diff --git a/src/engine/QueryExecutionTree.cpp b/src/engine/QueryExecutionTree.cpp index d8723f6107..eeff0f09e8 100644 --- a/src/engine/QueryExecutionTree.cpp +++ b/src/engine/QueryExecutionTree.cpp @@ -107,7 +107,7 @@ size_t QueryExecutionTree::getSizeEstimate() { //_____________________________________________________________________________ std::optional> QueryExecutionTree::setPrefilterGetUpdatedQueryExecutionTree( - std::vector prefilterPairs) const { + std::vector& prefilterPairs) const { AD_CONTRACT_CHECK(rootOperation_); VariableToColumnMap varToColMap = getVariableColumns(); std::erase_if(prefilterPairs, [&varToColMap](const auto& pair) { @@ -118,7 +118,7 @@ QueryExecutionTree::setPrefilterGetUpdatedQueryExecutionTree( return std::nullopt; } else { return rootOperation_->setPrefilterGetUpdatedQueryExecutionTree( - std::move(prefilterPairs)); + prefilterPairs); } } diff --git a/src/engine/QueryExecutionTree.h b/src/engine/QueryExecutionTree.h index 0eac785f16..14e0766df2 100644 --- a/src/engine/QueryExecutionTree.h +++ b/src/engine/QueryExecutionTree.h @@ -100,7 +100,7 @@ class QueryExecutionTree { // the corresponding `Variable` is visible in the `VariableToColumnMap`. std::optional> setPrefilterGetUpdatedQueryExecutionTree( - std::vector prefilterPairs) const; + std::vector& prefilterPairs) const; size_t getDistinctEstimate(size_t col) const { return static_cast(rootOperation_->getSizeEstimate() / diff --git a/test/engine/IndexScanTest.cpp b/test/engine/IndexScanTest.cpp index 38ae79109b..596f30ccfd 100644 --- a/test/engine/IndexScanTest.cpp +++ b/test/engine/IndexScanTest.cpp @@ -186,6 +186,7 @@ const auto testSetAndMakeScanWithPrefilterExpr = source_location l = source_location::current()) { auto t = generateLocationTrace(l); IndexScan scan{getQec(kg), permutation, triple}; + auto variable = pr.second; auto optUpdatedQet = scan.setPrefilterGetUpdatedQueryExecutionTree( makeFilterExpression::filterHelper::makePrefilterVec(std::move(pr))); if (optUpdatedQet.has_value()) { @@ -197,7 +198,8 @@ const auto testSetAndMakeScanWithPrefilterExpr = const IdTable& idTableFiltered = updatedQet->getRootOperation() ->computeResultOnlyForTesting() .idTable(); - auto isColumnIdSpan = idTableFiltered.getColumn(0); + auto isColumnIdSpan = + idTableFiltered.getColumn(updatedQet->getVariableColumn(variable)); auto expectedColumnIdSpan = idTableWithExpectedColumn.getColumn(0); ASSERT_EQ( (std::vector{isColumnIdSpan.begin(), isColumnIdSpan.end()}), @@ -695,7 +697,6 @@ TEST(IndexScan, SetPrefilterVariablePairAndCheckCacheKey) { TEST(IndexScan, checkEvaluationWithPrefiltering) { using namespace makeFilterExpression; using namespace filterHelper; - using I = TripleComponent::Iri; std::string kg = " 10 . 12 . " "18 . 22 . 25 . " @@ -747,16 +748,16 @@ TEST(IndexScan, checkEvaluationWithPrefiltering) { "18 . 22 . 25 . " " 147 . 189 . 194 " "."; - triple = {I::fromIriref(""), "", Tc{Variable{"?price"}}}; // The following test verifies that the prefilter procedure is successfully // applicable under the condition that the first and last block are // potentially incomplete. testSetAndMakeScanWithPrefilterExpr( - kgFirstAndLastIncomplete, triple, Permutation::PSO, + kgFirstAndLastIncomplete, triple, Permutation::POS, pr(orExpr(gt(IntId(100)), le(IntId(10))), Variable{"?price"}), - makeIdTableFromVector({{12}, {25}, {147}, {189}}, IntId)); + makeIdTableFromVector({{10}, {12}, {25}, {147}, {189}, {194}}, IntId)); testSetAndMakeScanWithPrefilterExpr( - kgFirstAndLastIncomplete, triple, Permutation::SPO, + kgFirstAndLastIncomplete, triple, Permutation::POS, pr(andExpr(gt(IntId(10)), lt(IntId(194))), Variable{"?price"}), - makeIdTableFromVector({{12}, {18}, {22}, {25}, {147}, {189}}, IntId)); + makeIdTableFromVector({{10}, {12}, {18}, {22}, {25}, {147}, {189}, {194}}, + IntId)); } From 0ff8689784ab0a3dfc2646674207ba29ec5069bc Mon Sep 17 00:00:00 2001 From: realHannes Date: Thu, 28 Nov 2024 23:12:24 +0100 Subject: [PATCH 68/81] test --- test/engine/IndexScanTest.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/engine/IndexScanTest.cpp b/test/engine/IndexScanTest.cpp index 596f30ccfd..861d7cdfad 100644 --- a/test/engine/IndexScanTest.cpp +++ b/test/engine/IndexScanTest.cpp @@ -742,6 +742,7 @@ TEST(IndexScan, checkEvaluationWithPrefiltering) { pr(andExpr(gt(VocabId(0)), lt(VocabId(100))), Variable{"?x"}), makeIdTableFromVector({{0}}, IntId), false); + /* // This knowledge graph yields an incomplete first and last block. std::string kgFirstAndLastIncomplete = " 10 . 12 . " @@ -760,4 +761,5 @@ TEST(IndexScan, checkEvaluationWithPrefiltering) { pr(andExpr(gt(IntId(10)), lt(IntId(194))), Variable{"?price"}), makeIdTableFromVector({{10}, {12}, {18}, {22}, {25}, {147}, {189}, {194}}, IntId)); + */ } From 6e54939a4f98f1591d8a7099191435b13c50d5b4 Mon Sep 17 00:00:00 2001 From: realHannes Date: Fri, 29 Nov 2024 10:19:01 +0100 Subject: [PATCH 69/81] fix test error --- test/engine/IndexScanTest.cpp | 46 ++++++++++++++++------------------- 1 file changed, 21 insertions(+), 25 deletions(-) diff --git a/test/engine/IndexScanTest.cpp b/test/engine/IndexScanTest.cpp index 861d7cdfad..33f056994a 100644 --- a/test/engine/IndexScanTest.cpp +++ b/test/engine/IndexScanTest.cpp @@ -182,7 +182,8 @@ void testLazyScanWithColumnThrows( const auto testSetAndMakeScanWithPrefilterExpr = [](const std::string& kg, const SparqlTriple& triple, const Permutation::Enum permutation, IndexScan::PrefilterVariablePair pr, - const IdTable& idTableWithExpectedColumn, bool prefilterCanBeSet = true, + const std::vector& expectedIdsOnFilterColumn, + bool prefilterCanBeSet = true, source_location l = source_location::current()) { auto t = generateLocationTrace(l); IndexScan scan{getQec(kg), permutation, triple}; @@ -195,16 +196,15 @@ const auto testSetAndMakeScanWithPrefilterExpr = // Check that the prefiltering procedure yields the correct result given // that the pair is correctly assigned // to the IndexScan. - const IdTable& idTableFiltered = updatedQet->getRootOperation() - ->computeResultOnlyForTesting() - .idTable(); + IdTable idTableFiltered = updatedQet->getRootOperation() + ->computeResultOnlyForTesting() + .idTable() + .clone(); auto isColumnIdSpan = idTableFiltered.getColumn(updatedQet->getVariableColumn(variable)); - auto expectedColumnIdSpan = idTableWithExpectedColumn.getColumn(0); ASSERT_EQ( (std::vector{isColumnIdSpan.begin(), isColumnIdSpan.end()}), - (std::vector{expectedColumnIdSpan.begin(), - expectedColumnIdSpan.end()})); + expectedIdsOnFilterColumn); } else { // Check our prediction that the prefilter with the given // pair is not applicable (no updated @@ -697,6 +697,7 @@ TEST(IndexScan, SetPrefilterVariablePairAndCheckCacheKey) { TEST(IndexScan, checkEvaluationWithPrefiltering) { using namespace makeFilterExpression; using namespace filterHelper; + auto I = ad_utility::testing::IntId; std::string kg = " 10 . 12 . " "18 . 22 . 25 . " @@ -707,27 +708,26 @@ TEST(IndexScan, checkEvaluationWithPrefiltering) { // For the following tests, the pair is set // and applied for the respective IndexScan. - testSetAndMakeScanWithPrefilterExpr( - kg, triple, Permutation::POS, pr(ge(IntId(10)), Variable{"?price"}), - makeIdTableFromVector( - {{10}, {12}, {18}, {22}, {25}, {147}, {174}, {174}, {189}, {194}}, - IntId)); + testSetAndMakeScanWithPrefilterExpr(kg, triple, Permutation::POS, + pr(ge(IntId(10)), Variable{"?price"}), + {I(10), I(12), I(18), I(22), I(25), + I(147), I(174), I(174), I(189), I(194)}); testSetAndMakeScanWithPrefilterExpr( kg, triple, Permutation::POS, pr(lt(DoubleId(147.32)), Variable{"?price"}), - makeIdTableFromVector({{10}, {12}, {18}, {22}, {25}, {147}}, IntId)); + {I(10), I(12), I(18), I(22), I(25), I(147)}); testSetAndMakeScanWithPrefilterExpr( kg, triple, Permutation::POS, pr(andExpr(gt(DoubleId(12.00)), le(IntId(174))), Variable{"?price"}), - makeIdTableFromVector({{18}, {22}, {25}, {147}, {174}, {174}}, IntId)); + {I(18), I(22), I(25), I(147), I(174), I(174)}); // For the following test, the Variable value doesn't match any of the scan // triple Variable values. We expect that the prefilter is not applicable (=> // set bool flag to false). testSetAndMakeScanWithPrefilterExpr( kg, triple, Permutation::POS, - pr(andExpr(gt(DoubleId(12.00)), le(IntId(174))), Variable{"?y"}), - makeIdTableFromVector({{0}}, IntId), false); + pr(andExpr(gt(DoubleId(12.00)), le(IntId(174))), Variable{"?y"}), {}, + false); // For the following tests, the first sorted column given the permutation // doesn't match with the corresponding column for the Variable of the @@ -735,14 +735,12 @@ TEST(IndexScan, checkEvaluationWithPrefiltering) { // is not applicable (and can't be set). testSetAndMakeScanWithPrefilterExpr( kg, triple, Permutation::PSO, - pr(andExpr(gt(DoubleId(12.00)), le(IntId(174))), Variable{"?price"}), - makeIdTableFromVector({{0}}, IntId), false); + pr(andExpr(gt(DoubleId(12.00)), le(IntId(174))), Variable{"?price"}), {}, + false); testSetAndMakeScanWithPrefilterExpr( kg, triple, Permutation::POS, - pr(andExpr(gt(VocabId(0)), lt(VocabId(100))), Variable{"?x"}), - makeIdTableFromVector({{0}}, IntId), false); + pr(andExpr(gt(VocabId(0)), lt(VocabId(100))), Variable{"?x"}), {}, false); - /* // This knowledge graph yields an incomplete first and last block. std::string kgFirstAndLastIncomplete = " 10 . 12 . " @@ -755,11 +753,9 @@ TEST(IndexScan, checkEvaluationWithPrefiltering) { testSetAndMakeScanWithPrefilterExpr( kgFirstAndLastIncomplete, triple, Permutation::POS, pr(orExpr(gt(IntId(100)), le(IntId(10))), Variable{"?price"}), - makeIdTableFromVector({{10}, {12}, {25}, {147}, {189}, {194}}, IntId)); + {I(10), I(12), I(25), I(147), I(189), I(194)}); testSetAndMakeScanWithPrefilterExpr( kgFirstAndLastIncomplete, triple, Permutation::POS, pr(andExpr(gt(IntId(10)), lt(IntId(194))), Variable{"?price"}), - makeIdTableFromVector({{10}, {12}, {18}, {22}, {25}, {147}, {189}, {194}}, - IntId)); - */ + {I(10), I(12), I(18), I(22), I(25), I(147), I(189), I(194)}); } From 2751fe1b4df01320baf49e2c832849ab2154438b Mon Sep 17 00:00:00 2001 From: realHannes Date: Fri, 29 Nov 2024 10:38:10 +0100 Subject: [PATCH 70/81] adapt code part merge conflict --- src/index/Permutation.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/index/Permutation.cpp b/src/index/Permutation.cpp index 7b9ff7d567..0c18d550d9 100644 --- a/src/index/Permutation.cpp +++ b/src/index/Permutation.cpp @@ -67,7 +67,10 @@ IdTable Permutation::scan( const auto& p = getActualPermutation(scanSpec); return p.reader().scan( - scanSpec, p.getAugmentedMetadataForPermutation(locatedTriplesSnapshot), + scanSpec, + blocks.has_value() + ? blocks.value() + : p.getAugmentedMetadataForPermutation(locatedTriplesSnapshot), additionalColumns, cancellationHandle, p.getLocatedTriplesForPermutation(locatedTriplesSnapshot), limitOffset); } @@ -79,7 +82,10 @@ size_t Permutation::getResultSizeOfScan( std::optional> blocks) const { const auto& p = getActualPermutation(scanSpec); return p.reader().getResultSizeOfScan( - scanSpec, p.getAugmentedMetadataForPermutation(locatedTriplesSnapshot), + scanSpec, + blocks.has_value() + ? blocks.value() + : p.getAugmentedMetadataForPermutation(locatedTriplesSnapshot), p.getLocatedTriplesForPermutation(locatedTriplesSnapshot)); } @@ -90,7 +96,10 @@ std::pair Permutation::getSizeEstimateForScan( std::optional> blocks) const { const auto& p = getActualPermutation(scanSpec); return p.reader().getSizeEstimateForScan( - scanSpec, p.getAugmentedMetadataForPermutation(locatedTriplesSnapshot), + scanSpec, + blocks.has_value() + ? blocks.value() + : p.getAugmentedMetadataForPermutation(locatedTriplesSnapshot), p.getLocatedTriplesForPermutation(locatedTriplesSnapshot)); } From 406e323a380608a50833569e0589d4919c457ba0 Mon Sep 17 00:00:00 2001 From: realHannes Date: Sat, 30 Nov 2024 18:18:37 +0100 Subject: [PATCH 71/81] implement use PrefilterExpression for Iri and Literals (1) --- .../sparqlExpressions/LiteralExpression.h | 31 ++++++ .../PrefilterExpressionIndex.cpp | 105 ++++++++++++++++-- .../PrefilterExpressionIndex.h | 16 ++- .../RelationalExpressions.cpp | 65 +++-------- .../sparqlExpressions/StringExpressions.cpp | 35 +++--- ...lterExpressionFromSparqlExpressionTest.cpp | 2 - 6 files changed, 170 insertions(+), 84 deletions(-) diff --git a/src/engine/sparqlExpressions/LiteralExpression.h b/src/engine/sparqlExpressions/LiteralExpression.h index c67bfb1bb9..5f6e992dc1 100644 --- a/src/engine/sparqlExpressions/LiteralExpression.h +++ b/src/engine/sparqlExpressions/LiteralExpression.h @@ -213,6 +213,7 @@ struct SingleUseExpression : public SparqlExpression { std::span childrenImpl() override { return {}; } }; + } // namespace detail /// The actual instantiations and aliases of LiteralExpressions. @@ -224,4 +225,34 @@ using IdExpression = detail::LiteralExpression; using VectorIdExpression = detail::LiteralExpression>; using SingleUseExpression = detail::SingleUseExpression; + +namespace detail { + +//______________________________________________________________________________ +using IdOrLocalVocabEntry = prefilterExpressions::IdOrLocalVocabEntry; +// Given a `SparqlExpression*` pointing to a `LiteralExpression`, this helper +// function retrieves a corresponding `IdOrLocalVocabEntry` variant +// (`std::variant`) for `LiteralExpression`s that +// contain a suitable type. +inline std::optional +getIdOrLocalVocabEntryFromLiteralExpression(const SparqlExpression* child, + bool stringAndIriOnly = false) { + using enum Datatype; + if (const auto* idExpr = dynamic_cast(child)) { + auto idType = idExpr->value().getDatatype(); + if (stringAndIriOnly && idType != VocabIndex && idType != LocalVocabIndex) { + return std::nullopt; + } + return idExpr->value(); + } else if (const auto* literalExpr = + dynamic_cast(child)) { + return LocalVocabEntry{literalExpr->value()}; + } else if (const auto* iriExpr = dynamic_cast(child)) { + return LocalVocabEntry{iriExpr->value()}; + } else { + return std::nullopt; + } +} +} // namespace detail + } // namespace sparqlExpression diff --git a/src/engine/sparqlExpressions/PrefilterExpressionIndex.cpp b/src/engine/sparqlExpressions/PrefilterExpressionIndex.cpp index eb00fd466f..9ba0409874 100644 --- a/src/engine/sparqlExpressions/PrefilterExpressionIndex.cpp +++ b/src/engine/sparqlExpressions/PrefilterExpressionIndex.cpp @@ -51,6 +51,19 @@ static auto getMaskedTriple(const BlockMetadata::PermutedTriple& triple, } }; +//______________________________________________________________________________ +// Retrieve the reference `ValueId` from `IdOrLocalVocabEntry` +static const ValueId getValueIdFromReferenceValue( + const IdOrLocalVocabEntry& referenceValue, LocalVocab localVocab = {}) { + if (std::holds_alternative(referenceValue)) { + return std::get(referenceValue); + } else { + assert(std::holds_alternative(referenceValue)); + return Id::makeFromLocalVocabIndex(localVocab.getIndexAndAddIfNotContained( + std::get(referenceValue))); + } +}; + //______________________________________________________________________________ // Check for constant values in all columns `< evaluationColumn` static bool checkBlockIsInconsistent(const BlockMetadata& block, @@ -213,25 +226,26 @@ template std::unique_ptr RelationalExpression::logicalComplement() const { using enum CompOp; + auto referenceId = getValueIdFromReferenceValue(referenceValue_); switch (Comparison) { case LT: // Complement X < Y: X >= Y - return std::make_unique(referenceId_); + return std::make_unique(referenceId); case LE: // Complement X <= Y: X > Y - return std::make_unique(referenceId_); + return std::make_unique(referenceId); case EQ: // Complement X == Y: X != Y - return std::make_unique(referenceId_); + return std::make_unique(referenceId); case NE: // Complement X != Y: X == Y - return std::make_unique(referenceId_); + return std::make_unique(referenceId); case GE: // Complement X >= Y: X < Y - return std::make_unique(referenceId_); + return std::make_unique(referenceId); case GT: // Complement X > Y: X <= Y - return std::make_unique(referenceId_); + return std::make_unique(referenceId); default: AD_FAIL(); } @@ -261,18 +275,19 @@ std::vector RelationalExpression::evaluateImpl( } } + auto referenceId = getValueIdFromReferenceValue(referenceValue_); // Use getRangesForId (from valueIdComparators) to extract the ranges // containing the relevant ValueIds. // For pre-filtering with CompOp::EQ, we have to consider empty ranges. - // Reason: The referenceId_ could be contained within the bounds formed by + // Reason: The referenceId could be contained within the bounds formed by // the IDs of firstTriple_ and lastTriple_ (set false flag to keep // empty ranges). auto relevantIdRanges = Comparison != CompOp::EQ ? getRangesForId(valueIdsInput.begin(), valueIdsInput.end(), - referenceId_, Comparison) + referenceId, Comparison) : getRangesForId(valueIdsInput.begin(), valueIdsInput.end(), - referenceId_, Comparison, false); + referenceId, Comparison, false); // The vector for relevant BlockMetadata values which contain ValueIds // defined as relevant by relevantIdRanges. @@ -312,14 +327,15 @@ bool RelationalExpression::operator==( if (!otherRelational) { return false; } - return referenceId_ == otherRelational->referenceId_; + return getValueIdFromReferenceValue(referenceValue_) == + getValueIdFromReferenceValue(otherRelational->referenceValue_); }; //______________________________________________________________________________ template std::unique_ptr RelationalExpression::clone() const { - return std::make_unique>(referenceId_); + return std::make_unique>(referenceValue_); }; //______________________________________________________________________________ @@ -328,7 +344,8 @@ std::string RelationalExpression::asString( [[maybe_unused]] size_t depth) const { std::stringstream stream; stream << "Prefilter RelationalExpression<" << getRelationalOpStr(Comparison) - << ">\nValueId: " << referenceId_ << std::endl; + << ">\nValueId: " << getValueIdFromReferenceValue(referenceValue_) + << std::endl; return stream.str(); }; @@ -456,6 +473,36 @@ template class LogicalExpression; namespace detail { +//______________________________________________________________________________ +template +static std::unique_ptr makeMirroredExpression( + const IdOrLocalVocabEntry& referenceValue) { + using enum CompOp; + switch (comparison) { + case LT: + // Id < ?var -> ?var > Id + return std::make_unique(referenceValue); + case LE: + // Id <= ?var -> ?var >= Id + return std::make_unique(referenceValue); + case GE: + // Id >= ?var -> ?var <= Id + return std::make_unique(referenceValue); + case GT: + // Id > ?var -> ?var < Id + return std::make_unique(referenceValue); + case EQ: + case NE: + // EQ(==) or NE(!=) + // Given that these two relations are symmetric w.r.t. ?var and Id, + // no swap regarding the relational operator is necessary. + return std::make_unique>(referenceValue); + default: + // Unchecked / new valueIdComparators::Comparison case. + AD_FAIL(); + } +} + //______________________________________________________________________________ void checkPropertiesForPrefilterConstruction( const std::vector& vec) { @@ -473,5 +520,39 @@ void checkPropertiesForPrefilterConstruction( } } +//______________________________________________________________________________ +template +std::vector makePrefilterExpressionVec( + const IdOrLocalVocabEntry& referenceValue, const Variable& variable, + const bool reversed) { + std::vector resVec{}; + resVec.emplace_back( + reversed + ? makeMirroredExpression(referenceValue) + : std::make_unique>(referenceValue), + variable); + return resVec; +} + +//______________________________________________________________________________ +template std::vector +makePrefilterExpressionVec(const IdOrLocalVocabEntry&, + const Variable&, const bool); +template std::vector +makePrefilterExpressionVec(const IdOrLocalVocabEntry&, + const Variable&, const bool); +template std::vector +makePrefilterExpressionVec(const IdOrLocalVocabEntry&, + const Variable&, const bool); +template std::vector +makePrefilterExpressionVec(const IdOrLocalVocabEntry&, + const Variable&, const bool); +template std::vector +makePrefilterExpressionVec(const IdOrLocalVocabEntry&, + const Variable&, const bool); +template std::vector +makePrefilterExpressionVec(const IdOrLocalVocabEntry&, + const Variable&, const bool); + } // namespace detail } // namespace prefilterExpressions diff --git a/src/engine/sparqlExpressions/PrefilterExpressionIndex.h b/src/engine/sparqlExpressions/PrefilterExpressionIndex.h index 8b73a720ca..6cea485efd 100644 --- a/src/engine/sparqlExpressions/PrefilterExpressionIndex.h +++ b/src/engine/sparqlExpressions/PrefilterExpressionIndex.h @@ -23,6 +23,8 @@ namespace prefilterExpressions { +using IdOrLocalVocabEntry = std::variant; + //______________________________________________________________________________ // The maximum recursion depth for `info()` / `operator<<()`. A depth of `3` // should be sufficient for most `PrefilterExpressions` in our use case. @@ -130,12 +132,12 @@ using CompOp = valueIdComparators::Comparison; template class RelationalExpression : public PrefilterExpression { private: - // The ValueId on which we perform the relational comparison on. - ValueId referenceId_; + // The value that represents our reference value for filtering. + IdOrLocalVocabEntry referenceValue_; public: - explicit RelationalExpression(const ValueId referenceId) - : referenceId_(referenceId) {} + explicit RelationalExpression(const IdOrLocalVocabEntry& referenceValue) + : referenceValue_(referenceValue) {} std::unique_ptr logicalComplement() const override; bool operator==(const PrefilterExpression& other) const override; @@ -239,5 +241,11 @@ using PrefilterExprVariablePair = void checkPropertiesForPrefilterConstruction( const std::vector& vec); +//______________________________________________________________________________ +template +std::vector makePrefilterExpressionVec( + const IdOrLocalVocabEntry& referenceValue, const Variable& variable, + const bool reversed); + } // namespace detail } // namespace prefilterExpressions diff --git a/src/engine/sparqlExpressions/RelationalExpressions.cpp b/src/engine/sparqlExpressions/RelationalExpressions.cpp index 6e027fac87..5f744a6562 100644 --- a/src/engine/sparqlExpressions/RelationalExpressions.cpp +++ b/src/engine/sparqlExpressions/RelationalExpressions.cpp @@ -418,58 +418,29 @@ template std::vector RelationalExpression::getPrefilterExpressionForMetadata( [[maybe_unused]] bool isNegated) const { - namespace p = prefilterExpressions; AD_CORRECTNESS_CHECK(children_.size() == 2); const SparqlExpression* child0 = children_.at(0).get(); const SparqlExpression* child1 = children_.at(1).get(); - const auto mirroredExpression = - [](const ValueId valueId) -> std::unique_ptr { - using enum Comparison; - switch (comp) { - case LT: - // Id < ?var -> ?var > Id - return std::make_unique(valueId); - case LE: - // Id <= ?var -> ?var >= Id - return std::make_unique(valueId); - case GE: - // Id >= ?var -> ?var <= Id - return std::make_unique(valueId); - case GT: - // Id > ?var -> ?var < Id - return std::make_unique(valueId); - case EQ: - case NE: - // EQ(==) or NE(!=) - // Given that these two relations are symmetric w.r.t. ?var and Id, - // no swap regarding the relational operator is necessary. - return std::make_unique>(valueId); - default: - // Unchecked / new valueIdComparators::Comparison case. - AD_FAIL(); + const auto tryGetPrefilterExprVariablePairVec = + [](const SparqlExpression* child0, const SparqlExpression* child1, + bool reversed) -> std::vector { + const auto* variableExpr = dynamic_cast(child0); + if (!variableExpr) { + return {}; } - }; - const auto tryGetPrefilterExprVariablePairVec = - [&mirroredExpression](const SparqlExpression* child0, - const SparqlExpression* child1, bool reversed) { - std::vector resVec{}; - if (const auto* variable = - dynamic_cast(child0)) { - if (const auto* valueId = dynamic_cast(child1)) { - resVec.emplace_back( - reversed ? mirroredExpression(valueId->value()) - : std::make_unique>( - valueId->value()), - variable->value()); - } - } - return resVec; - }; + const auto& optReferenceValue = + detail::getIdOrLocalVocabEntryFromLiteralExpression(child1); + if (optReferenceValue.has_value()) { + return prefilterExpressions::detail::makePrefilterExpressionVec( + optReferenceValue.value(), variableExpr->value(), reversed); + } + return {}; + }; // Option 1: // RelationalExpression containing a VariableExpression as the first child - // and an IdExpression as the second child. + // and an IdExpression, IdExpression or IriExpression as the second child. // E.g. for ?x >= 10 (RelationalExpression Sparql), we obtain the following // pair with PrefilterExpression and Variable: <(>= 10), ?x> auto resVec = tryGetPrefilterExprVariablePairVec(child0, child1, false); @@ -477,9 +448,9 @@ RelationalExpression::getPrefilterExpressionForMetadata( return resVec; } // Option 2: - // RelationalExpression containing an IdExpression as the first child and a - // VariableExpression as the second child. - // (1) 10 >= ?x (RelationalExpression Sparql), we obtain the following + // RelationalExpression containing an IdExpression, LiteralExpression or + // IriExpression as the first child and a VariableExpression as the second + // child. (1) 10 >= ?x (RelationalExpression Sparql), we obtain the following // pair with PrefilterExpression and Variable: <(<= 10), ?x> // (2) 10 != ?x (RelationalExpression Sparql), we obtain the following // pair with PrefilterExpression and Variable: <(!= 10), ?x>; diff --git a/src/engine/sparqlExpressions/StringExpressions.cpp b/src/engine/sparqlExpressions/StringExpressions.cpp index 2392ec2b18..f7e7d9bca7 100644 --- a/src/engine/sparqlExpressions/StringExpressions.cpp +++ b/src/engine/sparqlExpressions/StringExpressions.cpp @@ -244,26 +244,23 @@ class StrStartsExpressionImpl : public NaryExpression { AD_CORRECTNESS_CHECK(this->N == 2); const SparqlExpression* child0 = this->getNthChild(0).value(); const SparqlExpression* child1 = this->getNthChild(1).value(); - const auto getPrefilterExprVariableVec = [](const SparqlExpression* child0, - const SparqlExpression* child1, - bool startsWithVar) { - using namespace prefilterExpressions; - std::vector resVec{}; - if (const auto* variable = - dynamic_cast(child0)) { - if (const auto* valueId = dynamic_cast(child1)) { - !startsWithVar - // Will return: {<(>= VocabId(n)), ?var>} (Option 1) - ? resVec.emplace_back( - std::make_unique(valueId->value()), - variable->value()) - // Will return: {<(<= VocabId(n)), ?var>} (Option 2) - : resVec.emplace_back( - std::make_unique(valueId->value()), - variable->value()); - } + + const auto getPrefilterExprVariableVec = + [](const SparqlExpression* child0, const SparqlExpression* child1, + bool startsWithVar) -> std::vector { + const auto* varExpr = dynamic_cast(child0); + if (!varExpr) { + return {}; } - return resVec; + + const auto& optReferenceValue = + getIdOrLocalVocabEntryFromLiteralExpression(child1, true); + if (optReferenceValue.has_value()) { + return prefilterExpressions::detail::makePrefilterExpressionVec< + prefilterExpressions::CompOp::GE>(optReferenceValue.value(), + varExpr->value(), startsWithVar); + } + return {}; }; // Remark: With the current implementation we only prefilter w.r.t. one // bound. diff --git a/test/GetPrefilterExpressionFromSparqlExpressionTest.cpp b/test/GetPrefilterExpressionFromSparqlExpressionTest.cpp index d154f856c5..235c069cff 100644 --- a/test/GetPrefilterExpressionFromSparqlExpressionTest.cpp +++ b/test/GetPrefilterExpressionFromSparqlExpressionTest.cpp @@ -363,8 +363,6 @@ TEST(GetPrefilterExpressionFromSparqlExpression, const Iri iri = I(""); const Literal lit = L("\"lit\""); evalAndEqualityCheck(leSprql(var, var)); - evalAndEqualityCheck(neqSprql(iri, var)); - evalAndEqualityCheck(eqSprql(var, iri)); evalAndEqualityCheck(neqSprql(IntId(10), DoubleId(23.3))); evalAndEqualityCheck(gtSprql(DoubleId(10), lit)); evalAndEqualityCheck(ltSprql(VocabId(10), BoolId(10))); From 36b00f388f5d0d50343555740e8b77b1a966acba Mon Sep 17 00:00:00 2001 From: realHannes Date: Mon, 2 Dec 2024 11:29:43 +0100 Subject: [PATCH 72/81] implement use PrefilterExpression for Iri and Literals (2) --- .../PrefilterExpressionIndex.cpp | 102 ++++++++++-------- .../PrefilterExpressionIndex.h | 47 ++++---- 2 files changed, 83 insertions(+), 66 deletions(-) diff --git a/src/engine/sparqlExpressions/PrefilterExpressionIndex.cpp b/src/engine/sparqlExpressions/PrefilterExpressionIndex.cpp index 9ba0409874..dffa1025ea 100644 --- a/src/engine/sparqlExpressions/PrefilterExpressionIndex.cpp +++ b/src/engine/sparqlExpressions/PrefilterExpressionIndex.cpp @@ -51,19 +51,6 @@ static auto getMaskedTriple(const BlockMetadata::PermutedTriple& triple, } }; -//______________________________________________________________________________ -// Retrieve the reference `ValueId` from `IdOrLocalVocabEntry` -static const ValueId getValueIdFromReferenceValue( - const IdOrLocalVocabEntry& referenceValue, LocalVocab localVocab = {}) { - if (std::holds_alternative(referenceValue)) { - return std::get(referenceValue); - } else { - assert(std::holds_alternative(referenceValue)); - return Id::makeFromLocalVocabIndex(localVocab.getIndexAndAddIfNotContained( - std::get(referenceValue))); - } -}; - //______________________________________________________________________________ // Check for constant values in all columns `< evaluationColumn` static bool checkBlockIsInconsistent(const BlockMetadata& block, @@ -181,15 +168,12 @@ static std::string getLogicalOpStr(const LogicalOperator logOp) { // SECTION PREFILTER EXPRESSION (BASE CLASS) //______________________________________________________________________________ std::vector PrefilterExpression::evaluate( - std::span input, size_t evaluationColumn, - bool stripIncompleteBlocks) const { - if (!stripIncompleteBlocks) { - return evaluateAndCheckImpl(input, evaluationColumn); - } + std::span input, size_t evaluationColumn) const { if (input.size() < 3) { return std::vector(input.begin(), input.end()); } + LocalVocab vocab{}; std::optional firstBlock = std::nullopt; std::optional lastBlock = std::nullopt; if (checkBlockIsInconsistent(input.front(), evaluationColumn)) { @@ -200,8 +184,7 @@ std::vector PrefilterExpression::evaluate( lastBlock = input.back(); input = input.subspan(0, input.size() - 1); } - - auto result = evaluateAndCheckImpl(input, evaluationColumn); + auto result = evaluateAndCheckImpl(input, evaluationColumn, vocab); if (firstBlock.has_value()) { result.insert(result.begin(), firstBlock.value()); } @@ -213,9 +196,10 @@ std::vector PrefilterExpression::evaluate( // _____________________________________________________________________________ std::vector PrefilterExpression::evaluateAndCheckImpl( - std::span input, size_t evaluationColumn) const { + std::span input, size_t evaluationColumn, + LocalVocab& vocab) const { checkEvalRequirements(input, evaluationColumn); - const auto& relevantBlocks = evaluateImpl(input, evaluationColumn); + const auto& relevantBlocks = evaluateImpl(input, evaluationColumn, vocab); checkEvalRequirements(relevantBlocks, evaluationColumn); return relevantBlocks; } @@ -226,26 +210,25 @@ template std::unique_ptr RelationalExpression::logicalComplement() const { using enum CompOp; - auto referenceId = getValueIdFromReferenceValue(referenceValue_); switch (Comparison) { case LT: // Complement X < Y: X >= Y - return std::make_unique(referenceId); + return std::make_unique(referenceValue_); case LE: // Complement X <= Y: X > Y - return std::make_unique(referenceId); + return std::make_unique(referenceValue_); case EQ: // Complement X == Y: X != Y - return std::make_unique(referenceId); + return std::make_unique(referenceValue_); case NE: // Complement X != Y: X == Y - return std::make_unique(referenceId); + return std::make_unique(referenceValue_); case GE: // Complement X >= Y: X < Y - return std::make_unique(referenceId); + return std::make_unique(referenceValue_); case GT: // Complement X > Y: X <= Y - return std::make_unique(referenceId); + return std::make_unique(referenceValue_); default: AD_FAIL(); } @@ -254,7 +237,8 @@ RelationalExpression::logicalComplement() const { //______________________________________________________________________________ template std::vector RelationalExpression::evaluateImpl( - std::span input, size_t evaluationColumn) const { + std::span input, size_t evaluationColumn, + LocalVocab& vocab) const { using namespace valueIdComparators; std::vector valueIdsInput; // For each BlockMetadata value in vector input, we have a respective Id for @@ -275,7 +259,22 @@ std::vector RelationalExpression::evaluateImpl( } } - auto referenceId = getValueIdFromReferenceValue(referenceValue_); + // Helper to retrieve the reference `ValueId` from `IdOrLocalVocabEntry` + // variant. + const auto getValueIdFromReferenceValue = + [](const IdOrLocalVocabEntry& referenceValue, LocalVocab& localVocab) { + if (std::holds_alternative(referenceValue)) { + return std::get(referenceValue); + } else if (std::holds_alternative(referenceValue)) { + return Id::makeFromLocalVocabIndex( + localVocab.getIndexAndAddIfNotContained( + std::get(referenceValue))); + } else { + AD_FAIL(); + } + }; + + auto referenceId = getValueIdFromReferenceValue(referenceValue_, vocab); // Use getRangesForId (from valueIdComparators) to extract the ranges // containing the relevant ValueIds. // For pre-filtering with CompOp::EQ, we have to consider empty ranges. @@ -327,8 +326,7 @@ bool RelationalExpression::operator==( if (!otherRelational) { return false; } - return getValueIdFromReferenceValue(referenceValue_) == - getValueIdFromReferenceValue(otherRelational->referenceValue_); + return referenceValue_ == otherRelational->referenceValue_; }; //______________________________________________________________________________ @@ -342,10 +340,23 @@ std::unique_ptr RelationalExpression::clone() template std::string RelationalExpression::asString( [[maybe_unused]] size_t depth) const { + auto referenceValueToString = [](std::stringstream& stream, + const IdOrLocalVocabEntry& referenceValue) { + if (std::holds_alternative(referenceValue)) { + stream << std::get(referenceValue); + } else if (std::holds_alternative(referenceValue)) { + stream + << std::get(referenceValue).toStringRepresentation(); + } else { + AD_FAIL(); + } + }; + std::stringstream stream; stream << "Prefilter RelationalExpression<" << getRelationalOpStr(Comparison) - << ">\nValueId: " << getValueIdFromReferenceValue(referenceValue_) - << std::endl; + << ">\nreferenceValue_ : "; + referenceValueToString(stream, referenceValue_); + stream << " ." << std::endl; return stream.str(); }; @@ -372,15 +383,18 @@ LogicalExpression::logicalComplement() const { //______________________________________________________________________________ template std::vector LogicalExpression::evaluateImpl( - std::span input, size_t evaluationColumn) const { + std::span input, size_t evaluationColumn, + LocalVocab& vocab) const { using enum LogicalOperator; if constexpr (Operation == AND) { - auto resultChild1 = child1_->evaluate(input, evaluationColumn, false); - return child2_->evaluate(resultChild1, evaluationColumn, false); + auto resultChild1 = + child1_->evaluateAndCheckImpl(input, evaluationColumn, vocab); + return child2_->evaluateAndCheckImpl(resultChild1, evaluationColumn, vocab); } else { static_assert(Operation == OR); - return getSetUnion(child1_->evaluate(input, evaluationColumn, false), - child2_->evaluate(input, evaluationColumn, false)); + return getSetUnion( + child1_->evaluateAndCheckImpl(input, evaluationColumn, vocab), + child2_->evaluateAndCheckImpl(input, evaluationColumn, vocab)); } }; @@ -431,8 +445,9 @@ std::unique_ptr NotExpression::logicalComplement() const { //______________________________________________________________________________ std::vector NotExpression::evaluateImpl( - std::span input, size_t evaluationColumn) const { - return child_->evaluate(input, evaluationColumn, false); + std::span input, size_t evaluationColumn, + LocalVocab& vocab) const { + return child_->evaluateAndCheckImpl(input, evaluationColumn, vocab); }; //______________________________________________________________________________ @@ -509,7 +524,8 @@ void checkPropertiesForPrefilterConstruction( auto viewVariable = vec | std::views::values; if (!std::ranges::is_sorted(viewVariable, std::less<>{})) { throw std::runtime_error( - "The vector must contain the pairs in " + "The vector must contain the pairs " + "in " "sorted order w.r.t. Variable value."); } if (auto it = std::ranges::adjacent_find(viewVariable); diff --git a/src/engine/sparqlExpressions/PrefilterExpressionIndex.h b/src/engine/sparqlExpressions/PrefilterExpressionIndex.h index 6cea485efd..523ad1475c 100644 --- a/src/engine/sparqlExpressions/PrefilterExpressionIndex.h +++ b/src/engine/sparqlExpressions/PrefilterExpressionIndex.h @@ -93,8 +93,7 @@ class PrefilterExpression { // removes the respective block if it is conditionally (inconsistent columns) // necessary. std::vector evaluate(std::span input, - size_t evaluationColumn, - bool stripIncompleteBlocks = true) const; + size_t evaluationColumn) const; // Format for debugging friend std::ostream& operator<<(std::ostream& str, @@ -103,21 +102,23 @@ class PrefilterExpression { return str; } - private: - // Performs the following conditional checks on the provided `BlockMetadata` - // values: - // (1) unqiueness of blocks - // (2) sorted (order) - // (3) Constant values for all columns `< evaluationColumn` - // This function subsequently invokes the `evaluateImpl` method and - // checks the corresponding result for those conditions again. - // If a respective condition is violated, the function performing the checks - // will throw a `std::runtime_error`. + // Note: Use `evaluate` for general evaluation of `PrefilterExpression` + // instead of this method. + // Performs the following conditional checks on + // the provided `BlockMetadata` values: (1) unqiueness of blocks (2) sorted + // (order) (3) Constant values for all columns `< evaluationColumn` This + // function subsequently invokes the `evaluateImpl` method and checks the + // corresponding result for those conditions again. If a respective condition + // is violated, the function performing the checks will throw a + // `std::runtime_error`. std::vector evaluateAndCheckImpl( - std::span input, size_t evaluationColumn) const; + std::span input, size_t evaluationColumn, + LocalVocab& vocab) const; + private: virtual std::vector evaluateImpl( - std::span input, size_t evaluationColumn) const = 0; + std::span input, size_t evaluationColumn, + LocalVocab& vocab) const = 0; }; //______________________________________________________________________________ @@ -145,9 +146,9 @@ class RelationalExpression : public PrefilterExpression { std::string asString(size_t depth) const override; private: - std::vector evaluateImpl( - std::span input, - size_t evaluationColumn) const override; + std::vector evaluateImpl(std::span input, + size_t evaluationColumn, + LocalVocab& vocab) const override; }; //______________________________________________________________________________ @@ -175,9 +176,9 @@ class LogicalExpression : public PrefilterExpression { std::string asString(size_t depth) const override; private: - std::vector evaluateImpl( - std::span input, - size_t evaluationColumn) const override; + std::vector evaluateImpl(std::span input, + size_t evaluationColumn, + LocalVocab& vocab) const override; }; //______________________________________________________________________________ @@ -200,9 +201,9 @@ class NotExpression : public PrefilterExpression { std::string asString(size_t depth) const override; private: - std::vector evaluateImpl( - std::span input, - size_t evaluationColumn) const override; + std::vector evaluateImpl(std::span input, + size_t evaluationColumn, + LocalVocab& vocab) const override; }; //______________________________________________________________________________ From b242221b400136a63519de5afa3bec5d7fc79265 Mon Sep 17 00:00:00 2001 From: RobinTF <83676088+RobinTF@users.noreply.github.com> Date: Fri, 29 Nov 2024 18:38:50 +0100 Subject: [PATCH 73/81] Fix deadlock in lazy join implementation (#1650) The recent implementation of lazy joins introduced a bug that lead to deadlocks if the result of a lazy join was not fully consumed (e.g. because of a LIMIT clause, or because the query was cancelled or an exception occured upstream. This PR fixes that bug. Co-authored-by: Johannes Kalmbach --- src/engine/Join.cpp | 73 +++++--------- src/engine/Result.cpp | 2 +- src/util/CacheableGenerator.h | 40 -------- src/util/Generators.h | 160 ++++++++++++++++++++++++++++++ test/CMakeLists.txt | 2 +- test/CacheableGeneratorTest.cpp | 83 ---------------- test/GeneratorsTest.cpp | 169 ++++++++++++++++++++++++++++++++ 7 files changed, 355 insertions(+), 174 deletions(-) delete mode 100644 src/util/CacheableGenerator.h create mode 100644 src/util/Generators.h delete mode 100644 test/CacheableGeneratorTest.cpp create mode 100644 test/GeneratorsTest.cpp diff --git a/src/engine/Join.cpp b/src/engine/Join.cpp index 6d2376bb9c..24ee843c1a 100644 --- a/src/engine/Join.cpp +++ b/src/engine/Join.cpp @@ -19,6 +19,7 @@ #include "global/Id.h" #include "global/RuntimeParameters.h" #include "util/Exception.h" +#include "util/Generators.h" #include "util/HashMap.h" #include "util/JoinAlgorithms/JoinAlgorithms.h" @@ -433,56 +434,30 @@ void Join::join(const IdTable& a, ColumnIndex jc1, const IdTable& b, Result::Generator Join::runLazyJoinAndConvertToGenerator( ad_utility::InvocableWithExactReturnType< Result::IdTableVocabPair, - std::function> auto action, + std::function> auto runLazyJoin, OptionalPermutation permutation) const { - std::atomic_flag write = true; - std::variant - storage; - ad_utility::JThread thread{[&write, &storage, &action, &permutation]() { - auto writeValue = [&write, &storage](auto value) noexcept { - storage = std::move(value); - write.clear(); - write.notify_one(); - }; - auto writeValueAndWait = [&permutation, &write, - &writeValue](Result::IdTableVocabPair value) { - AD_CORRECTNESS_CHECK(write.test()); - applyPermutation(value.idTable_, permutation); - writeValue(std::move(value)); - // Wait until we are allowed to write again. - write.wait(false); - }; - auto addValue = [&writeValueAndWait](IdTable& idTable, - LocalVocab& localVocab) { - if (idTable.size() < CHUNK_SIZE) { - return; - } - writeValueAndWait({std::move(idTable), std::move(localVocab)}); - }; - try { - auto finalValue = action(addValue); - if (!finalValue.idTable_.empty()) { - writeValueAndWait(std::move(finalValue)); - } - writeValue(std::monostate{}); - } catch (...) { - writeValue(std::current_exception()); - } - }}; - while (true) { - // Wait for read phase. - write.wait(true); - if (std::holds_alternative(storage)) { - break; - } - if (std::holds_alternative(storage)) { - std::rethrow_exception(std::get(storage)); - } - co_yield std::get(storage); - // Initiate write phase. - write.test_and_set(); - write.notify_one(); - } + return ad_utility::generatorFromActionWithCallback( + [runLazyJoin = std::move(runLazyJoin), + permutation = std::move(permutation)]( + std::function callback) { + auto yieldValue = [&permutation, + &callback](Result::IdTableVocabPair value) { + applyPermutation(value.idTable_, permutation); + callback(std::move(value)); + }; + + // The lazy join implementation calls its callback for each but the last + // block. The last block (which also is the only block in case the + // result is to be fully materialized) + auto lastBlock = runLazyJoin( + [&yieldValue](IdTable& idTable, LocalVocab& localVocab) { + if (idTable.size() < CHUNK_SIZE) { + return; + } + yieldValue({std::move(idTable), std::move(localVocab)}); + }); + yieldValue(std::move(lastBlock)); + }); } // _____________________________________________________________________________ diff --git a/src/engine/Result.cpp b/src/engine/Result.cpp index c4b9137d42..e54f135fa1 100644 --- a/src/engine/Result.cpp +++ b/src/engine/Result.cpp @@ -8,8 +8,8 @@ #include -#include "util/CacheableGenerator.h" #include "util/Exception.h" +#include "util/Generators.h" #include "util/Log.h" #include "util/Timer.h" diff --git a/src/util/CacheableGenerator.h b/src/util/CacheableGenerator.h deleted file mode 100644 index 881dd1c282..0000000000 --- a/src/util/CacheableGenerator.h +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2024, University of Freiburg, -// Chair of Algorithms and Data Structures. -// Author: Robin Textor-Falconi - -#pragma once - -#include - -#include "util/Generator.h" -#include "util/TypeTraits.h" - -namespace ad_utility { - -// Wrap the given `generator` inside another generator that aggregates a cache -// by calling `aggregator` on every iteration of the inner `generator` until it -// returns false. If the `aggregator` returns false, the cached value is -// discarded. If the cached value is still present once the generator is fully -// consumed, `onFullyCached` is called with the cached value. -template -cppcoro::generator wrapGeneratorWithCache( - cppcoro::generator generator, - InvocableWithExactReturnType&, const T&> auto - aggregator, - InvocableWithExactReturnType auto onFullyCached) { - std::optional aggregatedData{}; - bool shouldBeAggregated = true; - for (T& element : generator) { - if (shouldBeAggregated) { - shouldBeAggregated = aggregator(aggregatedData, element); - if (!shouldBeAggregated) { - aggregatedData.reset(); - } - } - co_yield element; - } - if (aggregatedData.has_value()) { - onFullyCached(std::move(aggregatedData).value()); - } -} -}; // namespace ad_utility diff --git a/src/util/Generators.h b/src/util/Generators.h new file mode 100644 index 0000000000..9157c48359 --- /dev/null +++ b/src/util/Generators.h @@ -0,0 +1,160 @@ +// Copyright 2024, University of Freiburg, +// Chair of Algorithms and Data Structures. +// Author: Robin Textor-Falconi + +#pragma once + +#include + +#include + +#include "util/Generator.h" +#include "util/TypeTraits.h" +#include "util/jthread.h" + +namespace ad_utility { + +// Wrap the given `generator` inside another generator that aggregates a cache +// by calling `aggregator` on every iteration of the inner `generator` until it +// returns false. If the `aggregator` returns false, the cached value is +// discarded. If the cached value is still present once the generator is fully +// consumed, `onFullyCached` is called with the cached value. +template +cppcoro::generator wrapGeneratorWithCache( + cppcoro::generator generator, + InvocableWithExactReturnType&, const T&> auto + aggregator, + InvocableWithExactReturnType auto onFullyCached) { + std::optional aggregatedData{}; + bool shouldBeAggregated = true; + for (T& element : generator) { + if (shouldBeAggregated) { + shouldBeAggregated = aggregator(aggregatedData, element); + if (!shouldBeAggregated) { + aggregatedData.reset(); + } + } + co_yield element; + } + if (aggregatedData.has_value()) { + onFullyCached(std::move(aggregatedData).value()); + } +} + +// Convert a callback-based `functionWithCallback` into a generator by spawning +// a thread that runs the functionWithCallback. The `functionWithCallback` is a +// callable that takes a `callback` with signature `void(T)`. The arguments with +// which this callback is called when running the `functionWithCallback` become +// the elements that are yielded by the created `generator`. +template +cppcoro::generator generatorFromActionWithCallback( + std::invocable> auto functionWithCallback) { + std::mutex mutex; + std::condition_variable cv; + + // Only one of the threads can run at the same time. The semantics are + // `Inner` -> The inner thread that runs the callback has to produce the next + // value. `Outer` -> The outer thread that is the generator has to yield the + // next value. value. `OuterIsFinished` -> The generator is finished and will + // read no further values from the inner thread, the inner thread has to stop. + enum struct State { Inner, Outer, OuterIsFinished }; + State state = State::Inner; + + // Used to pass the produced values from the inner thread to the generator. + // `monostate` means that the inner thread is finished and there will be no + // further values. `exception_ptr` means, that the inner thread has + // encountered an exception, which then rethrown by the outer generator. + std::variant storage; + + // Set up the inner thread. + ad_utility::JThread thread{ + [&mutex, &cv, &state, &storage, &functionWithCallback]() { + struct FinishedBecauseOuterIsFinishedException : public std::exception { + }; + // The inner thread starts (it has to produce the first value), so we + // can unconditionally take the lock. + std::unique_lock lock(mutex); + AD_CORRECTNESS_CHECK(state == State::Inner); + + // Wait for the outer thread to consume the last value and return + // control to the inner thread. + auto wait = [&]() { + cv.wait(lock, [&]() { return state != State::Outer; }); + if (state == State::OuterIsFinished) { + throw FinishedBecauseOuterIsFinishedException{}; + } + }; + + // Write the `value` to the `storage` and notify the outer thread that + // it has to consume it. + auto writeValue = [&cv, &state, &storage, &lock](auto value) noexcept { + storage = std::move(value); + state = State::Outer; + lock.unlock(); + cv.notify_one(); + lock.lock(); + }; + + // Write the `value`, pass control to the outer thread and wait till it + // is our turn again. + auto writeValueAndWait = [&state, &writeValue, &wait](T value) { + AD_CORRECTNESS_CHECK(state == State::Inner); + writeValue(std::move(value)); + wait(); + }; + + try { + // `writeValueAndWait` is the callback that is repeatedly called by + // the `functionWithCallback`. + functionWithCallback(writeValueAndWait); + + // The function has completed, notify the outer thread and finish. + writeValue(std::monostate{}); + } catch (const FinishedBecauseOuterIsFinishedException&) { + // This means that the outer thread is completely finished and only + // waits for the inner thread to join. + return; + } catch (...) { + // The function has created an exception, pass it to the outer thread. + writeValue(std::current_exception()); + } + // The destructor of `unique_lock` automatically calls unlock, so we + // unconditionally pass control to the outer thread. + }}; + + // Now the logic for the outer generator thread: + // The outer thread initially has to wait for the inner thread to produce + // the first value. + std::unique_lock lock{mutex}; + cv.wait(lock, [&state]() { return state == State::Outer; }); + + // At the end we have to notify the inner thread that it has to stop producing + // values, s.t. we can clean up. This happens in particular if the outer + // generator is prematurely destroyed. NOTE: This can only happen once we hold + // the lock, which means that the inner thread is waiting for the outer + // thread. + auto cleanup = absl::Cleanup{[&cv, &state, &lock]() { + state = State::OuterIsFinished; + lock.unlock(); + cv.notify_one(); + }}; + + while (true) { + // Wait for the inner thread to produce a value. + cv.wait(lock, [&state] { return state == State::Outer; }); + if (std::holds_alternative(storage)) { + break; + } + if (std::holds_alternative(storage)) { + std::rethrow_exception(std::get(storage)); + } + co_yield std::get(storage); + + // Pass control to the inner thread and repeat. + state = State::Inner; + lock.unlock(); + cv.notify_one(); + lock.lock(); + } +} +}; // namespace ad_utility diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 97a6d301e4..3846361dc5 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -421,7 +421,7 @@ addLinkAndDiscoverTest(CopyableSynchronizationTest) addLinkAndDiscoverTest(LazyJsonParserTest) -addLinkAndDiscoverTest(CacheableGeneratorTest) +addLinkAndDiscoverTest(GeneratorsTest) addLinkAndDiscoverTest(FilterTest engine) diff --git a/test/CacheableGeneratorTest.cpp b/test/CacheableGeneratorTest.cpp deleted file mode 100644 index a96a064429..0000000000 --- a/test/CacheableGeneratorTest.cpp +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright 2024, University of Freiburg, -// Chair of Algorithms and Data Structures. -// Author: Robin Textor-Falconi - -#include - -#include "util/CacheableGenerator.h" -#include "util/Generator.h" - -using ad_utility::wrapGeneratorWithCache; -using cppcoro::generator; -using ::testing::Optional; - -generator testGenerator(uint32_t range) { - for (uint32_t i = 0; i < range; i++) { - co_yield i; - } -} - -// _____________________________________________________________________________ -TEST(CacheableGenerator, testAggregation) { - bool called = false; - auto gen = wrapGeneratorWithCache( - testGenerator(4), - [](std::optional& optionalValue, const uint32_t& newValue) { - if (optionalValue.has_value()) { - optionalValue.value() += newValue; - } else { - optionalValue.emplace(newValue); - } - return true; - }, - [&called](std::optional value) { - called = true; - EXPECT_THAT(value, Optional(6)); - }); - uint32_t counter = 0; - for (uint32_t element : gen) { - EXPECT_EQ(counter, element); - ++counter; - } - EXPECT_EQ(counter, 4); - EXPECT_TRUE(called); -} - -// _____________________________________________________________________________ -TEST(CacheableGenerator, testEmptyGenerator) { - bool called = false; - auto gen = wrapGeneratorWithCache( - testGenerator(0), - [&called](std::optional&, uint32_t) { - called = true; - return true; - }, - [&called](std::optional) { called = true; }); - uint32_t tracker = 0; - for (uint32_t element : gen) { - tracker += element; - } - EXPECT_EQ(tracker, 0); - EXPECT_FALSE(called); -} - -// _____________________________________________________________________________ -TEST(CacheableGenerator, testAggregationCutoff) { - uint32_t callCounter = 0; - bool called = false; - auto gen = wrapGeneratorWithCache( - testGenerator(2), - [&callCounter](std::optional&, uint32_t) { - ++callCounter; - return false; - }, - [&called](std::optional) { called = true; }); - uint32_t loopCounter = 0; - for (uint32_t element : gen) { - EXPECT_EQ(element, loopCounter); - ++loopCounter; - } - EXPECT_EQ(loopCounter, 2); - EXPECT_EQ(callCounter, 1); - EXPECT_FALSE(called); -} diff --git a/test/GeneratorsTest.cpp b/test/GeneratorsTest.cpp new file mode 100644 index 0000000000..1bffc76ae5 --- /dev/null +++ b/test/GeneratorsTest.cpp @@ -0,0 +1,169 @@ +// Copyright 2024, University of Freiburg, +// Chair of Algorithms and Data Structures. +// Author: Robin Textor-Falconi + +#include + +#include "util/GTestHelpers.h" +#include "util/Generator.h" +#include "util/Generators.h" +#include "util/Views.h" + +using ad_utility::generatorFromActionWithCallback; +using ad_utility::wrapGeneratorWithCache; +using cppcoro::generator; +using ::testing::Optional; + +generator testGenerator(uint32_t range) { + for (uint32_t i = 0; i < range; i++) { + co_yield i; + } +} + +// _____________________________________________________________________________ +TEST(Generators, testAggregation) { + bool called = false; + auto gen = wrapGeneratorWithCache( + testGenerator(4), + [](std::optional& optionalValue, const uint32_t& newValue) { + if (optionalValue.has_value()) { + optionalValue.value() += newValue; + } else { + optionalValue.emplace(newValue); + } + return true; + }, + [&called](std::optional value) { + called = true; + EXPECT_THAT(value, Optional(6)); + }); + uint32_t counter = 0; + for (uint32_t element : gen) { + EXPECT_EQ(counter, element); + ++counter; + } + EXPECT_EQ(counter, 4); + EXPECT_TRUE(called); +} + +// _____________________________________________________________________________ +TEST(Generators, testEmptyGenerator) { + bool called = false; + auto gen = wrapGeneratorWithCache( + testGenerator(0), + [&called](std::optional&, uint32_t) { + called = true; + return true; + }, + [&called](std::optional) { called = true; }); + uint32_t tracker = 0; + for (uint32_t element : gen) { + tracker += element; + } + EXPECT_EQ(tracker, 0); + EXPECT_FALSE(called); +} + +// _____________________________________________________________________________ +TEST(Generators, testAggregationCutoff) { + uint32_t callCounter = 0; + bool called = false; + auto gen = wrapGeneratorWithCache( + testGenerator(2), + [&callCounter](std::optional&, uint32_t) { + ++callCounter; + return false; + }, + [&called](std::optional) { called = true; }); + uint32_t loopCounter = 0; + for (uint32_t element : gen) { + EXPECT_EQ(element, loopCounter); + ++loopCounter; + } + EXPECT_EQ(loopCounter, 2); + EXPECT_EQ(callCounter, 1); + EXPECT_FALSE(called); +} + +// _____________________________________________________________________________ +TEST(Generators, generatorFromActionWithCallbackCreatesProperGenerator) { + auto generator = generatorFromActionWithCallback([](auto callback) { + callback(0); + callback(1); + callback(2); + }); + int counter = 0; + for (int element : generator) { + EXPECT_EQ(element, counter); + ++counter; + } + EXPECT_EQ(counter, 3); +} + +// Test that in `generatorFromActionWithCallback` the inner and outer thread +// run mutually exclusive. +TEST(Generators, generatorFromActionWithCallbackThreadSafety) { + size_t counter = 0; + static constexpr size_t numValues = 20'000; + auto generator = + generatorFromActionWithCallback([&counter](auto callback) { + for (size_t i = 0; i < numValues; ++i) { + // The counter is modified concurrently directly here, as well as by + // the outer thread. + ++counter; + callback(1); + } + }); + for (auto element : generator) { + counter += element; + } + EXPECT_EQ(counter, 2 * numValues); +} + +// _____________________________________________________________________________ +TEST(Generators, generatorFromActionWithCallbackAbortsProperly) { + bool unreachableReached = false; + auto functionWithCallback = [&](auto callback) { + callback(634); + unreachableReached = true; + callback(1); + }; + { + auto generator = generatorFromActionWithCallback(functionWithCallback); + auto iterator = generator.begin(); + ASSERT_NE(iterator, generator.end()); + EXPECT_EQ(*iterator, 634); + // Prematurely destroy the generator (simulate cancellation). + } + ASSERT_FALSE(unreachableReached); + + // Destruction of the generator without consuming any values + { + auto generator = generatorFromActionWithCallback(functionWithCallback); + } + ASSERT_FALSE(unreachableReached); + + // Exception after consuming the first value + auto consumeAndThrow = [&]() { + auto generator = generatorFromActionWithCallback(functionWithCallback); + auto iterator = generator.begin(); + ASSERT_NE(iterator, generator.end()); + EXPECT_EQ(*iterator, 634); + throw std::runtime_error("bum"); + // Prematurely destroy the generator (simulate cancellation). + }; + AD_EXPECT_THROW_WITH_MESSAGE(consumeAndThrow(), ::testing::StrEq("bum")); +} + +// _____________________________________________________________________________ +TEST(Generators, generatorFromActionWithCallbackPropagatesException) { + auto generator = generatorFromActionWithCallback([](auto callback) { + callback(0); + throw std::runtime_error{"Test Exception"}; + }); + auto iterator = generator.begin(); + ASSERT_NE(iterator, generator.end()); + EXPECT_EQ(*iterator, 0); + AD_EXPECT_THROW_WITH_MESSAGE_AND_TYPE( + ++iterator, ::testing::StrEq("Test Exception"), std::runtime_error); +} From 792dd39c77ca035084e99d40274fc22030b694e7 Mon Sep 17 00:00:00 2001 From: Johannes Kalmbach Date: Fri, 29 Nov 2024 20:37:16 +0100 Subject: [PATCH 74/81] Correctly handle the cache in the presence of SPARQL UPDATE (#1646) An update can invalidate a cached query result in the sense that if one would run the query again after the update, the result may be different. This was ignored so far, and is now considered as follows: Each `LocatedTriplesSnapshot` gets its own "index" (starting from zero and then incremented for each new snaphot). That index becomes part of the cache key. That way, a query will make use of a cached result if and only if there was no update between the time of the query and the time when the cached result was computed. --- src/engine/Operation.cpp | 9 ++++---- src/engine/Operation.h | 2 +- src/engine/QueryExecutionContext.h | 22 +++++++++++++++++++- src/engine/QueryExecutionTree.cpp | 3 ++- src/engine/Server.cpp | 5 +++++ src/index/DeltaTriples.cpp | 8 +++++--- src/index/DeltaTriples.h | 5 ++++- src/index/Permutation.cpp | 2 +- test/OperationTest.cpp | 33 ++++++++++++++++++++++-------- 9 files changed, 68 insertions(+), 21 deletions(-) diff --git a/src/engine/Operation.cpp b/src/engine/Operation.cpp index 9eb6f0979e..48ce20f651 100644 --- a/src/engine/Operation.cpp +++ b/src/engine/Operation.cpp @@ -178,7 +178,7 @@ ProtoResult Operation::runComputation(const ad_utility::Timer& timer, // _____________________________________________________________________________ CacheValue Operation::runComputationAndPrepareForCache( const ad_utility::Timer& timer, ComputationMode computationMode, - const std::string& cacheKey, bool pinned) { + const QueryCacheKey& cacheKey, bool pinned) { auto& cache = _executionContext->getQueryTreeCache(); auto result = runComputation(timer, computationMode); if (!result.isFullyMaterialized() && @@ -235,7 +235,8 @@ std::shared_ptr Operation::getResult( signalQueryUpdate(); } auto& cache = _executionContext->getQueryTreeCache(); - const string cacheKey = getCacheKey(); + const QueryCacheKey cacheKey = { + getCacheKey(), _executionContext->locatedTriplesSnapshot().index_}; const bool pinFinalResultButNotSubtrees = _executionContext->_pinResult && isRoot; const bool pinResult = @@ -455,8 +456,8 @@ void Operation::createRuntimeInfoFromEstimates( } _runtimeInfo->multiplicityEstimates_ = multiplicityEstimates; - auto cachedResult = - _executionContext->getQueryTreeCache().getIfContained(getCacheKey()); + auto cachedResult = _executionContext->getQueryTreeCache().getIfContained( + {getCacheKey(), locatedTriplesSnapshot().index_}); if (cachedResult.has_value()) { const auto& [resultPointer, cacheStatus] = cachedResult.value(); _runtimeInfo->cacheStatus_ = cacheStatus; diff --git a/src/engine/Operation.h b/src/engine/Operation.h index af5eff7350..db6b084697 100644 --- a/src/engine/Operation.h +++ b/src/engine/Operation.h @@ -326,7 +326,7 @@ class Operation { // into the cache. CacheValue runComputationAndPrepareForCache(const ad_utility::Timer& timer, ComputationMode computationMode, - const std::string& cacheKey, + const QueryCacheKey& cacheKey, bool pinned); // Create and store the complete runtime information for this operation after diff --git a/src/engine/QueryExecutionContext.h b/src/engine/QueryExecutionContext.h index de7b5a4f6e..dd9adf12b2 100644 --- a/src/engine/QueryExecutionContext.h +++ b/src/engine/QueryExecutionContext.h @@ -19,6 +19,8 @@ #include "util/ConcurrentCache.h" #include "util/Synchronized.h" +// The value of the `QueryResultCache` below. It consists of a `Result` together +// with its `RuntimeInfo`. class CacheValue { private: std::shared_ptr result_; @@ -61,11 +63,29 @@ class CacheValue { }; }; +// The key for the `QueryResultCache` below. It consists of a `string` (the +// actual cache key of a `QueryExecutionTree` and the index of the +// `LocatedTriplesSnapshot` that was used to create the corresponding value. +// That way, two identical trees with different snapshot indices will have a +// different cache key. This has the (desired!) effect that UPDATE requests +// correctly invalidate preexisting cache results. +struct QueryCacheKey { + std::string key_; + size_t locatedTriplesSnapshotIndex_; + + bool operator==(const QueryCacheKey&) const = default; + + template + friend H AbslHashValue(H h, const QueryCacheKey& key) { + return H::combine(std::move(h), key.key_, key.locatedTriplesSnapshotIndex_); + } +}; + // Threadsafe LRU cache for (partial) query results, that // checks on insertion, if the result is currently being computed // by another query. using QueryResultCache = ad_utility::ConcurrentCache< - ad_utility::LRUCache>; + ad_utility::LRUCache>; // Execution context for queries. // Holds references to index and engine, implements caching. diff --git a/src/engine/QueryExecutionTree.cpp b/src/engine/QueryExecutionTree.cpp index eeff0f09e8..470476b166 100644 --- a/src/engine/QueryExecutionTree.cpp +++ b/src/engine/QueryExecutionTree.cpp @@ -143,7 +143,8 @@ void QueryExecutionTree::readFromCache() { return; } auto& cache = qec_->getQueryTreeCache(); - auto res = cache.getIfContained(getCacheKey()); + auto res = cache.getIfContained( + {getCacheKey(), qec_->locatedTriplesSnapshot().index_}); if (res.has_value()) { cachedResult_ = res->_resultPointer->resultTablePtr(); } diff --git a/src/engine/Server.cpp b/src/engine/Server.cpp index f9ffdbf7f1..08fc6f9607 100644 --- a/src/engine/Server.cpp +++ b/src/engine/Server.cpp @@ -915,6 +915,11 @@ void Server::processUpdateImpl( << std::endl; LOG(DEBUG) << "Runtime Info:\n" << qet.getRootOperation()->runtimeInfo().toString() << std::endl; + + // Clear the cache, because all cache entries have been invalidated by the + // update anyway (The index of the located triples snapshot is part of the + // cache key). + cache_.clearAll(); } // ____________________________________________________________________________ diff --git a/src/index/DeltaTriples.cpp b/src/index/DeltaTriples.cpp index ee86fab835..e6b3bcd555 100644 --- a/src/index/DeltaTriples.cpp +++ b/src/index/DeltaTriples.cpp @@ -178,12 +178,14 @@ LocatedTriplesSnapshot::getLocatedTriplesForPermutation( } // ____________________________________________________________________________ -SharedLocatedTriplesSnapshot DeltaTriples::getSnapshot() const { +SharedLocatedTriplesSnapshot DeltaTriples::getSnapshot() { // NOTE: Both members of the `LocatedTriplesSnapshot` are copied, but the // `localVocab_` has no copy constructor (in order to avoid accidental // copies), hence the explicit `clone`. + auto snapshotIndex = nextSnapshotIndex_; + ++nextSnapshotIndex_; return SharedLocatedTriplesSnapshot{std::make_shared( - locatedTriples(), localVocab_.clone())}; + locatedTriples(), localVocab_.clone(), snapshotIndex)}; } // ____________________________________________________________________________ @@ -193,7 +195,7 @@ DeltaTriples::DeltaTriples(const Index& index) // ____________________________________________________________________________ DeltaTriplesManager::DeltaTriplesManager(const IndexImpl& index) : deltaTriples_{index}, - currentLocatedTriplesSnapshot_{deltaTriples_.rlock()->getSnapshot()} {} + currentLocatedTriplesSnapshot_{deltaTriples_.wlock()->getSnapshot()} {} // _____________________________________________________________________________ void DeltaTriplesManager::modify( diff --git a/src/index/DeltaTriples.h b/src/index/DeltaTriples.h index 25d6b7af6b..89e57b05fe 100644 --- a/src/index/DeltaTriples.h +++ b/src/index/DeltaTriples.h @@ -26,6 +26,8 @@ using LocatedTriplesPerBlockAllPermutations = struct LocatedTriplesSnapshot { LocatedTriplesPerBlockAllPermutations locatedTriplesPerBlock_; LocalVocab localVocab_; + // A unique index for this snapshot that is used in the query cache. + size_t index_; // Get `TripleWithPosition` objects for given permutation. const LocatedTriplesPerBlock& getLocatedTriplesForPermutation( Permutation::Enum permutation) const; @@ -63,6 +65,7 @@ class DeltaTriples { private: // The index to which these triples are added. const IndexImpl& index_; + size_t nextSnapshotIndex_ = 0; // The located triples for all the 6 permutations. LocatedTriplesPerBlockAllPermutations locatedTriples_; @@ -140,7 +143,7 @@ class DeltaTriples { // Return a deep copy of the `LocatedTriples` and the corresponding // `LocalVocab` which form a snapshot of the current status of this // `DeltaTriples` object. - SharedLocatedTriplesSnapshot getSnapshot() const; + SharedLocatedTriplesSnapshot getSnapshot(); // Register the original `metadata` for the given `permutation`. This has to // be called before any updates are processed. diff --git a/src/index/Permutation.cpp b/src/index/Permutation.cpp index 0c18d550d9..e58b269511 100644 --- a/src/index/Permutation.cpp +++ b/src/index/Permutation.cpp @@ -251,7 +251,7 @@ const Permutation& Permutation::getActualPermutation(Id id) const { // ______________________________________________________________________ const LocatedTriplesPerBlock& Permutation::getLocatedTriplesForPermutation( const LocatedTriplesSnapshot& locatedTriplesSnapshot) const { - static const LocatedTriplesSnapshot emptySnapshot; + static const LocatedTriplesSnapshot emptySnapshot{{}, {}, 0}; const auto& actualSnapshot = isInternalPermutation_ ? emptySnapshot : locatedTriplesSnapshot; return actualSnapshot.getLocatedTriplesForPermutation(permutation_); diff --git a/test/OperationTest.cpp b/test/OperationTest.cpp index 0e93b8e9fc..800e0c3b2b 100644 --- a/test/OperationTest.cpp +++ b/test/OperationTest.cpp @@ -523,6 +523,12 @@ TEST(Operation, verifyLimitIsProperlyAppliedAndUpdatesRuntimeInfoCorrectly) { expectRtiHasDimensions(childRti, 2, 3); } +namespace { +QueryCacheKey makeQueryCacheKey(std::string s) { + return {std::move(s), 102394857}; +} +} // namespace + // _____________________________________________________________________________ TEST(Operation, ensureLazyOperationIsCachedIfSmallEnough) { auto qec = getQec(); @@ -536,14 +542,17 @@ TEST(Operation, ensureLazyOperationIsCachedIfSmallEnough) { ad_utility::Timer timer{ad_utility::Timer::InitialStatus::Started}; auto cacheValue = valuesForTesting.runComputationAndPrepareForCache( - timer, ComputationMode::LAZY_IF_SUPPORTED, "test", false); - EXPECT_FALSE(qec->getQueryTreeCache().cacheContains("test")); + timer, ComputationMode::LAZY_IF_SUPPORTED, makeQueryCacheKey("test"), + false); + EXPECT_FALSE( + qec->getQueryTreeCache().cacheContains(makeQueryCacheKey("test"))); for ([[maybe_unused]] Result::IdTableVocabPair& _ : cacheValue.resultTable().idTables()) { } - auto aggregatedValue = qec->getQueryTreeCache().getIfContained("test"); + auto aggregatedValue = + qec->getQueryTreeCache().getIfContained(makeQueryCacheKey("test")); ASSERT_TRUE(aggregatedValue.has_value()); ASSERT_TRUE(aggregatedValue.value()._resultPointer); @@ -588,15 +597,18 @@ TEST(Operation, checkLazyOperationIsNotCachedIfTooLarge) { qec->getQueryTreeCache().setMaxSizeSingleEntry(1_B); auto cacheValue = valuesForTesting.runComputationAndPrepareForCache( - timer, ComputationMode::LAZY_IF_SUPPORTED, "test", false); - EXPECT_FALSE(qec->getQueryTreeCache().cacheContains("test")); + timer, ComputationMode::LAZY_IF_SUPPORTED, makeQueryCacheKey("test"), + false); + EXPECT_FALSE( + qec->getQueryTreeCache().cacheContains(makeQueryCacheKey("test"))); qec->getQueryTreeCache().setMaxSizeSingleEntry(originalSize); for ([[maybe_unused]] Result::IdTableVocabPair& _ : cacheValue.resultTable().idTables()) { } - EXPECT_FALSE(qec->getQueryTreeCache().cacheContains("test")); + EXPECT_FALSE( + qec->getQueryTreeCache().cacheContains(makeQueryCacheKey("test"))); } // _____________________________________________________________________________ @@ -612,12 +624,15 @@ TEST(Operation, checkLazyOperationIsNotCachedIfUnlikelyToFitInCache) { ad_utility::Timer timer{ad_utility::Timer::InitialStatus::Started}; auto cacheValue = valuesForTesting.runComputationAndPrepareForCache( - timer, ComputationMode::LAZY_IF_SUPPORTED, "test", false); - EXPECT_FALSE(qec->getQueryTreeCache().cacheContains("test")); + timer, ComputationMode::LAZY_IF_SUPPORTED, makeQueryCacheKey("test"), + false); + EXPECT_FALSE( + qec->getQueryTreeCache().cacheContains(makeQueryCacheKey("test"))); for ([[maybe_unused]] Result::IdTableVocabPair& _ : cacheValue.resultTable().idTables()) { } - EXPECT_FALSE(qec->getQueryTreeCache().cacheContains("test")); + EXPECT_FALSE( + qec->getQueryTreeCache().cacheContains(makeQueryCacheKey("test"))); } From 1a6e5b22facec82fd6dca0e8d19d0ac31c3f531a Mon Sep 17 00:00:00 2001 From: Johannes Kalmbach Date: Fri, 29 Nov 2024 20:50:13 +0100 Subject: [PATCH 75/81] Update graph info in the augmented block metadata (#1642) An update can invalidate a cached query result in the sense that if one would run the query again after the update, the result may be different. This was ignored so far, and is now considered as follows: Each `LocatedTriplesSnapshot` gets its own "index" (starting from zero and then incremented for each new snaphot). That index becomes part of the cache key. That way, a query will make use of a cached result if and only if there was no update between the time of the query and the time when the cached result was computed. --- src/index/CompressedRelation.h | 18 +++- src/index/LocatedTriples.cpp | 49 +++++++++- test/CMakeLists.txt | 2 +- test/LocatedTriplesTest.cpp | 133 +++++++++++++++++++++++++- test/PrefilterExpressionIndexTest.cpp | 40 +++++--- 5 files changed, 220 insertions(+), 22 deletions(-) diff --git a/src/index/CompressedRelation.h b/src/index/CompressedRelation.h index 9f7095993e..c152999e10 100644 --- a/src/index/CompressedRelation.h +++ b/src/index/CompressedRelation.h @@ -59,6 +59,9 @@ struct CompressedBlockMetadataNoBlockIndex { size_t compressedSize_; bool operator==(const OffsetAndCompressedSize&) const = default; }; + + using GraphInfo = std::optional>; + std::vector offsetsAndCompressedSize_; size_t numRows_; @@ -115,7 +118,14 @@ struct CompressedBlockMetadataNoBlockIndex { const CompressedBlockMetadataNoBlockIndex& blockMetadata) { str << "#BlockMetadata\n(first) " << blockMetadata.firstTriple_ << "(last) " << blockMetadata.lastTriple_ << "num. rows: " << blockMetadata.numRows_ - << "." << std::endl; + << ".\n"; + if (blockMetadata.graphInfo_.has_value()) { + str << "Graphs: "; + ad_utility::lazyStrJoin(&str, blockMetadata.graphInfo_.value(), ", "); + str << '\n'; + } + str << "[possibly] contains duplicates: " + << blockMetadata.containsDuplicatesWithDifferentGraphs_ << '\n'; return str; } }; @@ -133,9 +143,9 @@ struct CompressedBlockMetadata : CompressedBlockMetadataNoBlockIndex { // Format BlockMetadata contents for debugging. friend std::ostream& operator<<( std::ostream& str, const CompressedBlockMetadata& blockMetadata) { - str << "#BlockMetadata\n(first) " << blockMetadata.firstTriple_ << "(last) " - << blockMetadata.lastTriple_ << "num. rows: " << blockMetadata.numRows_ - << "." << std::endl; + str << static_cast( + blockMetadata); + str << "block index: " << blockMetadata.blockIndex_ << "\n"; return str; } }; diff --git a/src/index/LocatedTriples.cpp b/src/index/LocatedTriples.cpp index b2244f8960..05353324a5 100644 --- a/src/index/LocatedTriples.cpp +++ b/src/index/LocatedTriples.cpp @@ -10,6 +10,7 @@ #include "absl/strings/str_join.h" #include "index/CompressedRelation.h" +#include "index/ConstantsIndexBuilding.h" #include "util/ChunkedForLoop.h" // ____________________________________________________________________________ @@ -250,6 +251,48 @@ void LocatedTriplesPerBlock::setOriginalMetadata( updateAugmentedMetadata(); } +// Update the `blockMetadata`, such that its graph info is consistent with the +// `locatedTriples` which are added to that block. In particular, all graphs to +// which at least one triple is inserted become part of the graph info, and if +// the number of total graphs becomes larger than the configured threshold, then +// the graph info is set to `nullopt`, which means that there is no info. +static auto updateGraphMetadata(CompressedBlockMetadata& blockMetadata, + const LocatedTriples& locatedTriples) { + // We do not know anything about the triples contained in the block, so we + // also cannot know if the `locatedTriples` introduces duplicates. We thus + // have to be conservative and assume that there are duplicates. + blockMetadata.containsDuplicatesWithDifferentGraphs_ = true; + auto& graphs = blockMetadata.graphInfo_; + if (!graphs.has_value()) { + // The original block already contains too many graphs, don't store any + // graph info. + return; + } + + // Compute a hash set of all graphs that are originally contained in the block + // and all the graphs that are added via the `locatedTriples`. + ad_utility::HashSet newGraphs(graphs.value().begin(), + graphs.value().end()); + for (auto& lt : locatedTriples) { + if (!lt.shouldTripleExist_) { + // Don't update the graph info for triples that are deleted. + continue; + } + newGraphs.insert(lt.triple_.ids_.at(ADDITIONAL_COLUMN_GRAPH_ID)); + // Handle the case that with the newly added triples we have too many + // distinct graphs to store them in the graph info. + if (newGraphs.size() > MAX_NUM_GRAPHS_STORED_IN_BLOCK_METADATA) { + graphs.reset(); + return; + } + } + graphs.emplace(newGraphs.begin(), newGraphs.end()); + + // Sort the stored graphs. Note: this is currently not expected by the code + // that uses the graph info, but makes testing much easier. + std::ranges::sort(graphs.value()); +} + // ____________________________________________________________________________ void LocatedTriplesPerBlock::updateAugmentedMetadata() { // TODO use view::enumerate @@ -265,6 +308,7 @@ void LocatedTriplesPerBlock::updateAugmentedMetadata() { blockMetadata.lastTriple_ = std::max(blockMetadata.lastTriple_, blockUpdates.rbegin()->triple_.toPermutedTriple()); + updateGraphMetadata(blockMetadata, blockUpdates); } blockIndex++; } @@ -287,7 +331,10 @@ void LocatedTriplesPerBlock::updateAugmentedMetadata() { lastTriple, std::nullopt, true}; - augmentedMetadata_->emplace_back(lastBlockN, blockIndex); + lastBlockN.graphInfo_.emplace(); + CompressedBlockMetadata lastBlock{lastBlockN, blockIndex}; + updateGraphMetadata(lastBlock, blockUpdates); + augmentedMetadata_->push_back(lastBlock); } } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 3846361dc5..6f8aa447e7 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -307,7 +307,7 @@ addLinkAndDiscoverTest(AlgorithmTest) addLinkAndDiscoverTestSerial(CompressedRelationsTest index) -addLinkAndDiscoverTestSerial(PrefilterExpressionIndexTest sparqlExpressions index) +addLinkAndDiscoverTestSerial(PrefilterExpressionIndexTest sparqlExpressions sparqlExpressions parser index) addLinkAndDiscoverTestSerial(GetPrefilterExpressionFromSparqlExpressionTest sparqlExpressions index) diff --git a/test/LocatedTriplesTest.cpp b/test/LocatedTriplesTest.cpp index c4364023fa..4a46644aff 100644 --- a/test/LocatedTriplesTest.cpp +++ b/test/LocatedTriplesTest.cpp @@ -29,10 +29,12 @@ auto IT = [](const auto& c1, const auto& c2, const auto& c3, int graph = g) { auto PT = [](const auto& c1, const auto& c2, const auto& c3, int graph = g) { return CompressedBlockMetadata::PermutedTriple{V(c1), V(c2), V(c3), V(graph)}; }; -auto CBM = [](const auto firstTriple, const auto lastTriple) { +auto CBM = [](const auto firstTriple, const auto lastTriple, + CompressedBlockMetadata::GraphInfo graphs = std::nullopt) { size_t dummyBlockIndex = 0; - return CompressedBlockMetadata{{{}, 0, firstTriple, lastTriple, {}, false}, - dummyBlockIndex}; + return CompressedBlockMetadata{ + {{}, 0, firstTriple, lastTriple, std::move(graphs), false}, + dummyBlockIndex}; }; auto numBlocks = @@ -750,10 +752,12 @@ TEST_F(LocatedTriplesTest, augmentedMetadata) { Span{T1}, metadata, {0, 1, 2}, false, handle)); expectedAugmentedMetadata[0] = CBM(T1.toPermutedTriple(), PT1); + expectedAugmentedMetadata[0].containsDuplicatesWithDifferentGraphs_ = true; EXPECT_THAT(locatedTriplesPerBlock.getAugmentedMetadata(), testing::ElementsAreArray(expectedAugmentedMetadata)); // T2 is inside block 1. Borders don't change. + expectedAugmentedMetadata[1].containsDuplicatesWithDifferentGraphs_ = true; locatedTriplesPerBlock.add(LocatedTriple::locateTriplesInPermutation( Span{T2}, metadata, {0, 1, 2}, true, handle)); @@ -762,6 +766,7 @@ TEST_F(LocatedTriplesTest, augmentedMetadata) { // T3 is equal to PT4, the beginning of block 2. All update (update and // delete) add to the block borders. Borders don't change. + expectedAugmentedMetadata[2].containsDuplicatesWithDifferentGraphs_ = true; locatedTriplesPerBlock.add(LocatedTriple::locateTriplesInPermutation( Span{T3}, metadata, {0, 1, 2}, false, handle)); @@ -774,6 +779,7 @@ TEST_F(LocatedTriplesTest, augmentedMetadata) { Span{T4}, metadata, {0, 1, 2}, true, handle)); expectedAugmentedMetadata[4] = CBM(T4.toPermutedTriple(), PT8); + expectedAugmentedMetadata[4].containsDuplicatesWithDifferentGraphs_ = true; EXPECT_THAT(locatedTriplesPerBlock.getAugmentedMetadata(), testing::ElementsAreArray(expectedAugmentedMetadata)); @@ -781,6 +787,9 @@ TEST_F(LocatedTriplesTest, augmentedMetadata) { locatedTriplesPerBlock.erase(4, handles[0]); expectedAugmentedMetadata[4] = CBM(PT8, PT8); + // The block 4 has no more updates, so we restore the info about the block + // having no duplicates from the original metadata. + expectedAugmentedMetadata[4].containsDuplicatesWithDifferentGraphs_ = false; EXPECT_THAT(locatedTriplesPerBlock.getAugmentedMetadata(), testing::ElementsAreArray(expectedAugmentedMetadata)); @@ -797,6 +806,124 @@ TEST_F(LocatedTriplesTest, augmentedMetadata) { } } +// _____________________________________________________________________________ +TEST_F(LocatedTriplesTest, augmentedMetadataGraphInfo) { + // Create a vector that is automatically converted to a span. + using Span = std::vector>; + + auto PT1 = PT(1, 10, 10); + auto PT2 = PT(2, 10, 10); + auto PT3 = PT(2, 15, 20); + // Two blocks, one without graph info, and one with graph info. + const std::vector metadata = { + CBM(PT1, PT1), CBM(PT2, PT3, std::vector{V(13)})}; + std::vector expectedAugmentedMetadata{metadata}; + + auto T1 = IT( + 1, 10, 10, + 12); // Before block 0 (because `12` is smaller than the default graph) + auto T2 = IT(1, 10, 10, + 99999999); // Becomes the lower bound of block 1, although it + // only differs in the graph info. + auto T3 = IT(2, 12, 10, 17); // Inside block 1, add graph 17. + auto T4 = IT(2, 12, 10, 18); // Inside block 1, add graph 18. + + auto T5 = IT(20, 30, 40, 19); // After the last block. + + ad_utility::SharedCancellationHandle handle = + std::make_shared>(); + + { + LocatedTriplesPerBlock locatedTriplesPerBlock; + locatedTriplesPerBlock.setOriginalMetadata(metadata); + + // Delete the located triples {T1 ... T4} + locatedTriplesPerBlock.add(LocatedTriple::locateTriplesInPermutation( + Span{T1, T2, T3, T4}, metadata, {0, 1, 2}, false, handle)); + + // All the blocks have updates, so their value of `containsDuplicates..` is + // set to `true`. + expectedAugmentedMetadata[0] = CBM(T1.toPermutedTriple(), PT1); + expectedAugmentedMetadata[1].firstTriple_ = T2.toPermutedTriple(); + expectedAugmentedMetadata[0].containsDuplicatesWithDifferentGraphs_ = true; + expectedAugmentedMetadata[1].containsDuplicatesWithDifferentGraphs_ = true; + + // Note: the GraphInfo hasn't changed, because the new triples all were + // deleted. + EXPECT_THAT(locatedTriplesPerBlock.getAugmentedMetadata(), + testing::ElementsAreArray(expectedAugmentedMetadata)); + } + { + expectedAugmentedMetadata = metadata; + LocatedTriplesPerBlock locatedTriplesPerBlock; + locatedTriplesPerBlock.setOriginalMetadata(metadata); + + // Add the located triples {T1 ... T5} + locatedTriplesPerBlock.add(LocatedTriple::locateTriplesInPermutation( + Span{T1, T2, T3, T4, T5}, metadata, {0, 1, 2}, true, handle)); + + expectedAugmentedMetadata[0] = CBM(T1.toPermutedTriple(), PT1); + expectedAugmentedMetadata[1].firstTriple_ = T2.toPermutedTriple(); + expectedAugmentedMetadata[1].graphInfo_.value() = + std::vector{V(13), V(17), V(18), V(99999999)}; + + // We have added a triple `T5` after the last block, so there now is an + // additional block, which also stores the correct graph info. + expectedAugmentedMetadata.push_back( + CBM(T5.toPermutedTriple(), T5.toPermutedTriple(), std::vector{V(19)})); + + // The automatically added metadata for the last block also has the correct + // block index and number of columns, so we have to properly initialize it. + expectedAugmentedMetadata.back().blockIndex_ = 2; + expectedAugmentedMetadata.back().offsetsAndCompressedSize_.resize(4, + {0, 0}); + + // All the blocks have updates, so their value of `containsDuplicates..` is + // set to `true`. + expectedAugmentedMetadata[0].containsDuplicatesWithDifferentGraphs_ = true; + expectedAugmentedMetadata[1].containsDuplicatesWithDifferentGraphs_ = true; + expectedAugmentedMetadata[2].containsDuplicatesWithDifferentGraphs_ = true; + + // Note: the GraphInfo hasn't changed, because the new triples all were + // deleted. + auto actualMetadata = locatedTriplesPerBlock.getAugmentedMetadata(); + EXPECT_THAT(actualMetadata, + testing::ElementsAreArray(expectedAugmentedMetadata)); + + // Test the case that a block loses its graph info if the added located + // triples have too many distinct graphs. + ASSERT_TRUE(actualMetadata[1].graphInfo_.has_value()); + std::vector> triples; + // Note: The `30` is an offset to guarantee that the added graphs are not + // contained in the located triples before. + for (size_t i = 30; i < 30 + 2 * MAX_NUM_GRAPHS_STORED_IN_BLOCK_METADATA; + ++i) { + auto tr = T3; + tr.ids_.at(ADDITIONAL_COLUMN_GRAPH_ID) = V(i); + triples.push_back(tr); + } + + size_t numGraphsBefore = actualMetadata[1].graphInfo_.value().size(); + size_t numGraphsToMax = + MAX_NUM_GRAPHS_STORED_IN_BLOCK_METADATA - numGraphsBefore; + + // Add the exact amount of graphs such that we are at the maximum number of + // stored graphs. + locatedTriplesPerBlock.add(LocatedTriple::locateTriplesInPermutation( + std::span{triples}.subspan(0, numGraphsToMax), metadata, {0, 1, 2}, + true, handle)); + actualMetadata = locatedTriplesPerBlock.getAugmentedMetadata(); + ASSERT_TRUE(actualMetadata[1].graphInfo_.has_value()); + + // Adding one more graph will exceed the maximum. + locatedTriplesPerBlock.add(LocatedTriple::locateTriplesInPermutation( + std::span{triples}.subspan(numGraphsToMax, numGraphsToMax + 1), + metadata, {0, 1, 2}, true, handle)); + actualMetadata = locatedTriplesPerBlock.getAugmentedMetadata(); + ASSERT_FALSE(actualMetadata[1].graphInfo_.has_value()); + } +} + TEST_F(LocatedTriplesTest, debugPrints) { using LT = LocatedTriple; diff --git a/test/PrefilterExpressionIndexTest.cpp b/test/PrefilterExpressionIndexTest.cpp index 1af72ed896..2dd2a7c77a 100644 --- a/test/PrefilterExpressionIndexTest.cpp +++ b/test/PrefilterExpressionIndexTest.cpp @@ -2,7 +2,7 @@ // Chair of Algorithms and Data Structures // Author: Hannes Baumann -#include +#include #include @@ -211,18 +211,32 @@ class PrefilterExpressionOnMetadataTest : public ::testing::Test { //______________________________________________________________________________ TEST_F(PrefilterExpressionOnMetadataTest, testBlockFormatForDebugging) { - EXPECT_EQ( - "#BlockMetadata\n(first) Triple: V:10 D:33.000000 I:0 V:0\n(last) " - "Triple: V:10 D:33.000000 I:0 V:0\nnum. rows: 0.\n", - (std::stringstream() << b5).str()); - EXPECT_EQ( - "#BlockMetadata\n(first) Triple: V:10 D:33.000000 I:-4 V:0\n(last) " - "Triple: V:10 D:33.000000 D:2.000000 V:0\nnum. rows: 0.\n", - (std::stringstream() << b11).str()); - EXPECT_EQ( - "#BlockMetadata\n(first) Triple: V:10 D:33.000000 V:14 V:0\n(last) " - "Triple: V:10 D:33.000000 V:17 V:0\nnum. rows: 0.\n", - (std::stringstream() << b21).str()); + auto toString = [](const CompressedBlockMetadata& b) { + return (std::stringstream{} << b).str(); + }; + + auto matcher = [&toString](const std::string& substring) { + return ::testing::ResultOf(toString, ::testing::HasSubstr(substring)); + }; + EXPECT_THAT( + b5, + matcher( + "#BlockMetadata\n(first) Triple: I:0 V:10 D:33.000000 V:0\n(last) " + "Triple: I:0 V:10 D:33.000000 V:0\nnum. rows: 0.\n")); + EXPECT_THAT( + b11, + matcher( + "#BlockMetadata\n(first) Triple: I:-4 V:10 D:33.000000 V:0\n(last) " + "Triple: D:2.000000 V:10 D:33.000000 V:0\nnum. rows: 0.\n")); + EXPECT_THAT( + b21, + matcher( + "#BlockMetadata\n(first) Triple: V:14 V:10 D:33.000000 V:0\n(last) " + "Triple: V:17 V:10 D:33.000000 V:0\nnum. rows: 0.\n")); + + auto blockWithGraphInfo = b21; + blockWithGraphInfo.graphInfo_.emplace({IntId(12), IntId(13)}); + EXPECT_THAT(blockWithGraphInfo, matcher("Graphs: I:12, I:13\n")); } // Test Relational Expressions From 863a337e4d1171b5c82ead61847d45d7e9205eaa Mon Sep 17 00:00:00 2001 From: Hannes Baumann <116301375+realHannes@users.noreply.github.com> Date: Mon, 2 Dec 2024 09:05:42 +0100 Subject: [PATCH 76/81] Apply binary search filter expressions directly on the block metadata of `Index Scan`s (#1619) With this PR, filter expressions that can be evaluated via binary search on a sorted input are directly evaluated on the block metadata of an IndexScan. For example in a query that contains `{ ?s ?p ?o FILTER (?o > 3)`} only the blocks of the full index scan (sorted by the object) are read from disk that according to their metadata might contain values `> 3`. Currently this mechanism has the following limitations: 1. It can only be applied if the IndexScan directly is the child of the FILTER clause 2. It can only be applied to logical expressions (AND/OR/NOT) and to relational expressions (greater than, equal to, etc.) between a variable and a constant. Currently the constant can not yet be an IRI or Literal. --- src/engine/Filter.cpp | 4 ++-- src/engine/QueryExecutionTree.cpp | 2 +- src/engine/QueryExecutionTree.h | 2 +- test/PrefilterExpressionIndexTest.cpp | 20 +++++++++----------- 4 files changed, 13 insertions(+), 15 deletions(-) diff --git a/src/engine/Filter.cpp b/src/engine/Filter.cpp index 3de901d3e7..7e0c66b551 100644 --- a/src/engine/Filter.cpp +++ b/src/engine/Filter.cpp @@ -48,8 +48,8 @@ string Filter::getDescriptor() const { void Filter::setPrefilterExpressionForChildren() { std::vector prefilterPairs = _expression.getPrefilterExpressionForMetadata(); - auto optNewSubTree = - _subtree->setPrefilterGetUpdatedQueryExecutionTree(prefilterPairs); + auto optNewSubTree = _subtree->setPrefilterGetUpdatedQueryExecutionTree( + std::move(prefilterPairs)); if (optNewSubTree.has_value()) { _subtree = std::move(optNewSubTree.value()); } diff --git a/src/engine/QueryExecutionTree.cpp b/src/engine/QueryExecutionTree.cpp index 470476b166..c9496fe958 100644 --- a/src/engine/QueryExecutionTree.cpp +++ b/src/engine/QueryExecutionTree.cpp @@ -107,7 +107,7 @@ size_t QueryExecutionTree::getSizeEstimate() { //_____________________________________________________________________________ std::optional> QueryExecutionTree::setPrefilterGetUpdatedQueryExecutionTree( - std::vector& prefilterPairs) const { + std::vector prefilterPairs) const { AD_CONTRACT_CHECK(rootOperation_); VariableToColumnMap varToColMap = getVariableColumns(); std::erase_if(prefilterPairs, [&varToColMap](const auto& pair) { diff --git a/src/engine/QueryExecutionTree.h b/src/engine/QueryExecutionTree.h index 14e0766df2..0eac785f16 100644 --- a/src/engine/QueryExecutionTree.h +++ b/src/engine/QueryExecutionTree.h @@ -100,7 +100,7 @@ class QueryExecutionTree { // the corresponding `Variable` is visible in the `VariableToColumnMap`. std::optional> setPrefilterGetUpdatedQueryExecutionTree( - std::vector& prefilterPairs) const; + std::vector prefilterPairs) const; size_t getDistinctEstimate(size_t col) const { return static_cast(rootOperation_->getSizeEstimate() / diff --git a/test/PrefilterExpressionIndexTest.cpp b/test/PrefilterExpressionIndexTest.cpp index 2dd2a7c77a..bc6e7d17d0 100644 --- a/test/PrefilterExpressionIndexTest.cpp +++ b/test/PrefilterExpressionIndexTest.cpp @@ -219,20 +219,18 @@ TEST_F(PrefilterExpressionOnMetadataTest, testBlockFormatForDebugging) { return ::testing::ResultOf(toString, ::testing::HasSubstr(substring)); }; EXPECT_THAT( - b5, - matcher( - "#BlockMetadata\n(first) Triple: I:0 V:10 D:33.000000 V:0\n(last) " - "Triple: I:0 V:10 D:33.000000 V:0\nnum. rows: 0.\n")); + b5, matcher("#BlockMetadata\n(first) Triple: V:10 D:33.000000 I:0 " + "V:0\n(last) Triple: V:10 D:33.000000 I:0 V:0\nnum. rows: " + "0.\n")); EXPECT_THAT( b11, - matcher( - "#BlockMetadata\n(first) Triple: I:-4 V:10 D:33.000000 V:0\n(last) " - "Triple: D:2.000000 V:10 D:33.000000 V:0\nnum. rows: 0.\n")); + matcher("#BlockMetadata\n(first) Triple: V:10 D:33.000000 I:-4 " + "V:0\n(last) Triple: V:10 D:33.000000 D:2.000000 V:0\nnum. rows: " + "0.\n")); EXPECT_THAT( - b21, - matcher( - "#BlockMetadata\n(first) Triple: V:14 V:10 D:33.000000 V:0\n(last) " - "Triple: V:17 V:10 D:33.000000 V:0\nnum. rows: 0.\n")); + b21, matcher("#BlockMetadata\n(first) Triple: V:10 D:33.000000 V:14 " + "V:0\n(last) Triple: V:10 D:33.000000 V:17 V:0\nnum. rows: " + "0.\n")); auto blockWithGraphInfo = b21; blockWithGraphInfo.graphInfo_.emplace({IntId(12), IntId(13)}); From 513dcd13f6a1bbe0ad05ffd7c844fa889fd0da72 Mon Sep 17 00:00:00 2001 From: realHannes Date: Tue, 3 Dec 2024 12:08:38 +0100 Subject: [PATCH 77/81] test prefiltering with LocalVocabEntry reference values --- .../PrefilterExpressionIndex.cpp | 26 +- test/CMakeLists.txt | 2 +- test/PrefilterExpressionIndexTest.cpp | 233 +++++++++++------- test/PrefilterExpressionTestHelpers.h | 28 ++- 4 files changed, 189 insertions(+), 100 deletions(-) diff --git a/src/engine/sparqlExpressions/PrefilterExpressionIndex.cpp b/src/engine/sparqlExpressions/PrefilterExpressionIndex.cpp index 51a3b0e779..9a3e0a1739 100644 --- a/src/engine/sparqlExpressions/PrefilterExpressionIndex.cpp +++ b/src/engine/sparqlExpressions/PrefilterExpressionIndex.cpp @@ -262,18 +262,19 @@ std::vector RelationalExpression::evaluateImpl( // Helper to retrieve the reference `ValueId` from `IdOrLocalVocabEntry` // variant. - const auto getValueIdFromReferenceValue = - [](const IdOrLocalVocabEntry& referenceValue, LocalVocab& localVocab) { - if (std::holds_alternative(referenceValue)) { - return std::get(referenceValue); - } else if (std::holds_alternative(referenceValue)) { - return Id::makeFromLocalVocabIndex( - localVocab.getIndexAndAddIfNotContained( - std::get(referenceValue))); - } else { - AD_FAIL(); - } - }; + auto getValueIdFromReferenceValue = + [](const IdOrLocalVocabEntry& referenceValue, + LocalVocab& localVocab) -> ValueId { + if (std::holds_alternative(referenceValue)) { + return std::get(referenceValue); + } else if (std::holds_alternative(referenceValue)) { + return Id::makeFromLocalVocabIndex( + localVocab.getIndexAndAddIfNotContained( + std::get(referenceValue))); + } else { + AD_FAIL(); + } + }; auto referenceId = getValueIdFromReferenceValue(referenceValue_, vocab); // Use getRangesForId (from valueIdComparators) to extract the ranges @@ -489,7 +490,6 @@ template class LogicalExpression; namespace detail { - //______________________________________________________________________________ template static std::unique_ptr makeMirroredExpression( diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 6f8aa447e7..9dd3a733a9 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -307,7 +307,7 @@ addLinkAndDiscoverTest(AlgorithmTest) addLinkAndDiscoverTestSerial(CompressedRelationsTest index) -addLinkAndDiscoverTestSerial(PrefilterExpressionIndexTest sparqlExpressions sparqlExpressions parser index) +addLinkAndDiscoverTestSerial(PrefilterExpressionIndexTest engine) addLinkAndDiscoverTestSerial(GetPrefilterExpressionFromSparqlExpressionTest sparqlExpressions index) diff --git a/test/PrefilterExpressionIndexTest.cpp b/test/PrefilterExpressionIndexTest.cpp index bc6e7d17d0..642fc98913 100644 --- a/test/PrefilterExpressionIndexTest.cpp +++ b/test/PrefilterExpressionIndexTest.cpp @@ -21,6 +21,9 @@ namespace { using namespace prefilterExpressions; using namespace makeFilterExpression; +using namespace filterHelper; + +constexpr auto getId = getValueIdFromIdOrLocalVocabEntry; //______________________________________________________________________________ /* @@ -49,12 +52,32 @@ const Id GraphId = VocabId(0); //______________________________________________________________________________ class PrefilterExpressionOnMetadataTest : public ::testing::Test { public: + // Not directly used. However, given that we depend on LocalVocab values + // during evaluation an active Index + global vocabulary is required. + QueryExecutionContext* qet = ad_utility::testing::getQec(); + LocalVocab vocab{}; const Id referenceDate1 = DateId(DateParser, "1999-11-11"); const Id referenceDate2 = DateId(DateParser, "2005-02-27"); const Id undef = Id::makeUndefined(); const Id falseId = BoolId(false); const Id trueId = BoolId(true); const Id referenceDateEqual = DateId(DateParser, "2000-01-01"); + const LocalVocabEntry augsburg = LVE("\"Augsburg\""); + const LocalVocabEntry berlin = LVE("\"Berlin\""); + const LocalVocabEntry düsseldorf = LVE("\"Düsseldorf\""); + const LocalVocabEntry frankfurt = LVE("\"Frankfurt\""); + const LocalVocabEntry hamburg = LVE("\"Hamburg\""); + const LocalVocabEntry köln = LVE("\"Köln\""); + const LocalVocabEntry münchen = LVE("\"München\""); + const LocalVocabEntry stuttgart = LVE("\"Stuttgart\""); + const Id idAugsburg = getId(augsburg, vocab); + const Id idBerlin = getId(berlin, vocab); + const Id idDüsseldorf = getId(düsseldorf, vocab); + const Id idFrankfurt = getId(frankfurt, vocab); + const Id idHamburg = getId(hamburg, vocab); + const Id idKöln = getId(köln, vocab); + const Id idMünchen = getId(münchen, vocab); + const Id idStuttgart = getId(stuttgart, vocab); // Define BlockMetadata const BlockMetadata b1 = makeBlock(undef, undef); @@ -80,12 +103,12 @@ class PrefilterExpressionOnMetadataTest : public ::testing::Test { const BlockMetadata b15 = makeBlock(DoubleId(-1.23), DoubleId(-6.25)); const BlockMetadata b16 = makeBlock(DoubleId(-6.25), DoubleId(-6.25)); const BlockMetadata b17 = makeBlock(DoubleId(-10.42), DoubleId(-12.00)); - const BlockMetadata b18 = makeBlock(DoubleId(-14.01), VocabId(0)); - const BlockMetadata b19 = makeBlock(VocabId(10), VocabId(14)); - const BlockMetadata b20 = makeBlock(VocabId(14), VocabId(14)); - const BlockMetadata b21 = makeBlock(VocabId(14), VocabId(17)); + const BlockMetadata b18 = makeBlock(DoubleId(-14.01), idAugsburg); + const BlockMetadata b19 = makeBlock(idDüsseldorf, idHamburg); + const BlockMetadata b20 = makeBlock(idHamburg, idHamburg); + const BlockMetadata b21 = makeBlock(idHamburg, idMünchen); const BlockMetadata b22 = - makeBlock(VocabId(20), DateId(DateParser, "1999-12-12")); + makeBlock(idStuttgart, DateId(DateParser, "1999-12-12")); const BlockMetadata b23 = makeBlock(DateId(DateParser, "2000-01-01"), DateId(DateParser, "2000-01-01")); const BlockMetadata b24 = @@ -228,9 +251,10 @@ TEST_F(PrefilterExpressionOnMetadataTest, testBlockFormatForDebugging) { "V:0\n(last) Triple: V:10 D:33.000000 D:2.000000 V:0\nnum. rows: " "0.\n")); EXPECT_THAT( - b21, matcher("#BlockMetadata\n(first) Triple: V:10 D:33.000000 V:14 " - "V:0\n(last) Triple: V:10 D:33.000000 V:17 V:0\nnum. rows: " - "0.\n")); + b21, + matcher("#BlockMetadata\n(first) Triple: V:10 D:33.000000 L:\"Hamburg\" " + "V:0\n(last) Triple: V:10 D:33.000000 L:\"M\xC3\xBCnchen\" " + "V:0\nnum. rows: 0.\n")); auto blockWithGraphInfo = b21; blockWithGraphInfo.graphInfo_.emplace({IntId(12), IntId(13)}); @@ -250,10 +274,10 @@ TEST_F(PrefilterExpressionOnMetadataTest, testLessThanExpressions) { makeTest(lt(DoubleId(-14.01)), {b18}); makeTest(lt(DoubleId(-11.22)), {b17, b18}, true); makeTest(lt(DoubleId(-4.121)), {b9, b15, b16, b17, b18}); - makeTest(lt(VocabId(0)), {b18}); - makeTest(lt(VocabId(12)), {b18, b19}); - makeTest(lt(VocabId(14)), {b18, b19}, true); - makeTest(lt(VocabId(16)), {b18, b19, b20, b21}); + makeTest(lt(augsburg), {b18}); + makeTest(lt(frankfurt), {b18, b19}); + makeTest(lt(hamburg), {b18, b19}, true); + makeTest(lt(münchen), {b18, b19, b20, b21}); makeTest(lt(IntId(100)), {b5, b6, b7, b8, b9, b10, b12, b13, b14, b15, b16, b17, b18}); makeTest(lt(undef), {}); @@ -281,9 +305,9 @@ TEST_F(PrefilterExpressionOnMetadataTest, testLessEqualExpressions) { {b5, b6, b9, b10, b11, b12, b15, b16, b17, b18}); makeTest(le(DoubleId(-11.99999999999999)), {b17, b18}, true); makeTest(le(DoubleId(-14.03)), {b18}); - makeTest(le(VocabId(0)), {b18}); - makeTest(le(VocabId(11)), {b18, b19}); - makeTest(le(VocabId(14)), {b18, b19, b20, b21}, true); + makeTest(le(LVE("\"Aachen\"")), {b18}); + makeTest(le(frankfurt), {b18, b19}); + makeTest(le(hamburg), {b18, b19, b20, b21}, true); makeTest(le(undef), {}); makeTest(le(falseId), {b2, b3}); makeTest(le(trueId), {b2, b3, b4}, true); @@ -308,9 +332,9 @@ TEST_F(PrefilterExpressionOnMetadataTest, testGreaterThanExpression) { makeTest(gt(IntId(4)), {b6, b7, b8, b11, b14}); makeTest(gt(IntId(-4)), {b5, b6, b7, b8, b11, b12, b13, b14, b15}); makeTest(gt(IntId(33)), {}); - makeTest(gt(VocabId(22)), {b22}); - makeTest(gt(VocabId(14)), {b21, b22}, true); - makeTest(gt(VocabId(12)), {b19, b20, b21, b22}); + makeTest(gt(stuttgart), {b22}); + makeTest(gt(hamburg), {b21, b22}, true); + makeTest(gt(berlin), {b19, b20, b21, b22}); makeTest(gt(undef), {}, true); makeTest(gt(falseId), {b4}, true); makeTest(gt(trueId), {}); @@ -338,9 +362,9 @@ TEST_F(PrefilterExpressionOnMetadataTest, testGreaterEqualExpression) { {b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18}); makeTest(ge(DoubleId(7.999999)), {b8, b11, b14}); makeTest(ge(DoubleId(10.0001)), {}); - makeTest(ge(VocabId(14)), {b18, b19, b20, b21, b22}, true); - makeTest(ge(VocabId(10)), {b18, b19, b20, b21, b22}); - makeTest(ge(VocabId(17)), {b18, b21, b22}); + makeTest(ge(hamburg), {b18, b19, b20, b21, b22}, true); + makeTest(ge(düsseldorf), {b18, b19, b20, b21, b22}); + makeTest(ge(münchen), {b18, b21, b22}); makeTest(ge(undef), {}, true); makeTest(ge(falseId), {b2, b3, b4}, true); makeTest(ge(trueId), {b4}); @@ -364,10 +388,10 @@ TEST_F(PrefilterExpressionOnMetadataTest, testEqualExpression) { makeTest(eq(IntId(2)), {b6, b11, b12}, true); makeTest(eq(DoubleId(5.5)), {b7, b11, b14}); makeTest(eq(DoubleId(1.5)), {b6, b11}); - makeTest(eq(VocabId(1)), {b18}); - makeTest(eq(VocabId(14)), {b18, b19, b20, b21}, true); - makeTest(eq(VocabId(11)), {b18, b19}); - makeTest(eq(VocabId(17)), {b18, b21}); + makeTest(eq(berlin), {b18}); + makeTest(eq(hamburg), {b18, b19, b20, b21}, true); + makeTest(eq(frankfurt), {b18, b19}); + makeTest(eq(köln), {b18, b21}); makeTest(eq(IntId(-4)), {b10, b11, b15}, true); makeTest(eq(trueId), {b4}); makeTest(eq(referenceDate1), {b22}); @@ -397,10 +421,10 @@ TEST_F(PrefilterExpressionOnMetadataTest, testNotEqualExpression) { makeTest(neq(DoubleId(-101.23)), {b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18}, true); - makeTest(neq(VocabId(0)), {b19, b20, b21, b22}); - makeTest(neq(VocabId(7)), {b18, b19, b20, b21, b22}, true); - makeTest(neq(VocabId(14)), {b18, b19, b21, b22}); - makeTest(neq(VocabId(17)), {b18, b19, b20, b21, b22}); + makeTest(neq(augsburg), {b19, b20, b21, b22}); + makeTest(neq(berlin), {b18, b19, b20, b21, b22}, true); + makeTest(neq(hamburg), {b18, b19, b21, b22}); + makeTest(neq(münchen), {b18, b19, b20, b21, b22}); makeTest(neq(undef), {}); makeTest(neq(falseId), {b4}, true); makeTest(neq(referenceDateEqual), {b22, b24}); @@ -413,14 +437,13 @@ TEST_F(PrefilterExpressionOnMetadataTest, testNotEqualExpression) { // Note: the `makeTest` function automatically adds the blocks with mixed // datatypes to the expected result. TEST_F(PrefilterExpressionOnMetadataTest, testAndExpression) { - makeTest(andExpr(ge(VocabId(10)), gt(VocabId(10))), {b19, b20, b21, b22}); - makeTest(andExpr(ge(VocabId(10)), ge(VocabId(10))), {b19, b20, b21, b22}, - true); - makeTest(andExpr(ge(VocabId(12)), gt(VocabId(17))), {b22}); - makeTest(andExpr(ge(VocabId(12)), gt(VocabId(17))), {b22}, true); - makeTest(andExpr(ge(VocabId(10)), lt(VocabId(14))), {b19}, true); - makeTest(andExpr(le(VocabId(0)), lt(VocabId(10))), {b18}); - makeTest(andExpr(le(VocabId(17)), lt(VocabId(17))), {b18, b19, b20, b21}); + makeTest(andExpr(ge(düsseldorf), gt(düsseldorf)), {b19, b20, b21, b22}); + makeTest(andExpr(ge(düsseldorf), ge(düsseldorf)), {b19, b20, b21, b22}, true); + makeTest(andExpr(ge(frankfurt), gt(münchen)), {b22}); + makeTest(andExpr(ge(frankfurt), gt(münchen)), {b22}, true); + makeTest(andExpr(ge(düsseldorf), lt(hamburg)), {b19}, true); + makeTest(andExpr(le(augsburg), lt(düsseldorf)), {b18}); + makeTest(andExpr(le(münchen), lt(münchen)), {b18, b19, b20, b21}); makeTest(andExpr(ge(DoubleId(-6.25)), lt(IntId(-7))), {}); makeTest(andExpr(gt(DoubleId(-6.25)), lt(DoubleId(-6.25))), {}); makeTest(andExpr(gt(IntId(0)), lt(IntId(0))), {}); @@ -453,13 +476,13 @@ TEST_F(PrefilterExpressionOnMetadataTest, testAndExpression) { // Note: the `makeTest` function automatically adds the blocks with mixed // datatypes to the expected result. TEST_F(PrefilterExpressionOnMetadataTest, testOrExpression) { - makeTest(orExpr(lt(VocabId(22)), le(VocabId(0))), {b18, b19, b20, b21}); - makeTest(orExpr(le(VocabId(0)), ge(VocabId(16))), {b18, b21, b22}); - makeTest(orExpr(gt(VocabId(17)), ge(VocabId(17))), {b21, b22}); - makeTest(orExpr(lt(DoubleId(-5.95)), eq(VocabId(14))), + makeTest(orExpr(lt(stuttgart), le(augsburg)), {b18, b19, b20, b21}); + makeTest(orExpr(le(augsburg), ge(köln)), {b18, b21, b22}); + makeTest(orExpr(gt(münchen), ge(münchen)), {b21, b22}); + makeTest(orExpr(lt(DoubleId(-5.95)), eq(hamburg)), {b9, b15, b16, b17, b18, b19, b20, b21}); - makeTest(orExpr(eq(DoubleId(0)), neq(VocabId(14))), - {b5, b6, b11, b18, b19, b21}, true); + makeTest(orExpr(eq(DoubleId(0)), neq(hamburg)), {b5, b6, b11, b18, b19, b21}, + true); makeTest(orExpr(eq(DoubleId(0)), eq(DoubleId(-6.25))), {b5, b6, b11, b15, b16, b18}, true); makeTest(orExpr(gt(undef), le(IntId(-6))), {b9, b15, b16, b17, b18}); @@ -467,8 +490,8 @@ TEST_F(PrefilterExpressionOnMetadataTest, testOrExpression) { makeTest(orExpr(eq(IntId(0)), orExpr(lt(IntId(-10)), gt(IntId(8)))), {b5, b6, b8, b11, b14, b17, b18}, true); makeTest(orExpr(gt(referenceDate2), eq(trueId)), {b4}); - makeTest(orExpr(eq(VocabId(17)), orExpr(lt(VocabId(0)), gt(VocabId(20)))), - {b21, b22}, true); + makeTest(orExpr(eq(münchen), orExpr(lt(augsburg), gt(stuttgart))), {b21, b22}, + true); makeTest(orExpr(eq(undef), gt(referenceDateEqual)), {b24}); makeTest(orExpr(gt(IntId(8)), gt(DoubleId(22.1))), {b8, b14}); makeTest(orExpr(lt(DoubleId(-8.25)), le(IntId(-10))), {b9, b17, b18}, true); @@ -482,7 +505,7 @@ TEST_F(PrefilterExpressionOnMetadataTest, testOrExpression) { orExpr(eq(DoubleId(-6.25)), lt(DoubleId(-12)))), {b4, b5, b6, b7, b11, b14, b15, b16, b18}); makeTest(orExpr(le(trueId), gt(falseId)), {b2, b3, b4}, true); - makeTest(orExpr(eq(VocabId(0)), eq(DoubleId(0.25))), {b6, b11, b18}, true); + makeTest(orExpr(eq(augsburg), eq(DoubleId(0.25))), {b6, b11, b18}, true); } //______________________________________________________________________________ @@ -490,10 +513,10 @@ TEST_F(PrefilterExpressionOnMetadataTest, testOrExpression) { // Note: the `makeTest` function automatically adds the blocks with mixed // datatypes to the expected result. TEST_F(PrefilterExpressionOnMetadataTest, testNotExpression) { - makeTest(notExpr(eq(VocabId(2))), {b18, b19, b20, b21, b22}, true); - makeTest(notExpr(eq(VocabId(14))), {b18, b19, b21, b22}); - makeTest(notExpr(neq(VocabId(14))), {b19, b20, b21}, true); - makeTest(notExpr(gt(VocabId(2))), {b18}); + makeTest(notExpr(eq(berlin)), {b18, b19, b20, b21, b22}, true); + makeTest(notExpr(eq(hamburg)), {b18, b19, b21, b22}); + makeTest(notExpr(neq(hamburg)), {b19, b20, b21}, true); + makeTest(notExpr(gt(berlin)), {b18}); makeTest(notExpr(lt(DoubleId(-14.01))), {b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18}); makeTest(notExpr(ge(DoubleId(-14.01))), {b18}); @@ -512,7 +535,7 @@ TEST_F(PrefilterExpressionOnMetadataTest, testNotExpression) { makeTest(notExpr(notExpr(eq(IntId(0)))), {b4, b5, b6, b11}, true); makeTest(notExpr(notExpr(neq(DoubleId(-6.25)))), {b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b17, b18}); - makeTest(notExpr(notExpr(lt(VocabId(10)))), {b18}); + makeTest(notExpr(notExpr(lt(düsseldorf))), {b18}); makeTest(notExpr(notExpr(ge(DoubleId(3.99)))), {b6, b7, b8, b11, b13, b14}, true); makeTest(notExpr(andExpr(le(IntId(0)), ge(IntId(0)))), @@ -528,11 +551,11 @@ TEST_F(PrefilterExpressionOnMetadataTest, testNotExpression) { {b6, b7, b11, b12, b13, b14}, true); makeTest(notExpr(orExpr(ge(DoubleId(0)), gt(IntId(-10)))), {b9, b11, b17, b18}, true); - makeTest(notExpr(orExpr(lt(VocabId(10)), gt(VocabId(10)))), {b19}); + makeTest(notExpr(orExpr(lt(düsseldorf), gt(düsseldorf))), {b19}); makeTest(notExpr(orExpr(lt(DoubleId(-4)), gt(IntId(-4)))), {b10, b11, b15}, true); - makeTest(notExpr(orExpr(gt(IntId(-42)), ge(VocabId(0)))), {b11}, true); - makeTest(notExpr(orExpr(ge(VocabId(14)), gt(VocabId(15)))), {b18, b19}); + makeTest(notExpr(orExpr(gt(IntId(-42)), ge(augsburg))), {b11}, true); + makeTest(notExpr(orExpr(ge(hamburg), gt(köln))), {b18, b19}); } //______________________________________________________________________________ @@ -556,13 +579,11 @@ TEST_F(PrefilterExpressionOnMetadataTest, {b10, b11, b15}); makeTest(orExpr(notExpr(andExpr(lt(IntId(10)), gt(IntId(5)))), eq(IntId(0))), {b4, b5, b6, b7, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18}); - makeTest(andExpr(orExpr(gt(VocabId(16)), le(VocabId(5))), gt(DoubleId(7.25))), - {}, true); + makeTest(andExpr(orExpr(gt(köln), le(berlin)), gt(DoubleId(7.25))), {}, true); makeTest(andExpr(lt(falseId), orExpr(lt(IntId(10)), gt(DoubleId(17.25)))), {}); - makeTest( - orExpr(andExpr(gt(VocabId(16)), ge(VocabId(17))), gt(DoubleId(7.25))), - {b8, b14, b18, b21, b22}); + makeTest(orExpr(andExpr(gt(köln), ge(münchen)), gt(DoubleId(7.25))), + {b8, b14, b18, b21, b22}); makeTest(orExpr(eq(trueId), andExpr(gt(referenceDate1), lt(referenceDate2))), {b4, b22, b23}, true); } @@ -615,6 +636,7 @@ TEST_F(PrefilterExpressionOnMetadataTest, testMethodClonePrefilterExpression) { makeTestClone(gt(referenceDate2)); makeTestClone(andExpr(lt(VocabId(20)), gt(VocabId(10)))); makeTestClone(neq(IntId(10))); + makeTestClone(le(LVE("\"Hello World\""))); makeTestClone(orExpr(eq(IntId(10)), neq(DoubleId(10)))); makeTestClone(notExpr(ge(referenceDate1))); makeTestClone(notExpr(notExpr(neq(VocabId(0))))); @@ -625,6 +647,8 @@ TEST_F(PrefilterExpressionOnMetadataTest, testMethodClonePrefilterExpression) { orExpr(gt(DoubleId(0.001)), lt(IntId(250))))); makeTestClone(orExpr(orExpr(eq(VocabId(101)), lt(IntId(100))), notExpr(andExpr(lt(VocabId(0)), neq(IntId(100)))))); + makeTestClone(orExpr(orExpr(le(LVE("")), gt(LVE(""))), + neq(LVE("")))); } //______________________________________________________________________________ @@ -635,16 +659,21 @@ TEST_F(PrefilterExpressionOnMetadataTest, testEqualityOperator) { ASSERT_FALSE(*neq(BoolId(true)) == *eq(BoolId(true))); ASSERT_TRUE(*eq(IntId(1)) == *eq(IntId(1))); ASSERT_TRUE(*ge(referenceDate1) == *ge(referenceDate1)); + ASSERT_TRUE(*eq(LVE("")) == *eq(LVE(""))); + ASSERT_FALSE(*gt(LVE("")) == *gt(LVE("\"iri\""))); // NotExpression ASSERT_TRUE(*notExpr(eq(IntId(0))) == *notExpr(eq(IntId(0)))); ASSERT_TRUE(*notExpr(notExpr(ge(VocabId(0)))) == *notExpr(notExpr(ge(VocabId(0))))); + ASSERT_TRUE(*notExpr(le(LVE(""))) == *notExpr(le(LVE("")))); ASSERT_FALSE(*notExpr(gt(IntId(0))) == *eq(IntId(0))); ASSERT_FALSE(*notExpr(andExpr(eq(IntId(1)), eq(IntId(0)))) == *notExpr(ge(VocabId(0)))); // Binary PrefilterExpressions (AND and OR) ASSERT_TRUE(*orExpr(eq(IntId(0)), le(IntId(0))) == *orExpr(eq(IntId(0)), le(IntId(0)))); + ASSERT_TRUE(*orExpr(lt(LVE("\"L\"")), gt(LVE("\"O\""))) == + *orExpr(lt(LVE("\"L\"")), gt(LVE("\"O\"")))); ASSERT_TRUE(*andExpr(le(VocabId(1)), le(IntId(0))) == *andExpr(le(VocabId(1)), le(IntId(0)))); ASSERT_FALSE(*orExpr(eq(IntId(0)), le(IntId(0))) == @@ -657,29 +686,65 @@ TEST_F(PrefilterExpressionOnMetadataTest, testEqualityOperator) { // Test PrefilterExpression content formatting for debugging. TEST(PrefilterExpressionExpressionOnMetadataTest, checkPrintFormattedPrefilterExpression) { - auto expr = lt(IntId(10)); - EXPECT_EQ((std::stringstream() << *expr).str(), - "Prefilter RelationalExpression\nValueId: I:10\n.\n"); - expr = orExpr(eq(VocabId(0)), eq(VocabId(10))); - EXPECT_EQ((std::stringstream() << *expr).str(), - "Prefilter LogicalExpression\nchild1 {Prefilter " - "RelationalExpression\nValueId: V:0\n}child2 {Prefilter " - "RelationalExpression\nValueId: V:10\n}\n.\n"); - expr = neq(DoubleId(8.21)); - EXPECT_EQ((std::stringstream() << *expr).str(), - "Prefilter RelationalExpression\nValueId: D:8.210000\n.\n"); - expr = notExpr(eq(VocabId(0))); - EXPECT_EQ((std::stringstream() << *expr).str(), - "Prefilter NotExpression:\nchild {Prefilter " - "RelationalExpression\nValueId: V:0\n}\n.\n"); - expr = orExpr(le(IntId(0)), ge(IntId(5))); - EXPECT_EQ((std::stringstream() << *expr).str(), - "Prefilter LogicalExpression\nchild1 {Prefilter " - "RelationalExpression\nValueId: I:0\n}child2 {Prefilter " - "RelationalExpression=)>\nValueId: I:5\n}\n.\n"); - expr = andExpr(lt(IntId(20)), gt(IntId(10))); - EXPECT_EQ((std::stringstream() << *expr).str(), - "Prefilter LogicalExpression\nchild1 {Prefilter " - "RelationalExpression\nValueId: I:20\n}child2 {Prefilter " - "RelationalExpression)>\nValueId: I:10\n}\n.\n"); + auto exprToString = [](const PrefilterExpression& expr) { + return (std::stringstream{} << expr).str(); + }; + + auto matcher = [&exprToString](const std::string& substring) { + return ::testing::ResultOf(exprToString, ::testing::Eq(substring)); + }; + + EXPECT_THAT(*lt(IntId(10)), + matcher("Prefilter RelationalExpression\nreferenceValue_ " + ": I:10 .\n.\n")); + EXPECT_THAT( + *orExpr(eq(VocabId(0)), eq(VocabId(10))), + matcher("Prefilter LogicalExpression\nchild1 {Prefilter " + "RelationalExpression\nreferenceValue_ : V:0 .\n}child2 " + "{Prefilter RelationalExpression\nreferenceValue_ : V:10 " + ".\n}\n.\n")); + EXPECT_THAT( + *neq(DoubleId(8.21)), + matcher("Prefilter RelationalExpression\nreferenceValue_ : " + "D:8.210000 .\n.\n")); + EXPECT_THAT( + *notExpr(eq(VocabId(0))), + matcher("Prefilter NotExpression:\nchild {Prefilter " + "RelationalExpression\nreferenceValue_ : V:0 .\n}\n.\n")); + EXPECT_THAT( + *orExpr(le(IntId(0)), ge(IntId(5))), + matcher("Prefilter LogicalExpression\nchild1 {Prefilter " + "RelationalExpression\nreferenceValue_ : I:0 .\n}child2 " + "{Prefilter RelationalExpression=)>\nreferenceValue_ : I:5 " + ".\n}\n.\n")); + EXPECT_THAT( + *andExpr(lt(IntId(20)), gt(IntId(10))), + matcher("Prefilter LogicalExpression\nchild1 {Prefilter " + "RelationalExpression\nreferenceValue_ : I:20 .\n}child2 " + "{Prefilter RelationalExpression)>\nreferenceValue_ : I:10 " + ".\n}\n.\n")); + EXPECT_THAT(*eq(LVE("\"Sophia\"")), + matcher("Prefilter RelationalExpression\nreferenceValue_ " + ": \"Sophia\" .\n.\n")); + EXPECT_THAT(*neq(LVE("")), + matcher("Prefilter RelationalExpression\nreferenceValue_ " + ": .\n.\n")); + EXPECT_THAT( + *andExpr(orExpr(lt(LVE("\"Bob\"")), ge(LVE("\"Max\""))), + neq(LVE("\"Lars\""))), + matcher( + "Prefilter LogicalExpression\nchild1 {Prefilter " + "LogicalExpression\nchild1 {Prefilter " + "RelationalExpression\nreferenceValue_ : \"Bob\" .\n}child2 " + "{Prefilter RelationalExpression=)>\nreferenceValue_ : \"Max\" " + ".\n}\n}child2 {Prefilter " + "RelationalExpression\nreferenceValue_ : \"Lars\" " + ".\n}\n.\n")); + EXPECT_THAT( + *orExpr(neq(LVE("")), neq(LVE(""))), + matcher( + "Prefilter LogicalExpression\nchild1 {Prefilter " + "RelationalExpression\nreferenceValue_ : " + ".\n}child2 {Prefilter RelationalExpression\nreferenceValue_ " + ": .\n}\n.\n")); } diff --git a/test/PrefilterExpressionTestHelpers.h b/test/PrefilterExpressionTestHelpers.h index 934470bf7a..0cc828c370 100644 --- a/test/PrefilterExpressionTestHelpers.h +++ b/test/PrefilterExpressionTestHelpers.h @@ -25,8 +25,8 @@ namespace { //______________________________________________________________________________ // Make RelationalExpression template -auto relExpr = - [](const ValueId& referenceId) -> std::unique_ptr { +auto relExpr = [](const IdOrLocalVocabEntry& referenceId) + -> std::unique_ptr { return std::make_unique(referenceId); }; @@ -68,6 +68,15 @@ constexpr auto orExpr = logExpr; constexpr auto notExpr = notPrefilterExpression; namespace filterHelper { + +//______________________________________________________________________________ +// Create `LocalVocabEntry` / `LiteralOrIri`. +// Note: `Iri` string value must start and end with `<`/`>` and the `Literal` +// value with `'`/`'`. +const auto LVE = [](const std::string& litOrIri) -> LocalVocabEntry { + return LocalVocabEntry::fromStringRepresentation(litOrIri); +}; + //______________________________________________________________________________ // Construct a `PAIR` with the given `PrefilterExpression` and `Variable` value. auto pr = @@ -97,6 +106,21 @@ auto makePrefilterVec = } // namespace makeFilterExpression +// _____________________________________________________________________________ +// Helper to retrieve a `ValueId` from `IdOrLocalVocabEntry`. +const auto getValueIdFromIdOrLocalVocabEntry = + [](const prefilterExpressions::IdOrLocalVocabEntry& value, + LocalVocab& localVocab) { + if (std::holds_alternative(value)) { + return std::get(value); + } else { + assert(std::holds_alternative(value)); + return Id::makeFromLocalVocabIndex( + localVocab.getIndexAndAddIfNotContained( + std::get(value))); + } + }; + namespace makeSparqlExpression { using namespace sparqlExpression; From 169bf08b8487a03b0b5b6bc664ab5d3a1ef26348 Mon Sep 17 00:00:00 2001 From: realHannes Date: Tue, 3 Dec 2024 12:49:29 +0100 Subject: [PATCH 78/81] add Literal/Iri value tests for GetPrefilterExpressionFromSparqlExpression --- ...lterExpressionFromSparqlExpressionTest.cpp | 67 +++++++++++-------- 1 file changed, 38 insertions(+), 29 deletions(-) diff --git a/test/GetPrefilterExpressionFromSparqlExpressionTest.cpp b/test/GetPrefilterExpressionFromSparqlExpressionTest.cpp index 235c069cff..2e3502f2ab 100644 --- a/test/GetPrefilterExpressionFromSparqlExpressionTest.cpp +++ b/test/GetPrefilterExpressionFromSparqlExpressionTest.cpp @@ -176,14 +176,21 @@ TEST(GetPrefilterExpressionFromSparqlExpression, evalAndEqualityCheck( andSprqlExpr(geSprql(varX, IntId(10)), neqSprql(varX, IntId(20))), pr(andExpr(ge(IntId(10)), neq(IntId(20))), varX)); - // ?z > VocabId(0) AND ?y > 0 AND ?x < 30.00 + // ?x >= "berlin" AND ?x != "hamburg" + // expected prefilter pairs: + // {<((>= "berlin") AND (!= "hamburg")), ?x>} + evalAndEqualityCheck( + andSprqlExpr(geSprql(varX, L("\"berlin\"")), + neqSprql(varX, L("\"hamburg\""))), + pr(andExpr(ge(LVE("\"berlin\"")), neq(LVE("\"hamburg\""))), varX)); + // ?z > AND ?y > 0 AND ?x < 30.00 // expected prefilter pairs - // {<(< 30.00), ?x>, <(> 0), ?y>, <(> VocabId(0)), ?z>} - evalAndEqualityCheck(andSprqlExpr(andSprqlExpr(gtSprql(varZ, VocabId(0)), + // {<(< 30.00), ?x>, <(> 0), ?y>, <(> ), ?z>} + evalAndEqualityCheck(andSprqlExpr(andSprqlExpr(gtSprql(varZ, I("")), gtSprql(varY, IntId(0))), ltSprql(varX, DoubleId(30.00))), pr(lt(DoubleId(30.00)), varX), pr(gt(IntId(0)), varY), - pr(gt(VocabId(0)), varZ)); + pr(gt(LVE("")), varZ)); // ?x == VocabId(10) AND ?y >= VocabId(10) // expected prefilter pairs: @@ -212,21 +219,23 @@ TEST(GetPrefilterExpressionFromSparqlExpression, neqSprql(DoubleId(22.1), varY)), pr(eq(VocabId(10)), varX), pr(neq(DoubleId(22.1)), varY), pr(eq(VocabId(0)), varZ)); - // (?z >= 1000 AND ?x == VocabId(10)) OR ?z >= 10000 + // (?z >= 1000 AND ?x == "hamburg") OR ?z >= 10000 // expected prefilter pairs: // {<((>=1000) OR (>= 10000)), ?z>} - evalAndEqualityCheck(orSprqlExpr(andSprqlExpr(geSprql(varZ, IntId(1000)), - eqSprql(varX, VocabId(10))), - geSprql(varZ, IntId(10000))), - pr(orExpr(ge(IntId(1000)), ge(IntId(10000))), varZ)); - // !((?z <= VocabId(10) OR ?y <= VocabId(10)) OR ?x <= VocabId(10)) + evalAndEqualityCheck( + orSprqlExpr(andSprqlExpr(geSprql(varZ, IntId(1000)), + eqSprql(varX, L("\"hamburg\""))), + geSprql(varZ, IntId(10000))), + pr(orExpr(ge(IntId(1000)), ge(IntId(10000))), varZ)); + // !((?z <= VocabId(10) OR ?y <= "world") OR ?x <= VocabId(10)) // expected prefilter pairs: // {, , } evalAndEqualityCheck( - notSprqlExpr(orSprqlExpr( - orSprqlExpr(leSprql(varZ, VocabId(10)), leSprql(varY, VocabId(10))), - leSprql(varX, VocabId(10)))), - pr(notExpr(le(VocabId(10))), varX), pr(notExpr(le(VocabId(10))), varY), + notSprqlExpr(orSprqlExpr(orSprqlExpr(leSprql(varZ, VocabId(10)), + leSprql(varY, L("\"world\""))), + leSprql(varX, VocabId(10)))), + pr(notExpr(le(VocabId(10))), varX), + pr(notExpr(le(LVE("\"world\""))), varY), pr(notExpr(le(VocabId(10))), varZ)); // ?x >= 10 AND ?y >= 10 // expected prefilter pairs: @@ -259,16 +268,16 @@ TEST(GetPrefilterExpressionFromSparqlExpression, leSprql(varY, IntId(0))))), pr(orExpr(notExpr(ge(IntId(10))), notExpr(le(IntId(0)))), varX), pr(orExpr(notExpr(ge(IntId(10))), notExpr(le(IntId(0)))), varY)); - // !(?x == VocabId(10) OR ?x == VocabId(20)) AND !(?z >= 10.00 OR ?y == false) + // !(?x == OR ?x == ) AND !(?z >= 10.00 OR ?y == false) // expected prefilter pairs: - // {, , + // {) OR (== )), ?x>, , // = 10), ?z>} evalAndEqualityCheck( - andSprqlExpr(notSprqlExpr(orSprqlExpr(eqSprql(varX, VocabId(10)), - eqSprql(varX, VocabId(20)))), + andSprqlExpr(notSprqlExpr(orSprqlExpr(eqSprql(varX, I("")), + eqSprql(varX, I("")))), notSprqlExpr(orSprqlExpr(geSprql(varZ, DoubleId(10)), eqSprql(varY, BoolId(false))))), - pr(notExpr(orExpr(eq(VocabId(10)), eq(VocabId(20)))), varX), + pr(notExpr(orExpr(eq(LVE("")), eq(LVE("")))), varX), pr(notExpr(eq(BoolId(false))), varY), pr(notExpr(ge(DoubleId(10))), varZ)); // !(!(?x >= 10 AND ?y >= 10)) OR !(!(?x <= 0 AND ?y <= 0)) @@ -295,15 +304,15 @@ TEST(GetPrefilterExpressionFromSparqlExpression, pr(notExpr(orExpr(andExpr(ge(VocabId(0)), le(VocabId(10))), notExpr(neq(VocabId(99))))), varX)); - // !((?y >= 10 AND ?y <= 100) OR !(?x >= VocabId(99))) + // !((?y >= VocabId(0) AND ?y <= "W") OR !(?x >= )) // expected prefilter pairs: - // {= VocabId(0)) AND (<= VocabId(10)), ?y>, = VocabId(99))), ?x>} + // {= VocabId(0)) AND (<= "W"), ?y>, = )), ?x>} evalAndEqualityCheck( notSprqlExpr(orSprqlExpr( - andSprqlExpr(geSprql(varY, VocabId(0)), leSprql(varY, VocabId(10))), - notSprqlExpr(geSprql(varX, VocabId(99))))), - pr(notExpr(notExpr(ge(VocabId(99)))), varX), - pr(notExpr(andExpr(ge(VocabId(0)), le(VocabId(10)))), varY)); + andSprqlExpr(geSprql(varY, VocabId(0)), leSprql(varY, L("\"W\""))), + notSprqlExpr(geSprql(varX, I(""))))), + pr(notExpr(notExpr(ge(LVE("")))), varX), + pr(notExpr(andExpr(ge(VocabId(0)), le(LVE("\"W\"")))), varY)); // ?z >= 10 AND ?z <= 100 AND ?x >= 10 AND ?x != 50 AND !(?y <= 10) AND // !(?city <= VocabId(1000) OR ?city == VocabId(1005)) // expected prefilter pairs: @@ -465,10 +474,10 @@ TEST(GetPrefilterExpressionFromSparqlExpression, getPrefilterExprForStrStartsExpr) { const auto varX = Variable{"?x"}; const auto varY = Variable{"?y"}; - evalAndEqualityCheck(strStartsSprql(varX, VocabId(0)), - pr(ge(VocabId(0)), varX)); - evalAndEqualityCheck(strStartsSprql(VocabId(0), varX), - pr(le(VocabId(0)), varX)); + evalAndEqualityCheck(strStartsSprql(varX, L("\"de\"")), + pr(ge(LVE("\"de\"")), varX)); + evalAndEqualityCheck(strStartsSprql(L("\"\""), varX), + pr(le(LVE("\"\"")), varX)); evalAndEqualityCheck(strStartsSprql(varX, varY)); evalAndEqualityCheck(strStartsSprql(VocabId(0), VocabId(10))); } From 4d7b4135e2b486a78ae73544009cff9f9ab83111 Mon Sep 17 00:00:00 2001 From: realHannes Date: Wed, 4 Dec 2024 20:09:54 +0100 Subject: [PATCH 79/81] changes for review (1) --- .../sparqlExpressions/LiteralExpression.h | 4 + .../PrefilterExpressionIndex.cpp | 80 +++++++++---------- .../PrefilterExpressionIndex.h | 39 +++++---- .../RelationalExpressions.cpp | 8 +- test/PrefilterExpressionTestHelpers.h | 16 ++-- 5 files changed, 78 insertions(+), 69 deletions(-) diff --git a/src/engine/sparqlExpressions/LiteralExpression.h b/src/engine/sparqlExpressions/LiteralExpression.h index 5f6e992dc1..50e0de5fd3 100644 --- a/src/engine/sparqlExpressions/LiteralExpression.h +++ b/src/engine/sparqlExpressions/LiteralExpression.h @@ -234,6 +234,10 @@ using IdOrLocalVocabEntry = prefilterExpressions::IdOrLocalVocabEntry; // function retrieves a corresponding `IdOrLocalVocabEntry` variant // (`std::variant`) for `LiteralExpression`s that // contain a suitable type. +// Given the boolean flag `stringAndIriOnly` is set to `true`, only `Literal`s, +// `Iri`s and `ValueId`s of type `VocabIndex`/`LocalVocabIndex` are returned. If +// `stringAndIriOnly` is set to `false` (default), all `ValueId` types retrieved +// from `LiteralExpression` will be returned. inline std::optional getIdOrLocalVocabEntryFromLiteralExpression(const SparqlExpression* child, bool stringAndIriOnly = false) { diff --git a/src/engine/sparqlExpressions/PrefilterExpressionIndex.cpp b/src/engine/sparqlExpressions/PrefilterExpressionIndex.cpp index 9a3e0a1739..4c5b2d0caa 100644 --- a/src/engine/sparqlExpressions/PrefilterExpressionIndex.cpp +++ b/src/engine/sparqlExpressions/PrefilterExpressionIndex.cpp @@ -7,6 +7,7 @@ #include #include "global/ValueIdComparators.h" +#include "util/OverloadCallOperator.h" namespace prefilterExpressions { @@ -168,12 +169,15 @@ static std::string getLogicalOpStr(const LogicalOperator logOp) { // SECTION PREFILTER EXPRESSION (BASE CLASS) //______________________________________________________________________________ std::vector PrefilterExpression::evaluate( - std::span input, size_t evaluationColumn) const { + std::span input, size_t evaluationColumn, + bool stripIncompleteBlocks) const { + if (!stripIncompleteBlocks) { + return evaluateAndCheckImpl(input, evaluationColumn); + } if (input.size() < 3) { return std::vector(input.begin(), input.end()); } - LocalVocab vocab{}; std::optional firstBlock = std::nullopt; std::optional lastBlock = std::nullopt; if (checkBlockIsInconsistent(input.front(), evaluationColumn)) { @@ -185,7 +189,7 @@ std::vector PrefilterExpression::evaluate( input = input.subspan(0, input.size() - 1); } - auto result = evaluateAndCheckImpl(input, evaluationColumn, vocab); + auto result = evaluateAndCheckImpl(input, evaluationColumn); if (firstBlock.has_value()) { result.insert(result.begin(), firstBlock.value()); } @@ -197,10 +201,9 @@ std::vector PrefilterExpression::evaluate( // _____________________________________________________________________________ std::vector PrefilterExpression::evaluateAndCheckImpl( - std::span input, size_t evaluationColumn, - LocalVocab& vocab) const { + std::span input, size_t evaluationColumn) const { checkEvalRequirements(input, evaluationColumn); - const auto& relevantBlocks = evaluateImpl(input, evaluationColumn, vocab); + const auto& relevantBlocks = evaluateImpl(input, evaluationColumn); checkEvalRequirements(relevantBlocks, evaluationColumn); return relevantBlocks; } @@ -238,8 +241,7 @@ RelationalExpression::logicalComplement() const { //______________________________________________________________________________ template std::vector RelationalExpression::evaluateImpl( - std::span input, size_t evaluationColumn, - LocalVocab& vocab) const { + std::span input, size_t evaluationColumn) const { using namespace valueIdComparators; std::vector valueIdsInput; // For each BlockMetadata value in vector input, we have a respective Id for @@ -263,19 +265,20 @@ std::vector RelationalExpression::evaluateImpl( // Helper to retrieve the reference `ValueId` from `IdOrLocalVocabEntry` // variant. auto getValueIdFromReferenceValue = - [](const IdOrLocalVocabEntry& referenceValue, - LocalVocab& localVocab) -> ValueId { - if (std::holds_alternative(referenceValue)) { - return std::get(referenceValue); - } else if (std::holds_alternative(referenceValue)) { - return Id::makeFromLocalVocabIndex( - localVocab.getIndexAndAddIfNotContained( - std::get(referenceValue))); - } else { - AD_FAIL(); - } - }; + [](const IdOrLocalVocabEntry& referenceValue, LocalVocab& vocab) { + return std::visit( + ad_utility::OverloadCallOperator{ + [](const ValueId& referenceId) -> ValueId { + return referenceId; + }, + [&vocab](const LocalVocabEntry& referenceLVE) -> ValueId { + return Id::makeFromLocalVocabIndex( + vocab.getIndexAndAddIfNotContained(referenceLVE)); + }}, + referenceValue); + }; + LocalVocab vocab{}; auto referenceId = getValueIdFromReferenceValue(referenceValue_, vocab); // Use getRangesForId (from valueIdComparators) to extract the ranges // containing the relevant ValueIds. @@ -344,14 +347,13 @@ std::string RelationalExpression::asString( [[maybe_unused]] size_t depth) const { auto referenceValueToString = [](std::stringstream& stream, const IdOrLocalVocabEntry& referenceValue) { - if (std::holds_alternative(referenceValue)) { - stream << std::get(referenceValue); - } else if (std::holds_alternative(referenceValue)) { - stream - << std::get(referenceValue).toStringRepresentation(); - } else { - AD_FAIL(); - } + std::visit( + ad_utility::OverloadCallOperator{ + [&stream](const ValueId& referenceId) { stream << referenceId; }, + [&stream](const LocalVocabEntry& referenceValue) { + stream << referenceValue.toStringRepresentation(); + }}, + referenceValue); }; std::stringstream stream; @@ -385,18 +387,15 @@ LogicalExpression::logicalComplement() const { //______________________________________________________________________________ template std::vector LogicalExpression::evaluateImpl( - std::span input, size_t evaluationColumn, - LocalVocab& vocab) const { + std::span input, size_t evaluationColumn) const { using enum LogicalOperator; if constexpr (Operation == AND) { - auto resultChild1 = - child1_->evaluateAndCheckImpl(input, evaluationColumn, vocab); - return child2_->evaluateAndCheckImpl(resultChild1, evaluationColumn, vocab); + auto resultChild1 = child1_->evaluate(input, evaluationColumn, false); + return child2_->evaluate(resultChild1, evaluationColumn, false); } else { static_assert(Operation == OR); - return getSetUnion( - child1_->evaluateAndCheckImpl(input, evaluationColumn, vocab), - child2_->evaluateAndCheckImpl(input, evaluationColumn, vocab)); + return getSetUnion(child1_->evaluate(input, evaluationColumn, false), + child2_->evaluate(input, evaluationColumn, false)); } }; @@ -447,9 +446,8 @@ std::unique_ptr NotExpression::logicalComplement() const { //______________________________________________________________________________ std::vector NotExpression::evaluateImpl( - std::span input, size_t evaluationColumn, - LocalVocab& vocab) const { - return child_->evaluateAndCheckImpl(input, evaluationColumn, vocab); + std::span input, size_t evaluationColumn) const { + return child_->evaluate(input, evaluationColumn, false); }; //______________________________________________________________________________ @@ -542,10 +540,10 @@ void checkPropertiesForPrefilterConstruction( template std::vector makePrefilterExpressionVec( const IdOrLocalVocabEntry& referenceValue, const Variable& variable, - const bool reversed) { + const bool mirrored) { std::vector resVec{}; resVec.emplace_back( - reversed + mirrored ? makeMirroredExpression(referenceValue) : std::make_unique>(referenceValue), variable); diff --git a/src/engine/sparqlExpressions/PrefilterExpressionIndex.h b/src/engine/sparqlExpressions/PrefilterExpressionIndex.h index 523ad1475c..0868bca358 100644 --- a/src/engine/sparqlExpressions/PrefilterExpressionIndex.h +++ b/src/engine/sparqlExpressions/PrefilterExpressionIndex.h @@ -93,7 +93,8 @@ class PrefilterExpression { // removes the respective block if it is conditionally (inconsistent columns) // necessary. std::vector evaluate(std::span input, - size_t evaluationColumn) const; + size_t evaluationColumn, + bool stripIncompleteBlocks = true) const; // Format for debugging friend std::ostream& operator<<(std::ostream& str, @@ -102,6 +103,7 @@ class PrefilterExpression { return str; } + private: // Note: Use `evaluate` for general evaluation of `PrefilterExpression` // instead of this method. // Performs the following conditional checks on @@ -112,13 +114,10 @@ class PrefilterExpression { // is violated, the function performing the checks will throw a // `std::runtime_error`. std::vector evaluateAndCheckImpl( - std::span input, size_t evaluationColumn, - LocalVocab& vocab) const; + std::span input, size_t evaluationColumn) const; - private: virtual std::vector evaluateImpl( - std::span input, size_t evaluationColumn, - LocalVocab& vocab) const = 0; + std::span input, size_t evaluationColumn) const = 0; }; //______________________________________________________________________________ @@ -146,9 +145,9 @@ class RelationalExpression : public PrefilterExpression { std::string asString(size_t depth) const override; private: - std::vector evaluateImpl(std::span input, - size_t evaluationColumn, - LocalVocab& vocab) const override; + std::vector evaluateImpl( + std::span input, + size_t evaluationColumn) const override; }; //______________________________________________________________________________ @@ -176,9 +175,9 @@ class LogicalExpression : public PrefilterExpression { std::string asString(size_t depth) const override; private: - std::vector evaluateImpl(std::span input, - size_t evaluationColumn, - LocalVocab& vocab) const override; + std::vector evaluateImpl( + std::span input, + size_t evaluationColumn) const override; }; //______________________________________________________________________________ @@ -201,9 +200,9 @@ class NotExpression : public PrefilterExpression { std::string asString(size_t depth) const override; private: - std::vector evaluateImpl(std::span input, - size_t evaluationColumn, - LocalVocab& vocab) const override; + std::vector evaluateImpl( + std::span input, + size_t evaluationColumn) const override; }; //______________________________________________________________________________ @@ -243,10 +242,18 @@ void checkPropertiesForPrefilterConstruction( const std::vector& vec); //______________________________________________________________________________ +// Creates a `RelationalExpression` prefilter expression based on +// the templated `CompOp` comparison operation and the reference +// `IdOrLocalVocabEntry` value. With the next step, the corresponding +// `, Variable>` pair is created, and finally +// returned in a vector. The `mirrored` flag indicates if the given +// `RelationalExpression` should be mirrored. The mirroring +// procedure changes the (asymmetrical) comparison operations: +// e.g. `5 < ?x` is changed to `?x > 5`. template std::vector makePrefilterExpressionVec( const IdOrLocalVocabEntry& referenceValue, const Variable& variable, - const bool reversed); + const bool mirrored); } // namespace detail } // namespace prefilterExpressions diff --git a/src/engine/sparqlExpressions/RelationalExpressions.cpp b/src/engine/sparqlExpressions/RelationalExpressions.cpp index 5f744a6562..5bc36b528e 100644 --- a/src/engine/sparqlExpressions/RelationalExpressions.cpp +++ b/src/engine/sparqlExpressions/RelationalExpressions.cpp @@ -432,11 +432,11 @@ RelationalExpression::getPrefilterExpressionForMetadata( const auto& optReferenceValue = detail::getIdOrLocalVocabEntryFromLiteralExpression(child1); - if (optReferenceValue.has_value()) { - return prefilterExpressions::detail::makePrefilterExpressionVec( - optReferenceValue.value(), variableExpr->value(), reversed); + if (!optReferenceValue.has_value()) { + return {}; } - return {}; + return prefilterExpressions::detail::makePrefilterExpressionVec( + optReferenceValue.value(), variableExpr->value(), reversed); }; // Option 1: // RelationalExpression containing a VariableExpression as the first child diff --git a/test/PrefilterExpressionTestHelpers.h b/test/PrefilterExpressionTestHelpers.h index 0cc828c370..3461f40f5d 100644 --- a/test/PrefilterExpressionTestHelpers.h +++ b/test/PrefilterExpressionTestHelpers.h @@ -111,14 +111,14 @@ auto makePrefilterVec = const auto getValueIdFromIdOrLocalVocabEntry = [](const prefilterExpressions::IdOrLocalVocabEntry& value, LocalVocab& localVocab) { - if (std::holds_alternative(value)) { - return std::get(value); - } else { - assert(std::holds_alternative(value)); - return Id::makeFromLocalVocabIndex( - localVocab.getIndexAndAddIfNotContained( - std::get(value))); - } + return std::visit( + ad_utility::OverloadCallOperator{ + [](const ValueId& referenceId) { return referenceId; }, + [&localVocab](const LocalVocabEntry& referenceValue) { + return Id::makeFromLocalVocabIndex( + localVocab.getIndexAndAddIfNotContained(referenceValue)); + }}, + value); }; namespace makeSparqlExpression { From 99fb34b4d554cf2d66c2beb5010c3c1dd235efc8 Mon Sep 17 00:00:00 2001 From: realHannes Date: Thu, 5 Dec 2024 13:28:39 +0100 Subject: [PATCH 80/81] changes for code review (2) --- .../PrefilterExpressionIndex.cpp | 116 +++++++----------- .../PrefilterExpressionIndex.h | 19 ++- ...lterExpressionFromSparqlExpressionTest.cpp | 2 + test/PrefilterExpressionIndexTest.cpp | 2 +- test/PrefilterExpressionTestHelpers.h | 15 --- 5 files changed, 65 insertions(+), 89 deletions(-) diff --git a/src/engine/sparqlExpressions/PrefilterExpressionIndex.cpp b/src/engine/sparqlExpressions/PrefilterExpressionIndex.cpp index 4c5b2d0caa..ac2a492e64 100644 --- a/src/engine/sparqlExpressions/PrefilterExpressionIndex.cpp +++ b/src/engine/sparqlExpressions/PrefilterExpressionIndex.cpp @@ -7,6 +7,7 @@ #include #include "global/ValueIdComparators.h" +#include "util/ConstexprMap.h" #include "util/OverloadCallOperator.h" namespace prefilterExpressions { @@ -208,6 +209,18 @@ std::vector PrefilterExpression::evaluateAndCheckImpl( return relevantBlocks; } +//______________________________________________________________________________ +ValueId PrefilterExpression::getValueIdFromIdOrLocalVocabEntry( + const IdOrLocalVocabEntry& referenceValue, LocalVocab& vocab) { + return std::visit(ad_utility::OverloadCallOperator{ + [](const ValueId& referenceId) { return referenceId; }, + [&vocab](const LocalVocabEntry& referenceLVE) { + return Id::makeFromLocalVocabIndex( + vocab.getIndexAndAddIfNotContained(referenceLVE)); + }}, + referenceValue); +} + // SECTION RELATIONAL OPERATIONS //______________________________________________________________________________ template @@ -217,22 +230,22 @@ RelationalExpression::logicalComplement() const { switch (Comparison) { case LT: // Complement X < Y: X >= Y - return std::make_unique(referenceValue_); + return std::make_unique(rightSideReferenceValue_); case LE: // Complement X <= Y: X > Y - return std::make_unique(referenceValue_); + return std::make_unique(rightSideReferenceValue_); case EQ: // Complement X == Y: X != Y - return std::make_unique(referenceValue_); + return std::make_unique(rightSideReferenceValue_); case NE: // Complement X != Y: X == Y - return std::make_unique(referenceValue_); + return std::make_unique(rightSideReferenceValue_); case GE: // Complement X >= Y: X < Y - return std::make_unique(referenceValue_); + return std::make_unique(rightSideReferenceValue_); case GT: // Complement X > Y: X <= Y - return std::make_unique(referenceValue_); + return std::make_unique(rightSideReferenceValue_); default: AD_FAIL(); } @@ -262,24 +275,9 @@ std::vector RelationalExpression::evaluateImpl( } } - // Helper to retrieve the reference `ValueId` from `IdOrLocalVocabEntry` - // variant. - auto getValueIdFromReferenceValue = - [](const IdOrLocalVocabEntry& referenceValue, LocalVocab& vocab) { - return std::visit( - ad_utility::OverloadCallOperator{ - [](const ValueId& referenceId) -> ValueId { - return referenceId; - }, - [&vocab](const LocalVocabEntry& referenceLVE) -> ValueId { - return Id::makeFromLocalVocabIndex( - vocab.getIndexAndAddIfNotContained(referenceLVE)); - }}, - referenceValue); - }; - LocalVocab vocab{}; - auto referenceId = getValueIdFromReferenceValue(referenceValue_, vocab); + auto referenceId = + getValueIdFromIdOrLocalVocabEntry(rightSideReferenceValue_, vocab); // Use getRangesForId (from valueIdComparators) to extract the ranges // containing the relevant ValueIds. // For pre-filtering with CompOp::EQ, we have to consider empty ranges. @@ -331,14 +329,15 @@ bool RelationalExpression::operator==( if (!otherRelational) { return false; } - return referenceValue_ == otherRelational->referenceValue_; + return rightSideReferenceValue_ == otherRelational->rightSideReferenceValue_; }; //______________________________________________________________________________ template std::unique_ptr RelationalExpression::clone() const { - return std::make_unique>(referenceValue_); + return std::make_unique>( + rightSideReferenceValue_); }; //______________________________________________________________________________ @@ -359,7 +358,7 @@ std::string RelationalExpression::asString( std::stringstream stream; stream << "Prefilter RelationalExpression<" << getRelationalOpStr(Comparison) << ">\nreferenceValue_ : "; - referenceValueToString(stream, referenceValue_); + referenceValueToString(stream, rightSideReferenceValue_); stream << " ." << std::endl; return stream.str(); }; @@ -489,33 +488,21 @@ template class LogicalExpression; namespace detail { //______________________________________________________________________________ +// Returns the corresponding mirrored `RelationalExpression` for the given `CompOp comparison` template argument. For +// example, the mirroring procedure will transform the relational expression +// `referenceValue > ?var` into `?var < referenceValue`. template static std::unique_ptr makeMirroredExpression( const IdOrLocalVocabEntry& referenceValue) { using enum CompOp; - switch (comparison) { - case LT: - // Id < ?var -> ?var > Id - return std::make_unique(referenceValue); - case LE: - // Id <= ?var -> ?var >= Id - return std::make_unique(referenceValue); - case GE: - // Id >= ?var -> ?var <= Id - return std::make_unique(referenceValue); - case GT: - // Id > ?var -> ?var < Id - return std::make_unique(referenceValue); - case EQ: - case NE: - // EQ(==) or NE(!=) - // Given that these two relations are symmetric w.r.t. ?var and Id, - // no swap regarding the relational operator is necessary. - return std::make_unique>(referenceValue); - default: - // Unchecked / new valueIdComparators::Comparison case. - AD_FAIL(); - } + using P = std::pair; + using namespace ad_utility; + constexpr static std::array mirrorPairs{P{LT, GT}, P{LE, GE}, P{GE, LE}, + P{GT, LT}, P{EQ, EQ}, P{NE, NE}}; + constexpr ConstexprMap mirrorMap(mirrorPairs); + return std::make_unique>( + referenceValue); } //______________________________________________________________________________ @@ -524,8 +511,7 @@ void checkPropertiesForPrefilterConstruction( auto viewVariable = vec | std::views::values; if (!std::ranges::is_sorted(viewVariable, std::less<>{})) { throw std::runtime_error( - "The vector must contain the pairs " - "in " + "The vector must contain the pairs in " "sorted order w.r.t. Variable value."); } if (auto it = std::ranges::adjacent_find(viewVariable); @@ -551,24 +537,16 @@ std::vector makePrefilterExpressionVec( } //______________________________________________________________________________ -template std::vector -makePrefilterExpressionVec(const IdOrLocalVocabEntry&, - const Variable&, const bool); -template std::vector -makePrefilterExpressionVec(const IdOrLocalVocabEntry&, - const Variable&, const bool); -template std::vector -makePrefilterExpressionVec(const IdOrLocalVocabEntry&, - const Variable&, const bool); -template std::vector -makePrefilterExpressionVec(const IdOrLocalVocabEntry&, - const Variable&, const bool); -template std::vector -makePrefilterExpressionVec(const IdOrLocalVocabEntry&, - const Variable&, const bool); -template std::vector -makePrefilterExpressionVec(const IdOrLocalVocabEntry&, - const Variable&, const bool); +#define INSTANTIATE_MAKE_PREFILTER(Comparison) \ + template std::vector \ + makePrefilterExpressionVec(const IdOrLocalVocabEntry&, \ + const Variable&, const bool); +INSTANTIATE_MAKE_PREFILTER(CompOp::LT); +INSTANTIATE_MAKE_PREFILTER(CompOp::LE); +INSTANTIATE_MAKE_PREFILTER(CompOp::GE); +INSTANTIATE_MAKE_PREFILTER(CompOp::GT); +INSTANTIATE_MAKE_PREFILTER(CompOp::EQ); +INSTANTIATE_MAKE_PREFILTER(CompOp::NE); } // namespace detail } // namespace prefilterExpressions diff --git a/src/engine/sparqlExpressions/PrefilterExpressionIndex.h b/src/engine/sparqlExpressions/PrefilterExpressionIndex.h index 0868bca358..73f11e0409 100644 --- a/src/engine/sparqlExpressions/PrefilterExpressionIndex.h +++ b/src/engine/sparqlExpressions/PrefilterExpressionIndex.h @@ -103,6 +103,11 @@ class PrefilterExpression { return str; } + // Static helper to retrieve the reference `ValueId` from the + // `IdOrLocalVocabEntry` variant. + static ValueId getValueIdFromIdOrLocalVocabEntry( + const IdOrLocalVocabEntry& refernceValue, LocalVocab& vocab); + private: // Note: Use `evaluate` for general evaluation of `PrefilterExpression` // instead of this method. @@ -132,12 +137,18 @@ using CompOp = valueIdComparators::Comparison; template class RelationalExpression : public PrefilterExpression { private: - // The value that represents our reference value for filtering. - IdOrLocalVocabEntry referenceValue_; + // This is the right hand side value of the relational expression. The left + // hand value is indirectly supplied during the evaluation process via the + // `evaluationColumn` argument. `evaluationColumn` represents the column index + // associated with the `Variable` column of the `IndexScan`. + // E.g., a less-than expression with a value of 3 will represent the logical + // relation ?var < 3. A equal-to expression with a value of "Freiburg" will + // represent ?var = "Freiburg". + IdOrLocalVocabEntry rightSideReferenceValue_; public: explicit RelationalExpression(const IdOrLocalVocabEntry& referenceValue) - : referenceValue_(referenceValue) {} + : rightSideReferenceValue_(referenceValue) {} std::unique_ptr logicalComplement() const override; bool operator==(const PrefilterExpression& other) const override; @@ -253,7 +264,7 @@ void checkPropertiesForPrefilterConstruction( template std::vector makePrefilterExpressionVec( const IdOrLocalVocabEntry& referenceValue, const Variable& variable, - const bool mirrored); + bool mirrored); } // namespace detail } // namespace prefilterExpressions diff --git a/test/GetPrefilterExpressionFromSparqlExpressionTest.cpp b/test/GetPrefilterExpressionFromSparqlExpressionTest.cpp index 2e3502f2ab..b5312ee8b0 100644 --- a/test/GetPrefilterExpressionFromSparqlExpressionTest.cpp +++ b/test/GetPrefilterExpressionFromSparqlExpressionTest.cpp @@ -478,6 +478,8 @@ TEST(GetPrefilterExpressionFromSparqlExpression, pr(ge(LVE("\"de\"")), varX)); evalAndEqualityCheck(strStartsSprql(L("\"\""), varX), pr(le(LVE("\"\"")), varX)); + evalAndEqualityCheck(strStartsSprql(varX, IntId(33))); + evalAndEqualityCheck(strStartsSprql(DoubleId(0.001), varY)); evalAndEqualityCheck(strStartsSprql(varX, varY)); evalAndEqualityCheck(strStartsSprql(VocabId(0), VocabId(10))); } diff --git a/test/PrefilterExpressionIndexTest.cpp b/test/PrefilterExpressionIndexTest.cpp index 642fc98913..2b31304d4f 100644 --- a/test/PrefilterExpressionIndexTest.cpp +++ b/test/PrefilterExpressionIndexTest.cpp @@ -23,7 +23,7 @@ using namespace prefilterExpressions; using namespace makeFilterExpression; using namespace filterHelper; -constexpr auto getId = getValueIdFromIdOrLocalVocabEntry; +constexpr auto getId = PrefilterExpression::getValueIdFromIdOrLocalVocabEntry; //______________________________________________________________________________ /* diff --git a/test/PrefilterExpressionTestHelpers.h b/test/PrefilterExpressionTestHelpers.h index 3461f40f5d..d6ca3204f4 100644 --- a/test/PrefilterExpressionTestHelpers.h +++ b/test/PrefilterExpressionTestHelpers.h @@ -106,21 +106,6 @@ auto makePrefilterVec = } // namespace makeFilterExpression -// _____________________________________________________________________________ -// Helper to retrieve a `ValueId` from `IdOrLocalVocabEntry`. -const auto getValueIdFromIdOrLocalVocabEntry = - [](const prefilterExpressions::IdOrLocalVocabEntry& value, - LocalVocab& localVocab) { - return std::visit( - ad_utility::OverloadCallOperator{ - [](const ValueId& referenceId) { return referenceId; }, - [&localVocab](const LocalVocabEntry& referenceValue) { - return Id::makeFromLocalVocabIndex( - localVocab.getIndexAndAddIfNotContained(referenceValue)); - }}, - value); - }; - namespace makeSparqlExpression { using namespace sparqlExpression; From b88f3b179bc0b575efd7336a483688a332b27656 Mon Sep 17 00:00:00 2001 From: realHannes Date: Thu, 5 Dec 2024 15:47:55 +0100 Subject: [PATCH 81/81] changes for code review (3) --- .../PrefilterExpressionIndex.cpp | 42 +++++++------------ 1 file changed, 16 insertions(+), 26 deletions(-) diff --git a/src/engine/sparqlExpressions/PrefilterExpressionIndex.cpp b/src/engine/sparqlExpressions/PrefilterExpressionIndex.cpp index ac2a492e64..93af26df7e 100644 --- a/src/engine/sparqlExpressions/PrefilterExpressionIndex.cpp +++ b/src/engine/sparqlExpressions/PrefilterExpressionIndex.cpp @@ -227,28 +227,19 @@ template std::unique_ptr RelationalExpression::logicalComplement() const { using enum CompOp; - switch (Comparison) { - case LT: - // Complement X < Y: X >= Y - return std::make_unique(rightSideReferenceValue_); - case LE: - // Complement X <= Y: X > Y - return std::make_unique(rightSideReferenceValue_); - case EQ: - // Complement X == Y: X != Y - return std::make_unique(rightSideReferenceValue_); - case NE: - // Complement X != Y: X == Y - return std::make_unique(rightSideReferenceValue_); - case GE: - // Complement X >= Y: X < Y - return std::make_unique(rightSideReferenceValue_); - case GT: - // Complement X > Y: X <= Y - return std::make_unique(rightSideReferenceValue_); - default: - AD_FAIL(); - } + using namespace ad_utility; + using P = std::pair; + // The complementation logic implemented with the following mapping procedure: + // (1) ?var < referenceValue -> ?var >= referenceValue + // (2) ?var <= referenceValue -> ?var > referenceValue + // (3) ?var >= referenceValue -> ?var < referenceValue + // (4) ?var > referenceValue -> ?var <= referenceValue + // (5) ?var = referenceValue -> ?var != referenceValue + // (6) ?var != referenceValue -> ?var = referenceValue + constexpr ConstexprMap complementMap( + {P{LT, GE}, P{LE, GT}, P{GE, LT}, P{GT, LE}, P{EQ, NE}, P{NE, EQ}}); + return std::make_unique>( + rightSideReferenceValue_); }; //______________________________________________________________________________ @@ -496,11 +487,10 @@ template static std::unique_ptr makeMirroredExpression( const IdOrLocalVocabEntry& referenceValue) { using enum CompOp; - using P = std::pair; using namespace ad_utility; - constexpr static std::array mirrorPairs{P{LT, GT}, P{LE, GE}, P{GE, LE}, - P{GT, LT}, P{EQ, EQ}, P{NE, NE}}; - constexpr ConstexprMap mirrorMap(mirrorPairs); + using P = std::pair; + constexpr ConstexprMap mirrorMap( + {P{LT, GT}, P{LE, GE}, P{GE, LE}, P{GT, LT}, P{EQ, EQ}, P{NE, NE}}); return std::make_unique>( referenceValue); }