From eadce65e044ff72d963205117654668d26196438 Mon Sep 17 00:00:00 2001 From: Marcin Gasperowicz Date: Thu, 28 May 2020 17:19:59 +0200 Subject: [PATCH] LibJS: Implement standard semantics for relational operators (#2417) Previously, the relational operators where casting any value to double and comparing the results according to C++ semantics. This patch makes the relational operators in JS behave according to the standard specification. Since we don't have BigInt yet, the implementation doesn't take it into account. Moved PreferredType from Object to Value. Value::to_primitive now passes preferred_type to Object::to_primitive. --- Libraries/LibJS/Runtime/Object.cpp | 8 +- Libraries/LibJS/Runtime/Object.h | 8 +- Libraries/LibJS/Runtime/Value.cpp | 126 +++-- Libraries/LibJS/Runtime/Value.h | 12 +- Libraries/LibJS/Tests/binary-relational.js | 531 +++++++++++++++++++++ 5 files changed, 641 insertions(+), 44 deletions(-) create mode 100644 Libraries/LibJS/Tests/binary-relational.js diff --git a/Libraries/LibJS/Runtime/Object.cpp b/Libraries/LibJS/Runtime/Object.cpp index bab44c3b88..6f44638231 100644 --- a/Libraries/LibJS/Runtime/Object.cpp +++ b/Libraries/LibJS/Runtime/Object.cpp @@ -618,20 +618,20 @@ bool Object::has_own_property(PropertyName property_name) const return shape().lookup(property_name.as_string()).has_value(); } -Value Object::to_primitive(PreferredType preferred_type) const +Value Object::to_primitive(Value::PreferredType preferred_type) const { Value result = js_undefined(); switch (preferred_type) { - case PreferredType::Default: - case PreferredType::Number: { + case Value::PreferredType::Default: + case Value::PreferredType::Number: { result = value_of(); if (result.is_object()) { result = to_string(); } break; } - case PreferredType::String: { + case Value::PreferredType::String: { result = to_string(); if (result.is_object()) result = value_of(); diff --git a/Libraries/LibJS/Runtime/Object.h b/Libraries/LibJS/Runtime/Object.h index ad334a2d8a..44be63e506 100644 --- a/Libraries/LibJS/Runtime/Object.h +++ b/Libraries/LibJS/Runtime/Object.h @@ -97,14 +97,8 @@ public: void set_prototype(Object*); bool has_prototype(const Object* prototype) const; - enum class PreferredType { - Default, - String, - Number, - }; - virtual Value value_of() const { return Value(const_cast(this)); } - virtual Value to_primitive(PreferredType preferred_type = PreferredType::Default) const; + virtual Value to_primitive(Value::PreferredType preferred_type = Value::PreferredType::Default) const; virtual Value to_string() const; Value get_direct(size_t index) const { return m_storage[index]; } diff --git a/Libraries/LibJS/Runtime/Value.cpp b/Libraries/LibJS/Runtime/Value.cpp index 9f248829a3..b2eab819e2 100644 --- a/Libraries/LibJS/Runtime/Value.cpp +++ b/Libraries/LibJS/Runtime/Value.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -142,7 +143,7 @@ String Value::to_string(Interpreter& interpreter) const } if (is_object()) { - auto primitive_value = as_object().to_primitive(Object::PreferredType::String); + auto primitive_value = as_object().to_primitive(PreferredType::String); if (interpreter.exception()) return {}; return primitive_value.to_string(interpreter); @@ -175,10 +176,10 @@ bool Value::to_boolean() const } } -Value Value::to_primitive(Interpreter&) const +Value Value::to_primitive(Interpreter&, PreferredType preferred_type) const { if (is_object()) - return as_object().to_primitive(); + return as_object().to_primitive(preferred_type); return *this; } @@ -241,7 +242,7 @@ Value Value::to_number(Interpreter& interpreter) const interpreter.throw_exception("Can't convert symbol to number"); return {}; case Type::Object: - auto primitive = m_value.as_object->to_primitive(Object::PreferredType::Number); + auto primitive = m_value.as_object->to_primitive(PreferredType::Number); if (interpreter.exception()) return {}; return primitive.to_number(interpreter); @@ -297,46 +298,34 @@ size_t Value::to_size_t(Interpreter& interpreter) const Value greater_than(Interpreter& interpreter, Value lhs, Value rhs) { - auto lhs_number = lhs.to_number(interpreter); - if (interpreter.exception()) - return {}; - auto rhs_number = rhs.to_number(interpreter); - if (interpreter.exception()) - return {}; - return Value(lhs_number.as_double() > rhs_number.as_double()); + TriState relation = abstract_relation(interpreter, false, lhs, rhs); + if (relation == TriState::Unknown) + return Value(false); + return Value(relation == TriState::True); } Value greater_than_equals(Interpreter& interpreter, Value lhs, Value rhs) { - auto lhs_number = lhs.to_number(interpreter); - if (interpreter.exception()) - return {}; - auto rhs_number = rhs.to_number(interpreter); - if (interpreter.exception()) - return {}; - return Value(lhs_number.as_double() >= rhs_number.as_double()); + TriState relation = abstract_relation(interpreter, true, lhs, rhs); + if (relation == TriState::Unknown || relation == TriState::True) + return Value(false); + return Value(true); } Value less_than(Interpreter& interpreter, Value lhs, Value rhs) { - auto lhs_number = lhs.to_number(interpreter); - if (interpreter.exception()) - return {}; - auto rhs_number = rhs.to_number(interpreter); - if (interpreter.exception()) - return {}; - return Value(lhs_number.as_double() < rhs_number.as_double()); + TriState relation = abstract_relation(interpreter, true, lhs, rhs); + if (relation == TriState::Unknown) + return Value(false); + return Value(relation == TriState::True); } Value less_than_equals(Interpreter& interpreter, Value lhs, Value rhs) { - auto lhs_number = lhs.to_number(interpreter); - if (interpreter.exception()) - return {}; - auto rhs_number = rhs.to_number(interpreter); - if (interpreter.exception()) - return {}; - return Value(lhs_number.as_double() <= rhs_number.as_double()); + TriState relation = abstract_relation(interpreter, false, lhs, rhs); + if (relation == TriState::Unknown || relation == TriState::True) + return Value(false); + return Value(true); } Value bitwise_and(Interpreter& interpreter, Value lhs, Value rhs) @@ -673,4 +662,77 @@ bool abstract_eq(Interpreter& interpreter, Value lhs, Value rhs) return false; } +TriState abstract_relation(Interpreter& interpreter, bool left_first, Value lhs, Value rhs) +{ + Value x_primitive = {}; + Value y_primitive = {}; + + if (left_first) { + x_primitive = lhs.to_primitive(interpreter, Value::PreferredType::Number); + if (interpreter.exception()) + return {}; + y_primitive = rhs.to_primitive(interpreter, Value::PreferredType::Number); + if (interpreter.exception()) + return {}; + } else { + y_primitive = lhs.to_primitive(interpreter, Value::PreferredType::Number); + if (interpreter.exception()) + return {}; + x_primitive = rhs.to_primitive(interpreter, Value::PreferredType::Number); + if (interpreter.exception()) + return {}; + } + + if (x_primitive.is_string() && y_primitive.is_string()) { + auto x_string = x_primitive.as_string().string(); + auto y_string = y_primitive.as_string().string(); + + if (x_string.starts_with(y_string)) + return TriState::False; + if (y_string.starts_with(x_string)) + return TriState::True; + + Utf8View x_codepoints { x_string }; + Utf8View y_codepoints { y_string }; + for (auto k = x_codepoints.begin(), l = y_codepoints.begin(); + k != x_codepoints.end() && l != y_codepoints.end(); + ++k, ++l) { + if (*k != *l) { + if (*k < *l) { + return TriState::True; + } else { + return TriState::False; + } + } + } + ASSERT_NOT_REACHED(); + } + + // FIXME add BigInt cases here once we have BigInt + + auto x_numeric = x_primitive.to_number(interpreter); + if (interpreter.exception()) + return {}; + auto y_numeric = y_primitive.to_number(interpreter); + if (interpreter.exception()) + return {}; + + if (x_numeric.is_nan() || y_numeric.is_nan()) + return TriState::Unknown; + + if (x_numeric.is_positive_infinity() || y_numeric.is_negative_infinity()) + return TriState::False; + + if (x_numeric.is_negative_infinity() || y_numeric.is_positive_infinity()) + return TriState::True; + + auto x_value = x_numeric.as_double(); + auto y_value = y_numeric.as_double(); + + if (x_value < y_value) + return TriState::True; + else + return TriState::False; +} + } diff --git a/Libraries/LibJS/Runtime/Value.h b/Libraries/LibJS/Runtime/Value.h index 887a11ebcf..46520b00e6 100644 --- a/Libraries/LibJS/Runtime/Value.h +++ b/Libraries/LibJS/Runtime/Value.h @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -51,6 +52,12 @@ public: Accessor, }; + enum class PreferredType { + Default, + String, + Number, + }; + bool is_empty() const { return m_type == Type::Empty; } bool is_undefined() const { return m_type == Type::Undefined; } bool is_null() const { return m_type == Type::Null; } @@ -66,6 +73,8 @@ public: bool is_nan() const { return is_number() && __builtin_isnan(as_double()); } bool is_infinity() const { return is_number() && __builtin_isinf(as_double()); } + bool is_positive_infinity() const { return is_number() && __builtin_isinf_sign(as_double()) > 0; } + bool is_negative_infinity() const { return is_number() && __builtin_isinf_sign(as_double()) < 0; } bool is_positive_zero() const { return is_number() && 1.0 / as_double() == __builtin_huge_val(); } bool is_negative_zero() const { return is_number() && 1.0 / as_double() == -__builtin_huge_val(); } bool is_integer() const { return is_finite_number() && (i32)as_double() == as_double(); } @@ -201,7 +210,7 @@ public: String to_string(Interpreter&) const; PrimitiveString* to_primitive_string(Interpreter&); - Value to_primitive(Interpreter&) const; + Value to_primitive(Interpreter&, PreferredType preferred_type = PreferredType::Default) const; Object* to_object(Interpreter&) const; Value to_number(Interpreter&) const; double to_double(Interpreter&) const; @@ -282,6 +291,7 @@ bool strict_eq(Interpreter&, Value lhs, Value rhs); bool same_value(Interpreter&, Value lhs, Value rhs); bool same_value_zero(Interpreter&, Value lhs, Value rhs); bool same_value_non_numeric(Interpreter&, Value lhs, Value rhs); +TriState abstract_relation(Interpreter& interpreter, bool left_first, Value lhs, Value rhs); const LogStream& operator<<(const LogStream&, const Value&); diff --git a/Libraries/LibJS/Tests/binary-relational.js b/Libraries/LibJS/Tests/binary-relational.js new file mode 100644 index 0000000000..55884dd170 --- /dev/null +++ b/Libraries/LibJS/Tests/binary-relational.js @@ -0,0 +1,531 @@ +load("test-common.js"); + +try { + const vals = [ + 1, + 2, + [], + [1], + [2], + [1, 2], + "foo", + "fooo", + "🔥", + "❤️", + "bar", + {}, + { a: 1 }, + { a: 2 }, + { b: 1 }, + true, + false, + undefined, + null, + NaN, + +Infinity, + -Infinity, + ]; + + // Each row contains: x_index, y_index, (x < y), (x > y), (x <= y), (x >= y) + // where x = vals[x_index], y = vals[y_index] + const table = [ + [0, 0, false, false, true, true], + [0, 1, true, false, true, false], + [0, 2, false, true, false, true], + [0, 3, false, false, true, true], + [0, 4, true, false, true, false], + [0, 5, false, false, false, false], + [0, 6, false, false, false, false], + [0, 7, false, false, false, false], + [0, 8, false, false, false, false], + [0, 9, false, false, false, false], + [0, 10, false, false, false, false], + [0, 11, false, false, false, false], + [0, 12, false, false, false, false], + [0, 13, false, false, false, false], + [0, 14, false, false, false, false], + [0, 15, false, false, true, true], + [0, 16, false, true, false, true], + [0, 17, false, false, false, false], + [0, 18, false, true, false, true], + [0, 19, false, false, false, false], + [0, 20, true, false, true, false], + [0, 21, false, true, false, true], + [1, 0, false, true, false, true], + [1, 1, false, false, true, true], + [1, 2, false, true, false, true], + [1, 3, false, true, false, true], + [1, 4, false, false, true, true], + [1, 5, false, false, false, false], + [1, 6, false, false, false, false], + [1, 7, false, false, false, false], + [1, 8, false, false, false, false], + [1, 9, false, false, false, false], + [1, 10, false, false, false, false], + [1, 11, false, false, false, false], + [1, 12, false, false, false, false], + [1, 13, false, false, false, false], + [1, 14, false, false, false, false], + [1, 15, false, true, false, true], + [1, 16, false, true, false, true], + [1, 17, false, false, false, false], + [1, 18, false, true, false, true], + [1, 19, false, false, false, false], + [1, 20, true, false, true, false], + [1, 21, false, true, false, true], + [2, 0, true, false, true, false], + [2, 1, true, false, true, false], + [2, 2, false, false, true, true], + [2, 3, true, false, true, false], + [2, 4, true, false, true, false], + [2, 5, true, false, true, false], + [2, 6, true, false, true, false], + [2, 7, true, false, true, false], + [2, 8, true, false, true, false], + [2, 9, true, false, true, false], + [2, 10, true, false, true, false], + [2, 11, true, false, true, false], + [2, 12, true, false, true, false], + [2, 13, true, false, true, false], + [2, 14, true, false, true, false], + [2, 15, true, false, true, false], + [2, 16, false, false, true, true], + [2, 17, false, false, false, false], + [2, 18, false, false, true, true], + [2, 19, false, false, false, false], + [2, 20, true, false, true, false], + [2, 21, false, true, false, true], + [3, 0, false, false, true, true], + [3, 1, true, false, true, false], + [3, 2, false, true, false, true], + [3, 3, false, false, true, true], + [3, 4, true, false, true, false], + [3, 5, true, false, true, false], + [3, 6, true, false, true, false], + [3, 7, true, false, true, false], + [3, 8, true, false, true, false], + [3, 9, true, false, true, false], + [3, 10, true, false, true, false], + [3, 11, true, false, true, false], + [3, 12, true, false, true, false], + [3, 13, true, false, true, false], + [3, 14, true, false, true, false], + [3, 15, false, false, true, true], + [3, 16, false, true, false, true], + [3, 17, false, false, false, false], + [3, 18, false, true, false, true], + [3, 19, false, false, false, false], + [3, 20, true, false, true, false], + [3, 21, false, true, false, true], + [4, 0, false, true, false, true], + [4, 1, false, false, true, true], + [4, 2, false, true, false, true], + [4, 3, false, true, false, true], + [4, 4, false, false, true, true], + [4, 5, false, true, false, true], + [4, 6, true, false, true, false], + [4, 7, true, false, true, false], + [4, 8, true, false, true, false], + [4, 9, true, false, true, false], + [4, 10, true, false, true, false], + [4, 11, true, false, true, false], + [4, 12, true, false, true, false], + [4, 13, true, false, true, false], + [4, 14, true, false, true, false], + [4, 15, false, true, false, true], + [4, 16, false, true, false, true], + [4, 17, false, false, false, false], + [4, 18, false, true, false, true], + [4, 19, false, false, false, false], + [4, 20, true, false, true, false], + [4, 21, false, true, false, true], + [5, 0, false, false, false, false], + [5, 1, false, false, false, false], + [5, 2, false, true, false, true], + [5, 3, false, true, false, true], + [5, 4, true, false, true, false], + [5, 5, false, false, true, true], + [5, 6, true, false, true, false], + [5, 7, true, false, true, false], + [5, 8, true, false, true, false], + [5, 9, true, false, true, false], + [5, 10, true, false, true, false], + [5, 11, true, false, true, false], + [5, 12, true, false, true, false], + [5, 13, true, false, true, false], + [5, 14, true, false, true, false], + [5, 15, false, false, false, false], + [5, 16, false, false, false, false], + [5, 17, false, false, false, false], + [5, 18, false, false, false, false], + [5, 19, false, false, false, false], + [5, 20, false, false, false, false], + [5, 21, false, false, false, false], + [6, 0, false, false, false, false], + [6, 1, false, false, false, false], + [6, 2, false, true, false, true], + [6, 3, false, true, false, true], + [6, 4, false, true, false, true], + [6, 5, false, true, false, true], + [6, 6, false, false, true, true], + [6, 7, true, false, true, false], + [6, 8, true, false, true, false], + [6, 9, true, false, true, false], + [6, 10, false, true, false, true], + [6, 11, false, true, false, true], + [6, 12, false, true, false, true], + [6, 13, false, true, false, true], + [6, 14, false, true, false, true], + [6, 15, false, false, false, false], + [6, 16, false, false, false, false], + [6, 17, false, false, false, false], + [6, 18, false, false, false, false], + [6, 19, false, false, false, false], + [6, 20, false, false, false, false], + [6, 21, false, false, false, false], + [7, 0, false, false, false, false], + [7, 1, false, false, false, false], + [7, 2, false, true, false, true], + [7, 3, false, true, false, true], + [7, 4, false, true, false, true], + [7, 5, false, true, false, true], + [7, 6, false, true, false, true], + [7, 7, false, false, true, true], + [7, 8, true, false, true, false], + [7, 9, true, false, true, false], + [7, 10, false, true, false, true], + [7, 11, false, true, false, true], + [7, 12, false, true, false, true], + [7, 13, false, true, false, true], + [7, 14, false, true, false, true], + [7, 15, false, false, false, false], + [7, 16, false, false, false, false], + [7, 17, false, false, false, false], + [7, 18, false, false, false, false], + [7, 19, false, false, false, false], + [7, 20, false, false, false, false], + [7, 21, false, false, false, false], + [8, 0, false, false, false, false], + [8, 1, false, false, false, false], + [8, 2, false, true, false, true], + [8, 3, false, true, false, true], + [8, 4, false, true, false, true], + [8, 5, false, true, false, true], + [8, 6, false, true, false, true], + [8, 7, false, true, false, true], + [8, 8, false, false, true, true], + [8, 9, false, true, false, true], + [8, 10, false, true, false, true], + [8, 11, false, true, false, true], + [8, 12, false, true, false, true], + [8, 13, false, true, false, true], + [8, 14, false, true, false, true], + [8, 15, false, false, false, false], + [8, 16, false, false, false, false], + [8, 17, false, false, false, false], + [8, 18, false, false, false, false], + [8, 19, false, false, false, false], + [8, 20, false, false, false, false], + [8, 21, false, false, false, false], + [9, 0, false, false, false, false], + [9, 1, false, false, false, false], + [9, 2, false, true, false, true], + [9, 3, false, true, false, true], + [9, 4, false, true, false, true], + [9, 5, false, true, false, true], + [9, 6, false, true, false, true], + [9, 7, false, true, false, true], + [9, 8, true, false, true, false], + [9, 9, false, false, true, true], + [9, 10, false, true, false, true], + [9, 11, false, true, false, true], + [9, 12, false, true, false, true], + [9, 13, false, true, false, true], + [9, 14, false, true, false, true], + [9, 15, false, false, false, false], + [9, 16, false, false, false, false], + [9, 17, false, false, false, false], + [9, 18, false, false, false, false], + [9, 19, false, false, false, false], + [9, 20, false, false, false, false], + [9, 21, false, false, false, false], + [10, 0, false, false, false, false], + [10, 1, false, false, false, false], + [10, 2, false, true, false, true], + [10, 3, false, true, false, true], + [10, 4, false, true, false, true], + [10, 5, false, true, false, true], + [10, 6, true, false, true, false], + [10, 7, true, false, true, false], + [10, 8, true, false, true, false], + [10, 9, true, false, true, false], + [10, 10, false, false, true, true], + [10, 11, false, true, false, true], + [10, 12, false, true, false, true], + [10, 13, false, true, false, true], + [10, 14, false, true, false, true], + [10, 15, false, false, false, false], + [10, 16, false, false, false, false], + [10, 17, false, false, false, false], + [10, 18, false, false, false, false], + [10, 19, false, false, false, false], + [10, 20, false, false, false, false], + [10, 21, false, false, false, false], + [11, 0, false, false, false, false], + [11, 1, false, false, false, false], + [11, 2, false, true, false, true], + [11, 3, false, true, false, true], + [11, 4, false, true, false, true], + [11, 5, false, true, false, true], + [11, 6, true, false, true, false], + [11, 7, true, false, true, false], + [11, 8, true, false, true, false], + [11, 9, true, false, true, false], + [11, 10, true, false, true, false], + [11, 11, false, false, true, true], + [11, 12, false, false, true, true], + [11, 13, false, false, true, true], + [11, 14, false, false, true, true], + [11, 15, false, false, false, false], + [11, 16, false, false, false, false], + [11, 17, false, false, false, false], + [11, 18, false, false, false, false], + [11, 19, false, false, false, false], + [11, 20, false, false, false, false], + [11, 21, false, false, false, false], + [12, 0, false, false, false, false], + [12, 1, false, false, false, false], + [12, 2, false, true, false, true], + [12, 3, false, true, false, true], + [12, 4, false, true, false, true], + [12, 5, false, true, false, true], + [12, 6, true, false, true, false], + [12, 7, true, false, true, false], + [12, 8, true, false, true, false], + [12, 9, true, false, true, false], + [12, 10, true, false, true, false], + [12, 11, false, false, true, true], + [12, 12, false, false, true, true], + [12, 13, false, false, true, true], + [12, 14, false, false, true, true], + [12, 15, false, false, false, false], + [12, 16, false, false, false, false], + [12, 17, false, false, false, false], + [12, 18, false, false, false, false], + [12, 19, false, false, false, false], + [12, 20, false, false, false, false], + [12, 21, false, false, false, false], + [13, 0, false, false, false, false], + [13, 1, false, false, false, false], + [13, 2, false, true, false, true], + [13, 3, false, true, false, true], + [13, 4, false, true, false, true], + [13, 5, false, true, false, true], + [13, 6, true, false, true, false], + [13, 7, true, false, true, false], + [13, 8, true, false, true, false], + [13, 9, true, false, true, false], + [13, 10, true, false, true, false], + [13, 11, false, false, true, true], + [13, 12, false, false, true, true], + [13, 13, false, false, true, true], + [13, 14, false, false, true, true], + [13, 15, false, false, false, false], + [13, 16, false, false, false, false], + [13, 17, false, false, false, false], + [13, 18, false, false, false, false], + [13, 19, false, false, false, false], + [13, 20, false, false, false, false], + [13, 21, false, false, false, false], + [14, 0, false, false, false, false], + [14, 1, false, false, false, false], + [14, 2, false, true, false, true], + [14, 3, false, true, false, true], + [14, 4, false, true, false, true], + [14, 5, false, true, false, true], + [14, 6, true, false, true, false], + [14, 7, true, false, true, false], + [14, 8, true, false, true, false], + [14, 9, true, false, true, false], + [14, 10, true, false, true, false], + [14, 11, false, false, true, true], + [14, 12, false, false, true, true], + [14, 13, false, false, true, true], + [14, 14, false, false, true, true], + [14, 15, false, false, false, false], + [14, 16, false, false, false, false], + [14, 17, false, false, false, false], + [14, 18, false, false, false, false], + [14, 19, false, false, false, false], + [14, 20, false, false, false, false], + [14, 21, false, false, false, false], + [15, 0, false, false, true, true], + [15, 1, true, false, true, false], + [15, 2, false, true, false, true], + [15, 3, false, false, true, true], + [15, 4, true, false, true, false], + [15, 5, false, false, false, false], + [15, 6, false, false, false, false], + [15, 7, false, false, false, false], + [15, 8, false, false, false, false], + [15, 9, false, false, false, false], + [15, 10, false, false, false, false], + [15, 11, false, false, false, false], + [15, 12, false, false, false, false], + [15, 13, false, false, false, false], + [15, 14, false, false, false, false], + [15, 15, false, false, true, true], + [15, 16, false, true, false, true], + [15, 17, false, false, false, false], + [15, 18, false, true, false, true], + [15, 19, false, false, false, false], + [15, 20, true, false, true, false], + [15, 21, false, true, false, true], + [16, 0, true, false, true, false], + [16, 1, true, false, true, false], + [16, 2, false, false, true, true], + [16, 3, true, false, true, false], + [16, 4, true, false, true, false], + [16, 5, false, false, false, false], + [16, 6, false, false, false, false], + [16, 7, false, false, false, false], + [16, 8, false, false, false, false], + [16, 9, false, false, false, false], + [16, 10, false, false, false, false], + [16, 11, false, false, false, false], + [16, 12, false, false, false, false], + [16, 13, false, false, false, false], + [16, 14, false, false, false, false], + [16, 15, true, false, true, false], + [16, 16, false, false, true, true], + [16, 17, false, false, false, false], + [16, 18, false, false, true, true], + [16, 19, false, false, false, false], + [16, 20, true, false, true, false], + [16, 21, false, true, false, true], + [17, 0, false, false, false, false], + [17, 1, false, false, false, false], + [17, 2, false, false, false, false], + [17, 3, false, false, false, false], + [17, 4, false, false, false, false], + [17, 5, false, false, false, false], + [17, 6, false, false, false, false], + [17, 7, false, false, false, false], + [17, 8, false, false, false, false], + [17, 9, false, false, false, false], + [17, 10, false, false, false, false], + [17, 11, false, false, false, false], + [17, 12, false, false, false, false], + [17, 13, false, false, false, false], + [17, 14, false, false, false, false], + [17, 15, false, false, false, false], + [17, 16, false, false, false, false], + [17, 17, false, false, false, false], + [17, 18, false, false, false, false], + [17, 19, false, false, false, false], + [17, 20, false, false, false, false], + [17, 21, false, false, false, false], + [18, 0, true, false, true, false], + [18, 1, true, false, true, false], + [18, 2, false, false, true, true], + [18, 3, true, false, true, false], + [18, 4, true, false, true, false], + [18, 5, false, false, false, false], + [18, 6, false, false, false, false], + [18, 7, false, false, false, false], + [18, 8, false, false, false, false], + [18, 9, false, false, false, false], + [18, 10, false, false, false, false], + [18, 11, false, false, false, false], + [18, 12, false, false, false, false], + [18, 13, false, false, false, false], + [18, 14, false, false, false, false], + [18, 15, true, false, true, false], + [18, 16, false, false, true, true], + [18, 17, false, false, false, false], + [18, 18, false, false, true, true], + [18, 19, false, false, false, false], + [18, 20, true, false, true, false], + [18, 21, false, true, false, true], + [19, 0, false, false, false, false], + [19, 1, false, false, false, false], + [19, 2, false, false, false, false], + [19, 3, false, false, false, false], + [19, 4, false, false, false, false], + [19, 5, false, false, false, false], + [19, 6, false, false, false, false], + [19, 7, false, false, false, false], + [19, 8, false, false, false, false], + [19, 9, false, false, false, false], + [19, 10, false, false, false, false], + [19, 11, false, false, false, false], + [19, 12, false, false, false, false], + [19, 13, false, false, false, false], + [19, 14, false, false, false, false], + [19, 15, false, false, false, false], + [19, 16, false, false, false, false], + [19, 17, false, false, false, false], + [19, 18, false, false, false, false], + [19, 19, false, false, false, false], + [19, 20, false, false, false, false], + [19, 21, false, false, false, false], + [20, 0, false, true, false, true], + [20, 1, false, true, false, true], + [20, 2, false, true, false, true], + [20, 3, false, true, false, true], + [20, 4, false, true, false, true], + [20, 5, false, false, false, false], + [20, 6, false, false, false, false], + [20, 7, false, false, false, false], + [20, 8, false, false, false, false], + [20, 9, false, false, false, false], + [20, 10, false, false, false, false], + [20, 11, false, false, false, false], + [20, 12, false, false, false, false], + [20, 13, false, false, false, false], + [20, 14, false, false, false, false], + [20, 15, false, true, false, true], + [20, 16, false, true, false, true], + [20, 17, false, false, false, false], + [20, 18, false, true, false, true], + [20, 19, false, false, false, false], + [20, 20, false, false, true, true], + [20, 21, false, true, false, true], + [21, 0, true, false, true, false], + [21, 1, true, false, true, false], + [21, 2, true, false, true, false], + [21, 3, true, false, true, false], + [21, 4, true, false, true, false], + [21, 5, false, false, false, false], + [21, 6, false, false, false, false], + [21, 7, false, false, false, false], + [21, 8, false, false, false, false], + [21, 9, false, false, false, false], + [21, 10, false, false, false, false], + [21, 11, false, false, false, false], + [21, 12, false, false, false, false], + [21, 13, false, false, false, false], + [21, 14, false, false, false, false], + [21, 15, true, false, true, false], + [21, 16, true, false, true, false], + [21, 17, false, false, false, false], + [21, 18, true, false, true, false], + [21, 19, false, false, false, false], + [21, 20, true, false, true, false], + [21, 21, false, false, true, true], + ]; + + for (let test of table) { + let x = vals[test[0]]; + let y = vals[test[1]]; + + assert(x < y === test[2]); + assert(x > y === test[3]); + assert(x <= y === test[4]); + assert(x >= y === test[5]); + } + + console.log("PASS"); +} catch (e) { + console.log("FAIL: " + e); +}