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); +}