diff --git a/include/llmr/map/vector_tile.hpp b/include/llmr/map/vector_tile.hpp index ad7a0eea1..4a77f8317 100644 --- a/include/llmr/map/vector_tile.hpp +++ b/include/llmr/map/vector_tile.hpp @@ -46,7 +46,7 @@ public: VectorTileTagExtractor(const VectorTileLayer &layer); void setTags(const pbf &pbf); - std::forward_list getValues(const std::string &key) const; + std::vector getValues(const std::string &key) const; void setType(FilterExpression::GeometryType type); FilterExpression::GeometryType getType() const; diff --git a/include/llmr/style/filter_expression.hpp b/include/llmr/style/filter_expression.hpp index 61e0fae3c..97ba8c971 100644 --- a/include/llmr/style/filter_expression.hpp +++ b/include/llmr/style/filter_expression.hpp @@ -4,10 +4,9 @@ #include #include -#include +#include #include -#include -#include +#include namespace llmr { @@ -26,20 +25,14 @@ public: class Instance { public: - Instance(Operator op, std::forward_list &&values) + Instance(Operator op, std::vector &&values) : op(op), values(values) {} - bool compare(const Value &property_value) const; - bool compare(const std::forward_list &property_values) const; - - private: - template inline bool includes(const Value &property_value, const Comparer &comparer) const; - template inline bool compare(const Value &property_value, const Comparer &comparer) const; - template inline bool all(const std::forward_list &property_values, const Comparer &comparer) const; + bool compare(const std::vector &property_values) const; private: Operator op = Operator::Equal; - std::forward_list values; + std::vector values; friend std::ostream& operator <<(std::ostream &, const Instance &); }; @@ -52,12 +45,12 @@ public: template inline void add(Args&& ...args) { - instances.emplace_front(::std::forward(args)...); + instances.emplace_back(::std::forward(args)...); } private: std::string field; - std::forward_list instances; + std::vector instances; friend std::ostream& operator <<(std::ostream &, const FilterComparison &); }; @@ -101,8 +94,8 @@ public: private: Operator op = Operator::And; GeometryType type = GeometryType::Any; - std::forward_list comparisons; - std::forward_list expressions; + std::vector comparisons; + std::vector expressions; friend std::ostream& operator <<(std::ostream &, const FilterExpression &); }; diff --git a/include/llmr/style/filter_expression_private.hpp b/include/llmr/style/filter_expression_private.hpp index 9a1f9fed3..c21fcac08 100644 --- a/include/llmr/style/filter_expression_private.hpp +++ b/include/llmr/style/filter_expression_private.hpp @@ -7,7 +7,7 @@ namespace llmr { template inline bool FilterComparison::compare(const Extractor &extractor) const { - const std::forward_list values = extractor.getValues(field); + const std::vector values = extractor.getValues(field); // All instances are ANDed together. for (const Instance &instance : instances) { diff --git a/include/llmr/style/style_parser.hpp b/include/llmr/style/style_parser.hpp index 38ef343e5..eb3b39eb0 100644 --- a/include/llmr/style/style_parser.hpp +++ b/include/llmr/style/style_parser.hpp @@ -91,7 +91,7 @@ private: FilterExpression parseFilter(JSVal, FilterExpression::Operator op); FilterExpression parseFilter(JSVal); Value parseValue(JSVal value); - std::forward_list parseValues(JSVal values); + std::vector parseValues(JSVal values); private: std::unordered_map constants; diff --git a/include/llmr/style/value_comparison.hpp b/include/llmr/style/value_comparison.hpp index d69142bc5..4386fae59 100644 --- a/include/llmr/style/value_comparison.hpp +++ b/include/llmr/style/value_comparison.hpp @@ -51,6 +51,11 @@ struct relaxed_equal_operator { inline bool operator()(T0 lhs, T1 rhs) const { return lhs == rhs; } }; +struct relaxed_not_equal_operator { + template + inline bool operator()(T0 lhs, T1 rhs) const { return lhs != rhs; } +}; + struct relaxed_greater_operator { template inline bool operator()(T0 lhs, T1 rhs) const { return lhs > rhs; } @@ -77,6 +82,10 @@ inline bool relaxed_equal(Value const &lhs, Value const &rhs) { return apply_visitor(detail::relaxed_operator_visitor(), lhs, rhs); } +inline bool relaxed_not_equal(Value const &lhs, Value const &rhs) { + return apply_visitor(detail::relaxed_operator_visitor(), lhs, rhs); +} + inline bool relaxed_greater(Value const &lhs, Value const &rhs) { return apply_visitor(detail::relaxed_operator_visitor(), lhs, rhs); } diff --git a/src/map/vector_tile.cpp b/src/map/vector_tile.cpp index a32db7fb3..86844c5d6 100644 --- a/src/map/vector_tile.cpp +++ b/src/map/vector_tile.cpp @@ -203,12 +203,11 @@ void VectorTileTagExtractor::setTags(const pbf &pbf) { tags_ = pbf; } -std::forward_list VectorTileTagExtractor::getValues(const std::string &key) const { - std::forward_list values; +std::vector VectorTileTagExtractor::getValues(const std::string &key) const { + std::vector values; auto field_it = layer_.key_index.find(key); if (field_it != layer_.key_index.end()) { - auto it = values.before_begin(); const uint32_t filter_key = field_it->second; // Now loop through all the key/value pair tags. @@ -227,7 +226,7 @@ std::forward_list VectorTileTagExtractor::getValues(const std::string &ke if (tag_key == filter_key) { if (layer_.values.size() > tag_val) { - it = values.emplace_after(it, layer_.values[tag_val]); + values.emplace_back(layer_.values[tag_val]); } else { fprintf(stderr, "[WARNING] feature references out of range value\n"); break; diff --git a/src/style/filter_expression.cpp b/src/style/filter_expression.cpp index 38bb74272..aaa898b41 100644 --- a/src/style/filter_expression.cpp +++ b/src/style/filter_expression.cpp @@ -3,13 +3,14 @@ #include +#include + namespace llmr { -template -inline bool FilterComparison::Instance::includes(const Value &property_value, const Comparer &comparer) const { - for (const Value &filter_value : values) { - if (comparer(property_value, filter_value)) { +inline bool includes(const Value &property_value, const std::vector &filter_values) { + for (const Value &filter_value : filter_values) { + if (util::relaxed_equal(property_value, filter_value)) { return true; } } @@ -17,8 +18,8 @@ inline bool FilterComparison::Instance::includes(const Value &property_value, co } template -inline bool FilterComparison::Instance::compare(const Value &property_value, const Comparer &comparer) const { - for (const Value &filter_value : values) { +inline bool compare(const Value &property_value, const std::vector &filter_values, const Comparer &comparer) { + for (const Value &filter_value : filter_values) { if (!comparer(property_value, filter_value)) { return false; } @@ -27,9 +28,9 @@ inline bool FilterComparison::Instance::compare(const Value &property_value, con } template -inline bool FilterComparison::Instance::all(const std::forward_list &property_values, const Comparer &comparer) const { +inline bool all(const std::vector &property_values, const std::vector &filter_values, const Comparer &comparer) { for (const Value &property_value : property_values) { - if (!compare(property_value, comparer)) { + if (!compare(property_value, filter_values, comparer)) { return false; } } @@ -37,66 +38,54 @@ inline bool FilterComparison::Instance::all(const std::forward_list &prop } - -bool FilterComparison::Instance::compare(const Value &property_value) const { - switch (op) { - case Operator::Equal: - case Operator::In: - return includes(property_value, util::relaxed_equal); - case Operator::NotEqual: - case Operator::NotIn: - return !includes(property_value, util::relaxed_equal); - case Operator::Greater: - return compare(property_value, util::relaxed_greater); - case Operator::GreaterEqual: - return compare(property_value, util::relaxed_greater_equal); - case Operator::Less: - return compare(property_value, util::relaxed_less); - case Operator::LessEqual: - return compare(property_value, util::relaxed_less_equal); - default: +inline bool set_equal(const std::vector &property_values, const std::vector &filter_values) { + for (const Value &property_value : property_values) { + if (!includes(property_value, filter_values)) { return false; + } } + if (property_values.size() == filter_values.size()) { + // Optimization: When the count is the same, the set is guaranteed to be identical. + return true; + } + // Otherwise, check again for identical reverse-mapped values. + for (const Value &filter_value : filter_values) { + if (!includes(filter_value, property_values)) { + return false; + } + } + return true; } -bool FilterComparison::Instance::compare(const std::forward_list &property_values) const { + +bool FilterComparison::Instance::compare(const std::vector &property_values) const { switch (op) { case Operator::Equal: - for (const Value &property_value : property_values) { - if (includes(property_value, util::relaxed_equal)) { - return true; - } - } - return false; + return set_equal(property_values, values); case Operator::NotEqual: - for (const Value &property_value : property_values) { - if (includes(property_value, util::relaxed_equal)) { - return false; - } - } - return true; + return !set_equal(property_values, values); case Operator::In: for (const Value &property_value : property_values) { - if (includes(property_value, util::relaxed_equal)) { + if (includes(property_value, values)) { return true; } } return false; case Operator::NotIn: for (const Value &property_value : property_values) { - if (!includes(property_value, util::relaxed_equal)) { + if (!includes(property_value, values)) { return true; } } return false; case Operator::Greater: - return all(property_values, util::relaxed_greater); + return all(property_values, values, util::relaxed_greater); case Operator::GreaterEqual: - return all(property_values, util::relaxed_greater_equal); + return all(property_values, values, util::relaxed_greater_equal); case Operator::Less: - return all(property_values, util::relaxed_less); + return all(property_values, values, util::relaxed_less); case Operator::LessEqual: - return all(property_values, util::relaxed_less_equal); + return all(property_values, values, util::relaxed_less_equal); default: return false; } @@ -176,11 +165,11 @@ bool FilterExpression::empty() const { } void FilterExpression::add(const FilterComparison &comparison) { - comparisons.emplace_front(comparison); + comparisons.emplace_back(comparison); } void FilterExpression::add(const FilterExpression &expression) { - expressions.emplace_front(expression); + expressions.emplace_back(expression); } void FilterExpression::setGeometryType(GeometryType g) { diff --git a/src/style/style_parser.cpp b/src/style/style_parser.cpp index 50a078983..a3363a388 100644 --- a/src/style/style_parser.cpp +++ b/src/style/style_parser.cpp @@ -792,7 +792,7 @@ FilterExpression StyleParser::parseFilter(JSVal value, FilterExpression::Operato } else if (filterValue.IsArray()) { comparison.add(FilterComparison::Operator::In, parseValues(filterValue)); } else { - comparison.add(FilterComparison::Operator::Equal, std::forward_list({ parseValue(filterValue) })); + comparison.add(FilterComparison::Operator::Equal, std::vector({ parseValue(filterValue) })); } expression.add(comparison); } @@ -830,15 +830,14 @@ Value StyleParser::parseValue(JSVal value) { } } -std::forward_list StyleParser::parseValues(JSVal value) { - std::forward_list values; +std::vector StyleParser::parseValues(JSVal value) { + std::vector values; if (value.IsArray()) { - auto it = values.before_begin(); for (rapidjson::SizeType i = 0; i < value.Size(); i++) { - it = values.emplace_after(it, parseValue(replaceConstant(value[i]))); + values.emplace_back(parseValue(replaceConstant(value[i]))); } } else { - values.emplace_front(parseValue(value)); + values.emplace_back(parseValue(value)); } return values; } diff --git a/test/expressions.cpp b/test/expressions.cpp index dc28f16ee..de398fe7d 100644 --- a/test/expressions.cpp +++ b/test/expressions.cpp @@ -10,17 +10,16 @@ class MockExtractor { public: - MockExtractor(const std::multimap &values) : values(values) {} - MockExtractor() {} + inline MockExtractor(const std::multimap &values) : values(values) {} + inline MockExtractor() {} - inline std::forward_list getValues(const std::string &key) const { - std::forward_list result; - auto rit = result.before_begin(); + inline std::vector getValues(const std::string &key) const { + std::vector result; // Find all values with the requested key. const auto ret = values.equal_range(key); for (auto it = ret.first; it != ret.second; it++) { // Append the newly found value to the forward list. - rit = result.emplace_after(rit, it->second); + result.emplace_back(it->second); } return result; } @@ -30,15 +29,16 @@ private: }; -TEST(FilterComparison, SingleStringValue) { +TEST(FilterComparison, EqualsSingleStringValue) { using namespace llmr; FilterComparison comparison("test"); - comparison.add(FilterComparison::Operator::Equal, std::forward_list { std::string("bar") }); + comparison.add(FilterComparison::Operator::Equal, std::vector { std::string("bar") }); // comparison is { "test": { "==": "bar" } } ASSERT_TRUE(comparison.compare(MockExtractor({{ "test", std::string("bar") }}))); - ASSERT_TRUE(comparison.compare(MockExtractor({{ "test", std::string("bar") }, { "test", std::string("booz") }}))); + ASSERT_TRUE(comparison.compare(MockExtractor({{ "test", std::string("bar") }, { "test", std::string("bar") }}))); + ASSERT_FALSE(comparison.compare(MockExtractor({{ "test", std::string("bar") }, { "test", std::string("booz") }}))); ASSERT_TRUE(comparison.compare(MockExtractor({{ "other", std::string("bar") }, { "test", std::string("bar") }}))); ASSERT_FALSE(comparison.compare(MockExtractor({{ "other", std::string("bar") }}))); ASSERT_FALSE(comparison.compare(MockExtractor())); @@ -51,80 +51,256 @@ TEST(FilterComparison, SingleStringValue) { } -TEST(FilterComparison, SingleDoubleValue) { +TEST(FilterComparison, EqualsSingleDoubleValue) { using namespace llmr; FilterComparison comparison("test"); - comparison.add(FilterComparison::Operator::Equal, std::forward_list { double(32.8) }); + comparison.add(FilterComparison::Operator::Equal, std::vector { double(32.8) }); // comparison is { "test": { "==": 32.8 } } ASSERT_TRUE(comparison.compare(MockExtractor({{ "test", double(32.8) }}))); ASSERT_TRUE(comparison.compare(MockExtractor({{ "test", std::string("32.8") }}))); - ASSERT_TRUE(comparison.compare(MockExtractor({{ "test", double(32.8) }, { "test", std::string("booz") }}))); ASSERT_TRUE(comparison.compare(MockExtractor({{ "other", std::string("bar") }, { "test", double(32.8) }}))); + ASSERT_FALSE(comparison.compare(MockExtractor({{ "test", double(32.8) }, { "test", std::string("booz") }}))); ASSERT_FALSE(comparison.compare(MockExtractor({{ "other", double(32.8) }}))); ASSERT_FALSE(comparison.compare(MockExtractor())); ASSERT_FALSE(comparison.compare(MockExtractor({{ "test", double(32.9) }}))); ASSERT_FALSE(comparison.compare(MockExtractor({{ "test", int64_t(-18932) }}))); ASSERT_FALSE(comparison.compare(MockExtractor({{ "test", uint64_t(18932) }}))); + ASSERT_FALSE(comparison.compare(MockExtractor({{ "test", bool(false) }}))); + ASSERT_FALSE(comparison.compare(MockExtractor({{ "test", bool(true) }}))); } -TEST(FilterComparison, SingleUintValue) { +TEST(FilterComparison, EqualsSingleUintValue) { using namespace llmr; FilterComparison comparison("test"); - comparison.add(FilterComparison::Operator::Equal, std::forward_list { uint64_t(42) }); + comparison.add(FilterComparison::Operator::Equal, std::vector { uint64_t(42) }); // comparison is { "test": { "==": 42 } } ASSERT_TRUE(comparison.compare(MockExtractor({{ "test", uint64_t(42) }}))); ASSERT_TRUE(comparison.compare(MockExtractor({{ "test", double(42) }}))); ASSERT_TRUE(comparison.compare(MockExtractor({{ "test", int64_t(42) }}))); ASSERT_TRUE(comparison.compare(MockExtractor({{ "test", std::string("42") }}))); - ASSERT_TRUE(comparison.compare(MockExtractor({{ "test", uint64_t(42) }, { "test", std::string("booz") }}))); ASSERT_TRUE(comparison.compare(MockExtractor({{ "other", std::string("bar") }, { "test", uint64_t(42) }}))); + ASSERT_FALSE(comparison.compare(MockExtractor({{ "test", uint64_t(42) }, { "test", std::string("booz") }}))); ASSERT_FALSE(comparison.compare(MockExtractor({{ "other", uint64_t(42) }}))); ASSERT_FALSE(comparison.compare(MockExtractor())); ASSERT_FALSE(comparison.compare(MockExtractor({{ "test", double(43) }}))); ASSERT_FALSE(comparison.compare(MockExtractor({{ "test", int64_t(-18932) }}))); ASSERT_FALSE(comparison.compare(MockExtractor({{ "test", uint64_t(18932) }}))); + ASSERT_FALSE(comparison.compare(MockExtractor({{ "test", bool(false) }}))); + ASSERT_FALSE(comparison.compare(MockExtractor({{ "test", bool(true) }}))); } -TEST(FilterComparison, SingleIntValue) { +TEST(FilterComparison, EqualsSingleIntValue) { using namespace llmr; FilterComparison comparison("test"); - comparison.add(FilterComparison::Operator::Equal, std::forward_list { int64_t(-42) }); + comparison.add(FilterComparison::Operator::Equal, std::vector { int64_t(-42) }); // comparison is { "test": { "==": -42 } } ASSERT_TRUE(comparison.compare(MockExtractor({{ "test", int64_t(-42) }}))); ASSERT_TRUE(comparison.compare(MockExtractor({{ "test", double(-42) }}))); ASSERT_TRUE(comparison.compare(MockExtractor({{ "test", std::string("-42") }}))); - ASSERT_TRUE(comparison.compare(MockExtractor({{ "test", int64_t(-42) }, { "test", std::string("booz") }}))); ASSERT_TRUE(comparison.compare(MockExtractor({{ "other", std::string("bar") }, { "test", int64_t(-42) }}))); + ASSERT_FALSE(comparison.compare(MockExtractor({{ "test", int64_t(-42) }, { "test", std::string("booz") }}))); ASSERT_FALSE(comparison.compare(MockExtractor({{ "other", int64_t(-42) }}))); ASSERT_FALSE(comparison.compare(MockExtractor())); ASSERT_FALSE(comparison.compare(MockExtractor({{ "test", int64_t(-43) }}))); ASSERT_FALSE(comparison.compare(MockExtractor({{ "test", double(-43) }}))); ASSERT_FALSE(comparison.compare(MockExtractor({{ "test", uint64_t(-42) }}))); + ASSERT_FALSE(comparison.compare(MockExtractor({{ "test", bool(false) }}))); + ASSERT_FALSE(comparison.compare(MockExtractor({{ "test", bool(true) }}))); } -TEST(FilterComparison, SingleBoolValue) { +TEST(FilterComparison, EqualsSingleBoolValue) { using namespace llmr; FilterComparison comparison("test"); - comparison.add(FilterComparison::Operator::Equal, std::forward_list { bool(true) }); - // comparison is { "test": { "==": 32.8 } } + comparison.add(FilterComparison::Operator::Equal, std::vector { bool(true) }); + // comparison is { "test": { "==": true } } ASSERT_TRUE(comparison.compare(MockExtractor({{ "test", bool(true) }}))); ASSERT_TRUE(comparison.compare(MockExtractor({{ "test", int64_t(1) }}))); ASSERT_TRUE(comparison.compare(MockExtractor({{ "test", uint64_t(1) }}))); ASSERT_TRUE(comparison.compare(MockExtractor({{ "test", double(1) }}))); ASSERT_TRUE(comparison.compare(MockExtractor({{ "test", std::string("1") }}))); - ASSERT_TRUE(comparison.compare(MockExtractor({{ "test", bool(true) }, { "test", std::string("booz") }}))); ASSERT_TRUE(comparison.compare(MockExtractor({{ "other", std::string("bar") }, { "test", bool(true) }}))); + ASSERT_FALSE(comparison.compare(MockExtractor({{ "test", bool(true) }, { "test", std::string("booz") }}))); ASSERT_FALSE(comparison.compare(MockExtractor({{ "other", bool(true) }}))); ASSERT_FALSE(comparison.compare(MockExtractor())); ASSERT_FALSE(comparison.compare(MockExtractor({{ "test", bool(false) }}))); ASSERT_FALSE(comparison.compare(MockExtractor({{ "test", int64_t(-18932) }}))); ASSERT_FALSE(comparison.compare(MockExtractor({{ "test", uint64_t(18932) }}))); } + + + +TEST(FilterComparison, EqualsMultipleStringValues) { + using namespace llmr; + + FilterComparison comparison("test"); + comparison.add(FilterComparison::Operator::Equal, std::vector { std::string("bar"), std::string("baz") }); + // comparison is { "test": { "==": ["bar", "baz"]̇ } } + + ASSERT_TRUE(comparison.compare(MockExtractor({{ "test", std::string("bar") }, { "test", std::string("baz") }}))); + ASSERT_TRUE(comparison.compare(MockExtractor({{ "test", std::string("baz") }, { "test", std::string("bar") }}))); + ASSERT_FALSE(comparison.compare(MockExtractor({{ "test", std::string("bar") }}))); + ASSERT_FALSE(comparison.compare(MockExtractor({{ "test", std::string("baz") }}))); + ASSERT_FALSE(comparison.compare(MockExtractor({{ "test", std::string("bar") }, { "test", std::string("baz") }, { "test", std::string("boo") }}))); +} + +TEST(FilterComparison, EqualsMultipleIdenticalStringValues) { + using namespace llmr; + + FilterComparison comparison("test"); + comparison.add(FilterComparison::Operator::Equal, std::vector { std::string("bar"), std::string("bar") }); + // comparison is { "test": { "==": ["bar", "bar"]̇ } } + + ASSERT_TRUE(comparison.compare(MockExtractor({{ "test", std::string("bar") }}))); + ASSERT_TRUE(comparison.compare(MockExtractor({{ "test", std::string("bar") }, { "test", std::string("bar") }}))); + ASSERT_FALSE(comparison.compare(MockExtractor({{ "test", std::string("baz") }}))); + ASSERT_FALSE(comparison.compare(MockExtractor({{ "test", std::string("bar") }, { "test", std::string("baz") }}))); +} + + + +TEST(FilterComparison, NotEqualsSingleStringValue) { + using namespace llmr; + + FilterComparison comparison("test"); + comparison.add(FilterComparison::Operator::NotEqual, std::vector { std::string("bar") }); + // comparison is { "test": { "!=": "bar" } } + + ASSERT_FALSE(comparison.compare(MockExtractor({{ "test", std::string("bar") }}))); + ASSERT_FALSE(comparison.compare(MockExtractor({{ "test", std::string("bar") }, { "test", std::string("bar") }}))); + ASSERT_FALSE(comparison.compare(MockExtractor({{ "other", std::string("bar") }, { "test", std::string("bar") }}))); + ASSERT_TRUE(comparison.compare(MockExtractor({{ "test", std::string("bar") }, { "test", std::string("booz") }}))); + ASSERT_TRUE(comparison.compare(MockExtractor({{ "other", std::string("bar") }}))); + ASSERT_TRUE(comparison.compare(MockExtractor())); + ASSERT_TRUE(comparison.compare(MockExtractor({{ "test", std::string("barst") }}))); + ASSERT_TRUE(comparison.compare(MockExtractor({{ "test", int64_t(-18932) }}))); + ASSERT_TRUE(comparison.compare(MockExtractor({{ "test", uint64_t(18932) }}))); + ASSERT_TRUE(comparison.compare(MockExtractor({{ "test", double(32.8) }}))); + ASSERT_TRUE(comparison.compare(MockExtractor({{ "test", bool(false) }}))); + ASSERT_TRUE(comparison.compare(MockExtractor({{ "test", bool(true) }}))); +} + + +TEST(FilterComparison, NotEqualsSingleDoubleValue) { + using namespace llmr; + + FilterComparison comparison("test"); + comparison.add(FilterComparison::Operator::NotEqual, std::vector { double(32.8) }); + // comparison is { "test": { "!=": 32.8 } } + + ASSERT_FALSE(comparison.compare(MockExtractor({{ "test", double(32.8) }}))); + ASSERT_FALSE(comparison.compare(MockExtractor({{ "test", std::string("32.8") }}))); + ASSERT_FALSE(comparison.compare(MockExtractor({{ "other", std::string("bar") }, { "test", double(32.8) }}))); + ASSERT_TRUE(comparison.compare(MockExtractor({{ "test", double(32.8) }, { "test", std::string("booz") }}))); + ASSERT_TRUE(comparison.compare(MockExtractor({{ "other", double(32.8) }}))); + ASSERT_TRUE(comparison.compare(MockExtractor())); + ASSERT_TRUE(comparison.compare(MockExtractor({{ "test", double(32.9) }}))); + ASSERT_TRUE(comparison.compare(MockExtractor({{ "test", int64_t(-18932) }}))); + ASSERT_TRUE(comparison.compare(MockExtractor({{ "test", uint64_t(18932) }}))); + ASSERT_TRUE(comparison.compare(MockExtractor({{ "test", bool(false) }}))); + ASSERT_TRUE(comparison.compare(MockExtractor({{ "test", bool(true) }}))); +} + +TEST(FilterComparison, NotEqualsSingleUintValue) { + using namespace llmr; + + FilterComparison comparison("test"); + comparison.add(FilterComparison::Operator::NotEqual, std::vector { uint64_t(42) }); + // comparison is { "test": { "!=": 42 } } + + ASSERT_FALSE(comparison.compare(MockExtractor({{ "test", uint64_t(42) }}))); + ASSERT_FALSE(comparison.compare(MockExtractor({{ "test", double(42) }}))); + ASSERT_FALSE(comparison.compare(MockExtractor({{ "test", int64_t(42) }}))); + ASSERT_FALSE(comparison.compare(MockExtractor({{ "test", std::string("42") }}))); + ASSERT_FALSE(comparison.compare(MockExtractor({{ "other", std::string("bar") }, { "test", uint64_t(42) }}))); + ASSERT_TRUE(comparison.compare(MockExtractor({{ "test", uint64_t(42) }, { "test", std::string("booz") }}))); + ASSERT_TRUE(comparison.compare(MockExtractor({{ "other", uint64_t(42) }}))); + ASSERT_TRUE(comparison.compare(MockExtractor())); + ASSERT_TRUE(comparison.compare(MockExtractor({{ "test", double(43) }}))); + ASSERT_TRUE(comparison.compare(MockExtractor({{ "test", int64_t(-18932) }}))); + ASSERT_TRUE(comparison.compare(MockExtractor({{ "test", uint64_t(18932) }}))); + ASSERT_TRUE(comparison.compare(MockExtractor({{ "test", bool(false) }}))); + ASSERT_TRUE(comparison.compare(MockExtractor({{ "test", bool(true) }}))); +} + + +TEST(FilterComparison, NotEqualsSingleIntValue) { + using namespace llmr; + + FilterComparison comparison("test"); + comparison.add(FilterComparison::Operator::NotEqual, std::vector { int64_t(-42) }); + // comparison is { "test": { "!=": -42 } } + + ASSERT_FALSE(comparison.compare(MockExtractor({{ "test", int64_t(-42) }}))); + ASSERT_FALSE(comparison.compare(MockExtractor({{ "test", double(-42) }}))); + ASSERT_FALSE(comparison.compare(MockExtractor({{ "test", std::string("-42") }}))); + ASSERT_FALSE(comparison.compare(MockExtractor({{ "other", std::string("bar") }, { "test", int64_t(-42) }}))); + ASSERT_TRUE(comparison.compare(MockExtractor({{ "test", int64_t(-42) }, { "test", std::string("booz") }}))); + ASSERT_TRUE(comparison.compare(MockExtractor({{ "other", int64_t(-42) }}))); + ASSERT_TRUE(comparison.compare(MockExtractor())); + ASSERT_TRUE(comparison.compare(MockExtractor({{ "test", int64_t(-43) }}))); + ASSERT_TRUE(comparison.compare(MockExtractor({{ "test", double(-43) }}))); + ASSERT_TRUE(comparison.compare(MockExtractor({{ "test", uint64_t(-42) }}))); + ASSERT_TRUE(comparison.compare(MockExtractor({{ "test", bool(false) }}))); + ASSERT_TRUE(comparison.compare(MockExtractor({{ "test", bool(true) }}))); +} + + +TEST(FilterComparison, NotEqualsSingleBoolValue) { + using namespace llmr; + + FilterComparison comparison("test"); + comparison.add(FilterComparison::Operator::NotEqual, std::vector { bool(true) }); + // comparison is { "test": { "!=": true } } + + ASSERT_FALSE(comparison.compare(MockExtractor({{ "test", bool(true) }}))); + ASSERT_FALSE(comparison.compare(MockExtractor({{ "test", int64_t(1) }}))); + ASSERT_FALSE(comparison.compare(MockExtractor({{ "test", uint64_t(1) }}))); + ASSERT_FALSE(comparison.compare(MockExtractor({{ "test", double(1) }}))); + ASSERT_FALSE(comparison.compare(MockExtractor({{ "test", std::string("1") }}))); + ASSERT_FALSE(comparison.compare(MockExtractor({{ "other", std::string("bar") }, { "test", bool(true) }}))); + ASSERT_TRUE(comparison.compare(MockExtractor({{ "test", bool(true) }, { "test", std::string("booz") }}))); + ASSERT_TRUE(comparison.compare(MockExtractor({{ "other", bool(true) }}))); + ASSERT_TRUE(comparison.compare(MockExtractor())); + ASSERT_TRUE(comparison.compare(MockExtractor({{ "test", bool(false) }}))); + ASSERT_TRUE(comparison.compare(MockExtractor({{ "test", int64_t(-18932) }}))); + ASSERT_TRUE(comparison.compare(MockExtractor({{ "test", uint64_t(18932) }}))); +} + + + +TEST(FilterComparison, NotEqualsMultipleStringValues) { + using namespace llmr; + + FilterComparison comparison("test"); + comparison.add(FilterComparison::Operator::NotEqual, std::vector { std::string("bar"), std::string("baz") }); + // comparison is { "test": { "!=": ["bar", "baz"]̇ } } + + ASSERT_FALSE(comparison.compare(MockExtractor({{ "test", std::string("bar") }, { "test", std::string("baz") }}))); + ASSERT_FALSE(comparison.compare(MockExtractor({{ "test", std::string("baz") }, { "test", std::string("bar") }}))); + ASSERT_TRUE(comparison.compare(MockExtractor({{ "test", std::string("bar") }}))); + ASSERT_TRUE(comparison.compare(MockExtractor({{ "test", std::string("baz") }}))); + ASSERT_TRUE(comparison.compare(MockExtractor({{ "test", std::string("bar") }, { "test", std::string("baz") }, { "test", std::string("boo") }}))); +} + + + +TEST(FilterComparison, NotEqualsMultipleIdenticalStringValues) { + using namespace llmr; + + FilterComparison comparison("test"); + comparison.add(FilterComparison::Operator::NotEqual, std::vector { std::string("bar"), std::string("bar") }); + // comparison is { "test": { "!=": ["bar", "bar"]̇ } } + + ASSERT_FALSE(comparison.compare(MockExtractor({{ "test", std::string("bar") }}))); + ASSERT_FALSE(comparison.compare(MockExtractor({{ "test", std::string("bar") }, { "test", std::string("bar") }}))); + ASSERT_TRUE(comparison.compare(MockExtractor({{ "test", std::string("baz") }}))); + ASSERT_TRUE(comparison.compare(MockExtractor({{ "test", std::string("bar") }, { "test", std::string("baz") }}))); +}