pitrou commented on code in PR #13784:
URL: https://github.com/apache/arrow/pull/13784#discussion_r940204831


##########
cpp/src/arrow/compare.h:
##########
@@ -32,8 +36,14 @@ class DataType;
 class Tensor;
 class SparseTensor;
 struct Scalar;
+template <typename>
+struct NumericScalar;
+template <typename>
+class Result;

Review Comment:
   Actually, all these declarations (starting from `class Array`) should 
already be in `arrow/type_fwd.h` (thanks for including this!), so can be 
removed.



##########
cpp/src/arrow/compare.h:
##########
@@ -142,4 +152,208 @@ bool ARROW_EXPORT
 ScalarApproxEquals(const Scalar& left, const Scalar& right,
                    const EqualOptions& options = EqualOptions::Defaults());
 
+class OrderOptions {
+ public:
+  // Whether NaNs are considered least in order
+  bool nans_least() const { return nans_least_; }

Review Comment:
   That's... weird. In which situation you need such a convention?



##########
cpp/src/arrow/scalar_test.cc:
##########
@@ -156,6 +157,90 @@ TYPED_TEST(TestNumericScalar, Basics) {
   ASSERT_FALSE(two->ApproxEquals(ScalarType(3)));
 }
 
+template <typename T>
+void TestNumericScalarOrder(typename NumericScalar<T>::ValueType v1,
+                            typename NumericScalar<T>::ValueType v2,
+                            const OrderOptions& options, bool expectedLessThan,
+                            bool expectedIsAtMost, bool expectedMoreThan,
+                            bool expectedIsAtLeast) {
+  NumericScalar<T> s1{v1}, s2{v2};
+
+#define ARROW_NUMERIC_ORDER_TEST(Op)                                           
         \
+  ASSERT_EQ(expected##Op, Scalar##Op(s1, s2, options));                        
         \
+  ASSERT_EQ(expected##Op, s1.Op(s2, options));                                 
         \
+  ASSERT_OK_AND_ASSIGN(auto actual##Op, Scalar##Op(static_cast<Scalar&>(s1),   
         \
+                                                   static_cast<Scalar&>(s2), 
options)); \
+  ASSERT_EQ(expected##Op, actual##Op);
+
+  ARROW_NUMERIC_ORDER_TEST(LessThan);
+  ARROW_NUMERIC_ORDER_TEST(IsAtMost);
+  ARROW_NUMERIC_ORDER_TEST(MoreThan);
+  ARROW_NUMERIC_ORDER_TEST(IsAtLeast);
+
+#undef ARROW_NUMERIC_ORDER_TEST
+}
+
+template <typename T>
+void TestNumericScalarOrder(typename NumericScalar<T>::ValueType v1,
+                            typename NumericScalar<T>::ValueType v2,
+                            bool expectedLessThan, bool expectedIsAtMost,
+                            bool expectedMoreThan, bool expectedIsAtLeast) {
+  auto p1 = std::make_shared<NumericScalar<T>>(v1);
+  auto p2 = std::make_shared<NumericScalar<T>>(v2);
+  auto& s1 = *p1;
+  auto& s2 = *p2;

Review Comment:
   ```suggestion
     const auto& s1 = *p1;
     const auto& s2 = *p2;
   ```



##########
cpp/src/arrow/compare.h:
##########
@@ -142,4 +152,208 @@ bool ARROW_EXPORT
 ScalarApproxEquals(const Scalar& left, const Scalar& right,
                    const EqualOptions& options = EqualOptions::Defaults());
 
+class OrderOptions {
+ public:
+  // Whether NaNs are considered least in order
+  bool nans_least() const { return nans_least_; }
+
+  OrderOptions nans_least(bool v) const {
+    auto res = OrderOptions(*this);
+    res.nans_least_ = v;
+    return res;
+  }
+
+  /// The absolute tolerance for approximate comparisons of floating-point 
values.
+  double atolf() const { return atolf_; }
+
+  /// Return a new EqualOptions object with the "atol" property changed.
+  OrderOptions atolf(double v) const {
+    auto res = OrderOptions(*this);
+    res.atolf_ = v;
+    return res;
+  }
+
+  /// The absolute tolerance for approximate comparisons of integer values.
+  uint64_t atold() const { return atold_; }
+
+  /// Return a new EqualOptions object with the "atol" property changed.
+  OrderOptions atold(double v) const {
+    auto res = OrderOptions(*this);
+    res.atold_ = v;
+    return res;
+  }
+
+  static OrderOptions Defaults() { return {}; }
+
+ protected:
+  double atolf_ = kDefaultOrderFloatingPointAbsoluteTolerance;
+  uint64_t atold_ = kDefaultOrderIntegerAbsoluteTolerance;
+  bool nans_least_ = false;
+};
+
+namespace internal {
+
+template <typename T>
+T TypeAdjustedTolerance(double tolerance) {
+  T max = std::numeric_limits<T>::max();
+  return tolerance >= max ? max : static_cast<T>(tolerance);
+}
+
+template <typename T>
+T TypeAdjustedTolerance(uint64_t tolerance) {
+  T max = std::numeric_limits<T>::max();
+  return tolerance >= static_cast<uint64_t>(max) ? max : 
static_cast<T>(tolerance);
+}
+
+template <typename T, template <typename> class Op,
+          typename = std::enable_if<std::is_base_of<Scalar, T>::value>>
+bool NumericScalarCompare(const T& left, const T& right, const OrderOptions& 
options) {

Review Comment:
   I'm not sure why we're exposing these publicly? Aren't the high-level 
variants taking a `const Scalar&` sufficient?



##########
cpp/src/arrow/scalar_test.cc:
##########
@@ -156,6 +157,90 @@ TYPED_TEST(TestNumericScalar, Basics) {
   ASSERT_FALSE(two->ApproxEquals(ScalarType(3)));
 }
 
+template <typename T>
+void TestNumericScalarOrder(typename NumericScalar<T>::ValueType v1,
+                            typename NumericScalar<T>::ValueType v2,
+                            const OrderOptions& options, bool expectedLessThan,
+                            bool expectedIsAtMost, bool expectedMoreThan,
+                            bool expectedIsAtLeast) {
+  NumericScalar<T> s1{v1}, s2{v2};
+
+#define ARROW_NUMERIC_ORDER_TEST(Op)                                           
         \
+  ASSERT_EQ(expected##Op, Scalar##Op(s1, s2, options));                        
         \
+  ASSERT_EQ(expected##Op, s1.Op(s2, options));                                 
         \
+  ASSERT_OK_AND_ASSIGN(auto actual##Op, Scalar##Op(static_cast<Scalar&>(s1),   
         \
+                                                   static_cast<Scalar&>(s2), 
options)); \
+  ASSERT_EQ(expected##Op, actual##Op);
+
+  ARROW_NUMERIC_ORDER_TEST(LessThan);
+  ARROW_NUMERIC_ORDER_TEST(IsAtMost);
+  ARROW_NUMERIC_ORDER_TEST(MoreThan);
+  ARROW_NUMERIC_ORDER_TEST(IsAtLeast);
+
+#undef ARROW_NUMERIC_ORDER_TEST
+}
+
+template <typename T>
+void TestNumericScalarOrder(typename NumericScalar<T>::ValueType v1,
+                            typename NumericScalar<T>::ValueType v2,
+                            bool expectedLessThan, bool expectedIsAtMost,
+                            bool expectedMoreThan, bool expectedIsAtLeast) {
+  auto p1 = std::make_shared<NumericScalar<T>>(v1);
+  auto p2 = std::make_shared<NumericScalar<T>>(v2);
+  auto& s1 = *p1;
+  auto& s2 = *p2;
+  using Order = util::OrderComparable<NumericScalar<T>>;
+  ASSERT_EQ(expectedLessThan, typename Order::PtrsLessThan()(p1, p2));
+  ASSERT_EQ(expectedIsAtMost, typename Order::PtrsIsAtMost()(p1, p2));
+  ASSERT_EQ(expectedMoreThan, typename Order::PtrsMoreThan()(p1, p2));
+  ASSERT_EQ(expectedIsAtLeast, typename Order::PtrsIsAtLeast()(p1, p2));
+  ASSERT_EQ(expectedLessThan, s1 < s2);
+  ASSERT_EQ(expectedIsAtMost, s1 <= s2);
+  ASSERT_EQ(expectedMoreThan, s1 > s2);
+  ASSERT_EQ(expectedIsAtLeast, s1 >= s2);
+  TestNumericScalarOrder<T>(v1, v2, OrderOptions::Defaults(), expectedLessThan,
+                            expectedIsAtMost, expectedMoreThan, 
expectedIsAtLeast);
+}
+
+#define TEST_FLOATING_POINT_SCALAR_ORDER(...)        \
+  {                                                  \
+    TestNumericScalarOrder<FloatType>(__VA_ARGS__);  \

Review Comment:
   Hmm... I'm not sure `__VA_ARGS__` is supported on all compilers? 



##########
cpp/src/arrow/compare.h:
##########
@@ -142,4 +152,208 @@ bool ARROW_EXPORT
 ScalarApproxEquals(const Scalar& left, const Scalar& right,
                    const EqualOptions& options = EqualOptions::Defaults());
 
+class OrderOptions {
+ public:
+  // Whether NaNs are considered least in order
+  bool nans_least() const { return nans_least_; }
+
+  OrderOptions nans_least(bool v) const {
+    auto res = OrderOptions(*this);
+    res.nans_least_ = v;
+    return res;
+  }
+
+  /// The absolute tolerance for approximate comparisons of floating-point 
values.
+  double atolf() const { return atolf_; }
+
+  /// Return a new EqualOptions object with the "atol" property changed.
+  OrderOptions atolf(double v) const {
+    auto res = OrderOptions(*this);
+    res.atolf_ = v;
+    return res;
+  }
+
+  /// The absolute tolerance for approximate comparisons of integer values.

Review Comment:
   Why is it this useful?



##########
cpp/src/arrow/compare.h:
##########
@@ -142,4 +152,208 @@ bool ARROW_EXPORT
 ScalarApproxEquals(const Scalar& left, const Scalar& right,
                    const EqualOptions& options = EqualOptions::Defaults());
 
+class OrderOptions {
+ public:
+  // Whether NaNs are considered least in order
+  bool nans_least() const { return nans_least_; }
+
+  OrderOptions nans_least(bool v) const {
+    auto res = OrderOptions(*this);
+    res.nans_least_ = v;
+    return res;
+  }
+
+  /// The absolute tolerance for approximate comparisons of floating-point 
values.
+  double atolf() const { return atolf_; }
+
+  /// Return a new EqualOptions object with the "atol" property changed.
+  OrderOptions atolf(double v) const {
+    auto res = OrderOptions(*this);
+    res.atolf_ = v;
+    return res;
+  }
+
+  /// The absolute tolerance for approximate comparisons of integer values.
+  uint64_t atold() const { return atold_; }
+
+  /// Return a new EqualOptions object with the "atol" property changed.
+  OrderOptions atold(double v) const {
+    auto res = OrderOptions(*this);
+    res.atold_ = v;
+    return res;
+  }
+
+  static OrderOptions Defaults() { return {}; }
+
+ protected:
+  double atolf_ = kDefaultOrderFloatingPointAbsoluteTolerance;
+  uint64_t atold_ = kDefaultOrderIntegerAbsoluteTolerance;
+  bool nans_least_ = false;
+};
+
+namespace internal {
+
+template <typename T>
+T TypeAdjustedTolerance(double tolerance) {
+  T max = std::numeric_limits<T>::max();
+  return tolerance >= max ? max : static_cast<T>(tolerance);
+}
+
+template <typename T>
+T TypeAdjustedTolerance(uint64_t tolerance) {
+  T max = std::numeric_limits<T>::max();
+  return tolerance >= static_cast<uint64_t>(max) ? max : 
static_cast<T>(tolerance);
+}
+
+template <typename T, template <typename> class Op,
+          typename = std::enable_if<std::is_base_of<Scalar, T>::value>>
+bool NumericScalarCompare(const T& left, const T& right, const OrderOptions& 
options) {
+  if (left.type != right.type) {
+    return false;  // arbitrary - unequal types are unordered
+  }
+  auto ty = left.type;
+  bool fp_type = is_floating(left.type->id());
+  bool left_nan = !left.is_valid || (fp_type && std::isnan(left.value));
+  bool right_nan = !right.is_valid || (fp_type && std::isnan(right.value));
+  using V = decltype(left.value);
+  Op<V> cmp;
+  if (left_nan) {
+    if (right_nan) {
+      return false;  // arbitrary - NaNs are unordered
+    } else {
+      return options.nans_least() ? cmp(true) : cmp(false);
+    }
+  } else {
+    if (right_nan) {
+      return options.nans_least() ? cmp(false) : cmp(true);
+    } else {
+      V tolerance = fp_type ? TypeAdjustedTolerance<V>(options.atolf())
+                            : TypeAdjustedTolerance<V>(options.atold());
+      return cmp(left.value, right.value, tolerance);
+    }
+  }
+}
+
+template <typename T>
+struct LessThanOp {
+  bool operator()(T left, T right, T tolerance) {
+    return (left < right) || (left - right < tolerance);
+  }
+  bool operator()(bool left_least) { return left_least; }
+};
+
+template <typename T>
+struct IsAtMostOp {
+  bool operator()(T left, T right, T tolerance) {
+    return (left <= right) || (left - right <= tolerance);
+  }
+  bool operator()(bool left_least) { return left_least; }
+};
+
+template <typename T>
+struct MoreThanOp {
+  bool operator()(T left, T right, T tolerance) {
+    return (left > right) || (right - left < tolerance);
+  }
+  bool operator()(bool left_least) { return !left_least; }
+};
+
+template <typename T>
+struct IsAtLeastOp {
+  bool operator()(T left, T right, T tolerance) {
+    return (left >= right) || (right - left <= tolerance);
+  }
+  bool operator()(bool left_least) { return !left_least; }
+};
+
+}  // namespace internal
+
+/// Returns true if left numeric scalar is less than right numeric scalar
+/// \param[in] left a NumericScalar
+/// \param[in] right a NumericScalar
+/// \param[in] options comparison options
+template <typename T>
+bool ScalarLessThan(const NumericScalar<T>& left, const NumericScalar<T>& 
right,
+                    const OrderOptions& options = OrderOptions::Defaults()) {
+  return internal::NumericScalarCompare<NumericScalar<T>, 
internal::LessThanOp>(
+      left, right, options);
+}
+
+/// Returns true if left numeric scalar is at most right numeric scalar
+/// \param[in] left a NumericScalar
+/// \param[in] right a NumericScalar
+/// \param[in] options comparison options
+template <typename T>
+bool ScalarIsAtMost(const NumericScalar<T>& left, const NumericScalar<T>& 
right,

Review Comment:
   In my experience this is usually spelled "LessEqual" rather than "IsAtMost", 
is this convention widely used in some places?
   



##########
cpp/src/arrow/compare.cc:
##########
@@ -1336,4 +1336,52 @@ bool TypeEquals(const DataType& left, const DataType& 
right, bool check_metadata
   }
 }
 
+#define ARROW_CMP_TYPE_CASE(id, T, Op)                           \
+  case Type::id:                                                 \
+    return internal::NumericScalarCompare<NumericScalar<T>, Op>( \
+        static_cast<const NumericScalar<T>&>(left),              \
+        static_cast<const NumericScalar<T>&>(right), options);
+
+#define ARROW_CMP_FUNCTION_BODY(Op)                                    \
+  if (left.type != right.type) {                                       \
+    return Status::Invalid("comparing unequal types is unsupported");  \

Review Comment:
   Should be `TypeError`.



##########
cpp/src/arrow/compare.h:
##########
@@ -142,4 +152,208 @@ bool ARROW_EXPORT
 ScalarApproxEquals(const Scalar& left, const Scalar& right,
                    const EqualOptions& options = EqualOptions::Defaults());
 
+class OrderOptions {
+ public:
+  // Whether NaNs are considered least in order
+  bool nans_least() const { return nans_least_; }
+
+  OrderOptions nans_least(bool v) const {
+    auto res = OrderOptions(*this);
+    res.nans_least_ = v;
+    return res;
+  }
+
+  /// The absolute tolerance for approximate comparisons of floating-point 
values.
+  double atolf() const { return atolf_; }
+
+  /// Return a new EqualOptions object with the "atol" property changed.
+  OrderOptions atolf(double v) const {
+    auto res = OrderOptions(*this);
+    res.atolf_ = v;
+    return res;
+  }
+
+  /// The absolute tolerance for approximate comparisons of integer values.
+  uint64_t atold() const { return atold_; }
+
+  /// Return a new EqualOptions object with the "atol" property changed.
+  OrderOptions atold(double v) const {
+    auto res = OrderOptions(*this);
+    res.atold_ = v;
+    return res;
+  }
+
+  static OrderOptions Defaults() { return {}; }
+
+ protected:
+  double atolf_ = kDefaultOrderFloatingPointAbsoluteTolerance;
+  uint64_t atold_ = kDefaultOrderIntegerAbsoluteTolerance;
+  bool nans_least_ = false;
+};
+
+namespace internal {
+
+template <typename T>
+T TypeAdjustedTolerance(double tolerance) {
+  T max = std::numeric_limits<T>::max();
+  return tolerance >= max ? max : static_cast<T>(tolerance);
+}
+
+template <typename T>
+T TypeAdjustedTolerance(uint64_t tolerance) {
+  T max = std::numeric_limits<T>::max();
+  return tolerance >= static_cast<uint64_t>(max) ? max : 
static_cast<T>(tolerance);
+}
+
+template <typename T, template <typename> class Op,
+          typename = std::enable_if<std::is_base_of<Scalar, T>::value>>
+bool NumericScalarCompare(const T& left, const T& right, const OrderOptions& 
options) {
+  if (left.type != right.type) {
+    return false;  // arbitrary - unequal types are unordered
+  }
+  auto ty = left.type;
+  bool fp_type = is_floating(left.type->id());
+  bool left_nan = !left.is_valid || (fp_type && std::isnan(left.value));
+  bool right_nan = !right.is_valid || (fp_type && std::isnan(right.value));
+  using V = decltype(left.value);
+  Op<V> cmp;
+  if (left_nan) {
+    if (right_nan) {
+      return false;  // arbitrary - NaNs are unordered
+    } else {
+      return options.nans_least() ? cmp(true) : cmp(false);
+    }
+  } else {
+    if (right_nan) {
+      return options.nans_least() ? cmp(false) : cmp(true);
+    } else {
+      V tolerance = fp_type ? TypeAdjustedTolerance<V>(options.atolf())
+                            : TypeAdjustedTolerance<V>(options.atold());
+      return cmp(left.value, right.value, tolerance);
+    }
+  }
+}
+
+template <typename T>
+struct LessThanOp {
+  bool operator()(T left, T right, T tolerance) {
+    return (left < right) || (left - right < tolerance);
+  }
+  bool operator()(bool left_least) { return left_least; }
+};
+
+template <typename T>
+struct IsAtMostOp {
+  bool operator()(T left, T right, T tolerance) {
+    return (left <= right) || (left - right <= tolerance);
+  }
+  bool operator()(bool left_least) { return left_least; }
+};
+
+template <typename T>
+struct MoreThanOp {
+  bool operator()(T left, T right, T tolerance) {
+    return (left > right) || (right - left < tolerance);
+  }
+  bool operator()(bool left_least) { return !left_least; }
+};
+
+template <typename T>
+struct IsAtLeastOp {
+  bool operator()(T left, T right, T tolerance) {
+    return (left >= right) || (right - left <= tolerance);
+  }
+  bool operator()(bool left_least) { return !left_least; }
+};
+
+}  // namespace internal
+
+/// Returns true if left numeric scalar is less than right numeric scalar
+/// \param[in] left a NumericScalar
+/// \param[in] right a NumericScalar
+/// \param[in] options comparison options
+template <typename T>
+bool ScalarLessThan(const NumericScalar<T>& left, const NumericScalar<T>& 
right,
+                    const OrderOptions& options = OrderOptions::Defaults()) {
+  return internal::NumericScalarCompare<NumericScalar<T>, 
internal::LessThanOp>(
+      left, right, options);
+}
+
+/// Returns true if left numeric scalar is at most right numeric scalar
+/// \param[in] left a NumericScalar
+/// \param[in] right a NumericScalar
+/// \param[in] options comparison options
+template <typename T>
+bool ScalarIsAtMost(const NumericScalar<T>& left, const NumericScalar<T>& 
right,
+                    const OrderOptions& options = OrderOptions::Defaults()) {
+  return internal::NumericScalarCompare<NumericScalar<T>, 
internal::IsAtMostOp>(
+      left, right, options);
+}
+
+/// Returns true if left numeric scalar is more than right numeric scalar
+/// \param[in] left a NumericScalar
+/// \param[in] right a NumericScalar
+/// \param[in] options comparison options
+template <typename T>
+bool ScalarMoreThan(const NumericScalar<T>& left, const NumericScalar<T>& 
right,
+                    const OrderOptions& options = OrderOptions::Defaults()) {
+  return internal::NumericScalarCompare<NumericScalar<T>, 
internal::MoreThanOp>(
+      left, right, options);

Review Comment:
   Could perhaps save on code generation by avoiding a separate op for this:
   ```suggestion
     return internal::NumericScalarCompare<NumericScalar<T>, 
internal::LessEqualOp>(
         right, left, options);
   ```



##########
cpp/src/arrow/compare.h:
##########
@@ -142,4 +152,208 @@ bool ARROW_EXPORT
 ScalarApproxEquals(const Scalar& left, const Scalar& right,
                    const EqualOptions& options = EqualOptions::Defaults());
 
+class OrderOptions {
+ public:
+  // Whether NaNs are considered least in order
+  bool nans_least() const { return nans_least_; }
+
+  OrderOptions nans_least(bool v) const {
+    auto res = OrderOptions(*this);
+    res.nans_least_ = v;
+    return res;
+  }
+
+  /// The absolute tolerance for approximate comparisons of floating-point 
values.
+  double atolf() const { return atolf_; }
+
+  /// Return a new EqualOptions object with the "atol" property changed.
+  OrderOptions atolf(double v) const {
+    auto res = OrderOptions(*this);
+    res.atolf_ = v;
+    return res;
+  }
+
+  /// The absolute tolerance for approximate comparisons of integer values.
+  uint64_t atold() const { return atold_; }
+
+  /// Return a new EqualOptions object with the "atol" property changed.
+  OrderOptions atold(double v) const {
+    auto res = OrderOptions(*this);
+    res.atold_ = v;
+    return res;
+  }
+
+  static OrderOptions Defaults() { return {}; }
+
+ protected:
+  double atolf_ = kDefaultOrderFloatingPointAbsoluteTolerance;
+  uint64_t atold_ = kDefaultOrderIntegerAbsoluteTolerance;
+  bool nans_least_ = false;
+};
+
+namespace internal {
+
+template <typename T>
+T TypeAdjustedTolerance(double tolerance) {
+  T max = std::numeric_limits<T>::max();
+  return tolerance >= max ? max : static_cast<T>(tolerance);
+}
+
+template <typename T>
+T TypeAdjustedTolerance(uint64_t tolerance) {
+  T max = std::numeric_limits<T>::max();
+  return tolerance >= static_cast<uint64_t>(max) ? max : 
static_cast<T>(tolerance);
+}
+
+template <typename T, template <typename> class Op,
+          typename = std::enable_if<std::is_base_of<Scalar, T>::value>>
+bool NumericScalarCompare(const T& left, const T& right, const OrderOptions& 
options) {
+  if (left.type != right.type) {
+    return false;  // arbitrary - unequal types are unordered
+  }
+  auto ty = left.type;
+  bool fp_type = is_floating(left.type->id());
+  bool left_nan = !left.is_valid || (fp_type && std::isnan(left.value));
+  bool right_nan = !right.is_valid || (fp_type && std::isnan(right.value));
+  using V = decltype(left.value);
+  Op<V> cmp;
+  if (left_nan) {
+    if (right_nan) {
+      return false;  // arbitrary - NaNs are unordered
+    } else {
+      return options.nans_least() ? cmp(true) : cmp(false);
+    }
+  } else {
+    if (right_nan) {
+      return options.nans_least() ? cmp(false) : cmp(true);
+    } else {
+      V tolerance = fp_type ? TypeAdjustedTolerance<V>(options.atolf())
+                            : TypeAdjustedTolerance<V>(options.atold());
+      return cmp(left.value, right.value, tolerance);
+    }
+  }
+}
+
+template <typename T>
+struct LessThanOp {
+  bool operator()(T left, T right, T tolerance) {
+    return (left < right) || (left - right < tolerance);
+  }
+  bool operator()(bool left_least) { return left_least; }
+};
+
+template <typename T>
+struct IsAtMostOp {
+  bool operator()(T left, T right, T tolerance) {
+    return (left <= right) || (left - right <= tolerance);
+  }
+  bool operator()(bool left_least) { return left_least; }
+};
+
+template <typename T>
+struct MoreThanOp {
+  bool operator()(T left, T right, T tolerance) {
+    return (left > right) || (right - left < tolerance);
+  }
+  bool operator()(bool left_least) { return !left_least; }
+};
+
+template <typename T>
+struct IsAtLeastOp {
+  bool operator()(T left, T right, T tolerance) {
+    return (left >= right) || (right - left <= tolerance);
+  }
+  bool operator()(bool left_least) { return !left_least; }
+};
+
+}  // namespace internal
+
+/// Returns true if left numeric scalar is less than right numeric scalar
+/// \param[in] left a NumericScalar
+/// \param[in] right a NumericScalar
+/// \param[in] options comparison options
+template <typename T>
+bool ScalarLessThan(const NumericScalar<T>& left, const NumericScalar<T>& 
right,
+                    const OrderOptions& options = OrderOptions::Defaults()) {
+  return internal::NumericScalarCompare<NumericScalar<T>, 
internal::LessThanOp>(
+      left, right, options);
+}
+
+/// Returns true if left numeric scalar is at most right numeric scalar
+/// \param[in] left a NumericScalar
+/// \param[in] right a NumericScalar
+/// \param[in] options comparison options
+template <typename T>
+bool ScalarIsAtMost(const NumericScalar<T>& left, const NumericScalar<T>& 
right,
+                    const OrderOptions& options = OrderOptions::Defaults()) {
+  return internal::NumericScalarCompare<NumericScalar<T>, 
internal::IsAtMostOp>(
+      left, right, options);
+}
+
+/// Returns true if left numeric scalar is more than right numeric scalar
+/// \param[in] left a NumericScalar
+/// \param[in] right a NumericScalar
+/// \param[in] options comparison options
+template <typename T>
+bool ScalarMoreThan(const NumericScalar<T>& left, const NumericScalar<T>& 
right,

Review Comment:
   Similarly, I usually see this called "Greater" or "GreaterThan", not 
"MoreThan".



##########
cpp/src/arrow/compare.cc:
##########
@@ -1336,4 +1336,52 @@ bool TypeEquals(const DataType& left, const DataType& 
right, bool check_metadata
   }
 }
 
+#define ARROW_CMP_TYPE_CASE(id, T, Op)                           \
+  case Type::id:                                                 \
+    return internal::NumericScalarCompare<NumericScalar<T>, Op>( \
+        static_cast<const NumericScalar<T>&>(left),              \
+        static_cast<const NumericScalar<T>&>(right), options);
+
+#define ARROW_CMP_FUNCTION_BODY(Op)                                    \
+  if (left.type != right.type) {                                       \
+    return Status::Invalid("comparing unequal types is unsupported");  \
+  }                                                                    \
+  switch (left.type->id()) {                                           \
+    ARROW_CMP_TYPE_CASE(INT8, Int8Type, Op)                            \
+    ARROW_CMP_TYPE_CASE(INT16, Int16Type, Op)                          \
+    ARROW_CMP_TYPE_CASE(INT32, Int32Type, Op)                          \
+    ARROW_CMP_TYPE_CASE(INT64, Int64Type, Op)                          \
+    ARROW_CMP_TYPE_CASE(UINT8, UInt8Type, Op)                          \
+    ARROW_CMP_TYPE_CASE(UINT16, UInt16Type, Op)                        \
+    ARROW_CMP_TYPE_CASE(UINT32, UInt32Type, Op)                        \
+    ARROW_CMP_TYPE_CASE(UINT64, UInt64Type, Op)                        \
+    ARROW_CMP_TYPE_CASE(FLOAT, FloatType, Op)                          \
+    ARROW_CMP_TYPE_CASE(DOUBLE, DoubleType, Op)                        \
+    default:                                                           \
+      return Status::Invalid(left.type, " comparison is unsupported"); \

Review Comment:
   Should probably also be `TypeError`.



##########
cpp/src/arrow/compare.cc:
##########
@@ -1336,4 +1336,52 @@ bool TypeEquals(const DataType& left, const DataType& 
right, bool check_metadata
   }
 }
 
+#define ARROW_CMP_TYPE_CASE(id, T, Op)                           \
+  case Type::id:                                                 \
+    return internal::NumericScalarCompare<NumericScalar<T>, Op>( \
+        static_cast<const NumericScalar<T>&>(left),              \
+        static_cast<const NumericScalar<T>&>(right), options);

Review Comment:
   Should use `checked_cast` so that debug builds actually check the type 
downcast runtime (`checked_cast` is turned into `dynamic_cast` in debug mode 
and `static_cast` in release mode).



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to