LindaSummer commented on code in PR #3296:
URL: https://github.com/apache/kvrocks/pull/3296#discussion_r2623580076


##########
src/types/redis_tdigest.h:
##########
@@ -81,6 +81,10 @@ class TDigest : public SubKeyScanner {
                        std::vector<int>& result);
   rocksdb::Status RevRank(engine::Context& ctx, const Slice& digest_name, 
const std::vector<double>& inputs,
                           std::vector<int>& result);
+  rocksdb::Status ByRevRank(engine::Context& ctx, const Slice& digest_name, 
const std::vector<int>& inputs,

Review Comment:
   This is an optional advice, we'd better keep same style in this file and 
make an output value in parameter a pointer.



##########
tests/gocase/unit/type/tdigest/tdigest_test.go:
##########
@@ -716,4 +747,231 @@ func tdigestTests(t *testing.T, configs 
util.KvrocksServerConfigs) {
                        require.EqualValues(t, expected[i], rank, "REVRANK 
mismatch at index %d", i)
                }
        })
+
+       t.Run("tdigest.byrank and tdigest.byrevrank on empty sketch", func(t 
*testing.T) {
+               key := "tdigest_byrank_on_empty_sketch"
+               require.NoError(t, rdb.Do(ctx, "TDIGEST.CREATE", key, 
"compression", "100").Err())
+
+               // Test BYRANK on empty sketch
+               rsp := rdb.Do(ctx, "TDIGEST.BYRANK", key, "1", "2", "4", "5", 
"0", "1", "20")
+               require.NoError(t, rsp.Err())
+               vals, err := rsp.Slice()
+               require.NoError(t, err)
+               require.Len(t, vals, 7)
+               isRESP3 := configs["resp3-enabled"] == "yes"
+               if isRESP3 {
+                       for i, v := range vals {
+                               rank, ok := v.(float64)
+                               require.True(t, ok, "expected float64 but got 
%T at index %d", v, i)
+                               require.True(t, math.IsNaN(rank), "expected NaN 
but got %v at index %d", rank, i)
+                       }
+               } else {
+                       expected := []string{"nan", "nan", "nan", "nan", "nan", 
"nan", "nan"}
+                       for i, v := range vals {
+                               rank, ok := v.(string)
+                               require.True(t, ok, "expected string but got %T 
at index %d", v, i)
+                               require.EqualValues(t, expected[i], rank, "RANK 
mismatch at index %d", i)
+                       }
+               }
+
+               // Test BYREVRANK on empty sketch
+               rsp = rdb.Do(ctx, "TDIGEST.BYREVRANK", key, "1", "2", "4", "5", 
"0", "1", "20")
+               require.NoError(t, rsp.Err())
+               vals, err = rsp.Slice()
+               require.NoError(t, err)
+               require.Len(t, vals, 7)
+               if isRESP3 {
+                       for i, v := range vals {
+                               rank, ok := v.(float64)
+                               require.True(t, ok, "expected float64 but got 
%T at index %d", v, i)
+                               require.True(t, math.IsNaN(rank), "expected NaN 
but got %v at index %d", rank, i)
+                       }
+               } else {
+                       expected := []string{"nan", "nan", "nan", "nan", "nan", 
"nan", "nan"}
+                       for i, v := range vals {
+                               rank, ok := v.(string)
+                               require.True(t, ok, "expected string but got %T 
at index %d", v, i)
+                               require.EqualValues(t, expected[i], rank, 
"REVRANK mismatch at index %d", i)
+                       }
+               }
+       })
+
+       t.Run("tdigest.byrank and tdigest.byrevrank on non-empty sketch", 
func(t *testing.T) {
+               key := "tdigest_byrank_non_empty"
+               require.NoError(t, rdb.Do(ctx, "TDIGEST.CREATE", key, 
"compression", "100").Err())
+               require.NoError(t, rdb.Do(ctx, "TDIGEST.ADD", key, "1", "2", 
"3", "4", "5").Err())
+               isRESP3 := configs["resp3-enabled"] == "yes"
+
+               rsp := rdb.Do(ctx, "TDIGEST.BYRANK", key, "0", "1", "2", "3", 
"4", "5", "6")
+               require.NoError(t, rsp.Err())
+               vals, err := rsp.Slice()
+               require.NoError(t, err)
+               require.Len(t, vals, 7)
+               if isRESP3 {
+                       // Expected: rank 0 -> 1, rank 1 -> 2, rank 2 -> 3, 
rank 3 -> 4, rank 4 -> 5
+                       // Expected: rank 5, 6 -> out of range (+inf)
+                       expected := []float64{1, 2, 3, 4, 5, math.Inf(1), 
math.Inf(1)}
+                       for i, v := range vals {
+                               rank, ok := v.(float64)
+                               require.True(t, ok, "expected float64 but got 
%T at index %d", v, i)
+                               if math.IsInf(expected[i], 1) {
+                                       require.True(t, math.IsInf(rank, 1), 
"expected +Inf but got %v at index %d", rank, i)
+                               } else {
+                                       require.InDelta(t, expected[i], rank, 
0.1, "BYRANK mismatch at index %d", i)
+                               }
+                       }
+               } else {
+                       expectedStrings := []string{"1", "2", "3", "4", "5", 
"inf", "inf"}
+                       for i, v := range vals {
+                               rank, ok := v.(string)
+                               require.True(t, ok, "expected string but got %T 
at index %d", v, i)
+                               require.Equal(t, expectedStrings[i], rank, 
"BYRANK mismatch at index %d", i)
+                       }
+               }
+
+               rsp = rdb.Do(ctx, "TDIGEST.BYREVRANK", key, "0", "1", "2", "3", 
"4", "5", "6")
+               require.NoError(t, rsp.Err())
+               vals, err = rsp.Slice()
+               require.NoError(t, err)
+               require.Len(t, vals, 7)
+
+               if isRESP3 {
+                       // Expected: rank 0 -> 5, rank 1 -> 4, rank 2 -> 3, 
rank 3 -> 2, rank 4 -> 1
+                       // Expected: rank 5, 6 -> out of range (-inf)
+                       expected := []float64{5, 4, 3, 2, 1, math.Inf(-1), 
math.Inf(-1)}
+                       for i, v := range vals {
+                               rank, ok := v.(float64)
+                               require.True(t, ok, "expected float64 but got 
%T at index %d", v, i)
+                               if math.IsInf(expected[i], -1) {
+                                       require.True(t, math.IsInf(rank, -1), 
"expected -Inf but got %v at index %d", rank, i)
+                               } else {
+                                       require.InDelta(t, expected[i], rank, 
0.1, "BYREVRANK mismatch at index %d", i)
+                               }
+                       }
+               } else {
+                       expectedStrings := []string{"5", "4", "3", "2", "1", 
"-inf", "-inf"}
+                       for i, v := range vals {
+                               rank, ok := v.(string)
+                               require.True(t, ok, "expected string but got %T 
at index %d", v, i)
+                               require.Equal(t, expectedStrings[i], rank, 
"BYREVRANK mismatch at index %d", i)
+                       }
+               }
+       })
+
+       t.Run("tdigest.byrank and tdigest.byrevrank with duplicate values", 
func(t *testing.T) {
+               key := "tdigest_byrank_duplicates"
+               require.NoError(t, rdb.Do(ctx, "TDIGEST.CREATE", key, 
"compression", "100").Err())
+               require.NoError(t, rdb.Do(ctx, "TDIGEST.ADD", key, "1", "2", 
"2", "3", "3", "3").Err())
+               isRESP3 := configs["resp3-enabled"] == "yes"
+
+               rsp := rdb.Do(ctx, "TDIGEST.BYRANK", key, "0", "1", "2", "3", 
"4", "5", "6", "7")
+               require.NoError(t, rsp.Err())
+               vals, err := rsp.Slice()
+               require.NoError(t, err)
+               require.Len(t, vals, 8)
+               // test BYRANK with duplicate values
+               if isRESP3 {
+                       for i, v := range vals {
+                               rank, ok := v.(float64)
+                               require.True(t, ok, "expected float64 but got 
%T at index %d", v, i)
+                               expected := []float64{1, 2, 2, 3, 3, 3}
+                               if i < 6 {
+                                       require.InDelta(t, expected[i], rank, 
0.1, "BYRANK mismatch at index %d", i)
+                               } else {
+                                       require.True(t, math.IsInf(rank, 1), 
"rank %d should be +Inf, got %v", i, rank)
+                               }
+                       }
+               } else {
+                       expectedStrings := []string{"1", "2", "2", "3", "3", 
"3", "inf", "inf"}
+                       for i, v := range vals {
+                               rank, ok := v.(string)
+                               require.True(t, ok, "expected string but got %T 
at index %d", v, i)
+                               require.Equal(t, expectedStrings[i], rank, 
"BYRANK mismatch at index %d", i)
+                       }
+               }
+
+               // test BYREVRANK with duplicate values
+               rsp = rdb.Do(ctx, "TDIGEST.BYREVRANK", key, "0", "1", "2", "3", 
"4", "5", "6", "7")
+               require.NoError(t, rsp.Err())
+               vals, err = rsp.Slice()
+               require.NoError(t, err)
+               require.Len(t, vals, 8)
+               if isRESP3 {
+                       for i, v := range vals {
+                               rank, ok := v.(float64)
+                               require.True(t, ok, "expected float64 but got 
%T at index %d", v, i)
+                               expected := []float64{3, 3, 3, 2, 2, 1}

Review Comment:
   Same as above.



##########
src/commands/cmd_tdigest.cc:
##########
@@ -238,6 +238,76 @@ class CommandTDigestRevRank : public 
TDigestRankCommand<true> {};
 
 class CommandTDigestRank : public TDigestRankCommand<false> {};
 
+template <bool Reverse>
+class TDigestByRankCommand : public Commander {
+ public:
+  Status Parse(const std::vector<std::string> &args) override {
+    key_name_ = args[1];
+
+    std::set<std::string> unique_inputs_set(args.begin() + 2, args.end());
+    origin_inputs_.assign(args.begin() + 2, args.end());
+
+    unique_inputs_.reserve(unique_inputs_set.size());
+    size_t i = 0;
+    for (const auto &input : unique_inputs_set) {
+      auto value = ParseInt<int>(input);
+      if (!value) {
+        return {Status::RedisParseErr, errValueNotInteger};
+      }
+      if (*value < 0) {
+        return {Status::InvalidArgument, errInvalidRankValue};
+      }
+      unique_inputs_.push_back(*value);
+      unique_inputs_order_[input] = i;
+      ++i;
+    }
+    return Status::OK();
+  }
+  Status Execute(engine::Context &ctx, Server *srv, Connection *conn, 
std::string *output) override {
+    TDigest tdigest(srv->storage, conn->GetNamespace());
+    std::vector<double> result;
+    result.reserve(origin_inputs_.size());
+
+    if (const auto s =
+            [&]() {
+              if constexpr (Reverse) {
+                return tdigest.ByRevRank(ctx, key_name_, unique_inputs_, 
result);
+              } else {
+                return tdigest.ByRank(ctx, key_name_, unique_inputs_, result);
+              }
+            }();
+        !s.ok()) {
+      if (s.IsNotFound()) {
+        return {Status::RedisExecErr, errKeyNotFound};
+      }
+      return {Status::RedisExecErr, s.ToString()};
+    }
+
+    std::vector<std::string> ranks;
+    ranks.reserve(origin_inputs_.size());
+    auto is_resp3 = conn->GetProtocolVersion() == RESP::v3;
+    for (const auto &v : origin_inputs_) {
+      if (is_resp3) {
+        ranks.push_back(redis::Double(redis::RESP::v3, 
result[unique_inputs_order_[v]]));
+      } else {
+        ranks.push_back(redis::BulkString(fmt::format("{}", 
result[unique_inputs_order_[v]])));
+      }
+    }

Review Comment:
   ```suggestion
       for (const auto &v : origin_inputs_) {
         auto rank_value = result[unique_inputs_order_[v]];
         if (is_resp3) {
           ranks.push_back(redis::Double(redis::RESP::v3, rank_value));
         } else {
           ranks.push_back(redis::BulkString(fmt::format("{}", rank_value)));
         }
       }
   ```
   
   Maybe this way could be cleaner.



##########
tests/gocase/unit/type/tdigest/tdigest_test.go:
##########
@@ -75,6 +101,11 @@ func TestTDigest(t *testing.T) {
                        Options:    []string{"yes", "no"},
                        ConfigType: util.YesNo,
                },
+               {

Review Comment:
   Do we need this for all tdigest test cases or just for this command?
   If we need to enable for all commands, maybe a new PR will be better.



##########
tests/gocase/unit/type/tdigest/tdigest_test.go:
##########
@@ -716,4 +747,231 @@ func tdigestTests(t *testing.T, configs 
util.KvrocksServerConfigs) {
                        require.EqualValues(t, expected[i], rank, "REVRANK 
mismatch at index %d", i)
                }
        })
+
+       t.Run("tdigest.byrank and tdigest.byrevrank on empty sketch", func(t 
*testing.T) {
+               key := "tdigest_byrank_on_empty_sketch"
+               require.NoError(t, rdb.Do(ctx, "TDIGEST.CREATE", key, 
"compression", "100").Err())
+
+               // Test BYRANK on empty sketch
+               rsp := rdb.Do(ctx, "TDIGEST.BYRANK", key, "1", "2", "4", "5", 
"0", "1", "20")
+               require.NoError(t, rsp.Err())
+               vals, err := rsp.Slice()
+               require.NoError(t, err)
+               require.Len(t, vals, 7)
+               isRESP3 := configs["resp3-enabled"] == "yes"
+               if isRESP3 {
+                       for i, v := range vals {
+                               rank, ok := v.(float64)
+                               require.True(t, ok, "expected float64 but got 
%T at index %d", v, i)
+                               require.True(t, math.IsNaN(rank), "expected NaN 
but got %v at index %d", rank, i)
+                       }
+               } else {
+                       expected := []string{"nan", "nan", "nan", "nan", "nan", 
"nan", "nan"}
+                       for i, v := range vals {
+                               rank, ok := v.(string)
+                               require.True(t, ok, "expected string but got %T 
at index %d", v, i)
+                               require.EqualValues(t, expected[i], rank, "RANK 
mismatch at index %d", i)
+                       }
+               }
+
+               // Test BYREVRANK on empty sketch
+               rsp = rdb.Do(ctx, "TDIGEST.BYREVRANK", key, "1", "2", "4", "5", 
"0", "1", "20")
+               require.NoError(t, rsp.Err())
+               vals, err = rsp.Slice()
+               require.NoError(t, err)
+               require.Len(t, vals, 7)
+               if isRESP3 {
+                       for i, v := range vals {
+                               rank, ok := v.(float64)
+                               require.True(t, ok, "expected float64 but got 
%T at index %d", v, i)
+                               require.True(t, math.IsNaN(rank), "expected NaN 
but got %v at index %d", rank, i)
+                       }
+               } else {
+                       expected := []string{"nan", "nan", "nan", "nan", "nan", 
"nan", "nan"}
+                       for i, v := range vals {
+                               rank, ok := v.(string)
+                               require.True(t, ok, "expected string but got %T 
at index %d", v, i)
+                               require.EqualValues(t, expected[i], rank, 
"REVRANK mismatch at index %d", i)
+                       }
+               }
+       })
+
+       t.Run("tdigest.byrank and tdigest.byrevrank on non-empty sketch", 
func(t *testing.T) {
+               key := "tdigest_byrank_non_empty"
+               require.NoError(t, rdb.Do(ctx, "TDIGEST.CREATE", key, 
"compression", "100").Err())
+               require.NoError(t, rdb.Do(ctx, "TDIGEST.ADD", key, "1", "2", 
"3", "4", "5").Err())
+               isRESP3 := configs["resp3-enabled"] == "yes"
+
+               rsp := rdb.Do(ctx, "TDIGEST.BYRANK", key, "0", "1", "2", "3", 
"4", "5", "6")
+               require.NoError(t, rsp.Err())
+               vals, err := rsp.Slice()
+               require.NoError(t, err)
+               require.Len(t, vals, 7)
+               if isRESP3 {
+                       // Expected: rank 0 -> 1, rank 1 -> 2, rank 2 -> 3, 
rank 3 -> 4, rank 4 -> 5
+                       // Expected: rank 5, 6 -> out of range (+inf)
+                       expected := []float64{1, 2, 3, 4, 5, math.Inf(1), 
math.Inf(1)}
+                       for i, v := range vals {
+                               rank, ok := v.(float64)
+                               require.True(t, ok, "expected float64 but got 
%T at index %d", v, i)
+                               if math.IsInf(expected[i], 1) {
+                                       require.True(t, math.IsInf(rank, 1), 
"expected +Inf but got %v at index %d", rank, i)
+                               } else {
+                                       require.InDelta(t, expected[i], rank, 
0.1, "BYRANK mismatch at index %d", i)
+                               }
+                       }
+               } else {
+                       expectedStrings := []string{"1", "2", "3", "4", "5", 
"inf", "inf"}
+                       for i, v := range vals {
+                               rank, ok := v.(string)
+                               require.True(t, ok, "expected string but got %T 
at index %d", v, i)
+                               require.Equal(t, expectedStrings[i], rank, 
"BYRANK mismatch at index %d", i)
+                       }
+               }
+
+               rsp = rdb.Do(ctx, "TDIGEST.BYREVRANK", key, "0", "1", "2", "3", 
"4", "5", "6")
+               require.NoError(t, rsp.Err())
+               vals, err = rsp.Slice()
+               require.NoError(t, err)
+               require.Len(t, vals, 7)
+
+               if isRESP3 {
+                       // Expected: rank 0 -> 5, rank 1 -> 4, rank 2 -> 3, 
rank 3 -> 2, rank 4 -> 1
+                       // Expected: rank 5, 6 -> out of range (-inf)
+                       expected := []float64{5, 4, 3, 2, 1, math.Inf(-1), 
math.Inf(-1)}
+                       for i, v := range vals {
+                               rank, ok := v.(float64)
+                               require.True(t, ok, "expected float64 but got 
%T at index %d", v, i)
+                               if math.IsInf(expected[i], -1) {
+                                       require.True(t, math.IsInf(rank, -1), 
"expected -Inf but got %v at index %d", rank, i)
+                               } else {
+                                       require.InDelta(t, expected[i], rank, 
0.1, "BYREVRANK mismatch at index %d", i)
+                               }
+                       }
+               } else {
+                       expectedStrings := []string{"5", "4", "3", "2", "1", 
"-inf", "-inf"}
+                       for i, v := range vals {
+                               rank, ok := v.(string)
+                               require.True(t, ok, "expected string but got %T 
at index %d", v, i)
+                               require.Equal(t, expectedStrings[i], rank, 
"BYREVRANK mismatch at index %d", i)
+                       }
+               }
+       })
+
+       t.Run("tdigest.byrank and tdigest.byrevrank with duplicate values", 
func(t *testing.T) {
+               key := "tdigest_byrank_duplicates"
+               require.NoError(t, rdb.Do(ctx, "TDIGEST.CREATE", key, 
"compression", "100").Err())
+               require.NoError(t, rdb.Do(ctx, "TDIGEST.ADD", key, "1", "2", 
"2", "3", "3", "3").Err())
+               isRESP3 := configs["resp3-enabled"] == "yes"
+
+               rsp := rdb.Do(ctx, "TDIGEST.BYRANK", key, "0", "1", "2", "3", 
"4", "5", "6", "7")
+               require.NoError(t, rsp.Err())
+               vals, err := rsp.Slice()
+               require.NoError(t, err)
+               require.Len(t, vals, 8)
+               // test BYRANK with duplicate values
+               if isRESP3 {
+                       for i, v := range vals {
+                               rank, ok := v.(float64)
+                               require.True(t, ok, "expected float64 but got 
%T at index %d", v, i)
+                               expected := []float64{1, 2, 2, 3, 3, 3}

Review Comment:
   It should be out of the for loop.



##########
tests/cppunit/types/tdigest_test.cc:
##########
@@ -448,4 +448,79 @@ TEST_F(RedisTDigestTest, 
RevRank_and_Rank_on_different_or_same_and_unordered_inp
     EXPECT_EQ(got, expect_result_revrank[i]);
   }
   ASSERT_TRUE(status.ok()) << status.ToString();
+}
+
+TEST_F(RedisTDigestTest, ByRank_And_ByRevRank) {
+  std::string test_digest_name = "test_digest_byrank_and_byrevrank" + 
std::to_string(util::GetTimeStampMS());
+  bool exists = false;
+  auto status = tdigest_->Create(*ctx_, test_digest_name, {100}, &exists);
+  ASSERT_FALSE(exists);
+  ASSERT_TRUE(status.ok());
+
+  // Test 1: Empty TDigest should return NaN
+  std::vector<double> result;
+  std::vector<int> value = {1, 2};
+  result.reserve(value.size());
+  status = tdigest_->ByRank(*ctx_, test_digest_name, value, result);
+  for (size_t i = 0; i < result.size(); i++) {
+    EXPECT_TRUE(std::isnan(result[i])) << "Expected NaN at index " << i << ", 
got " << result[i];
+  }
+  ASSERT_TRUE(status.ok()) << status.ToString();
+
+  result.clear();
+  result.reserve(value.size());
+  status = tdigest_->ByRevRank(*ctx_, test_digest_name, value, result);
+  for (size_t i = 0; i < result.size(); i++) {
+    EXPECT_TRUE(std::isnan(result[i])) << "Expected NaN at index " << i << ", 
got " << result[i];
+  }
+  ASSERT_TRUE(status.ok()) << status.ToString();
+
+  // Test 2: Add values and test ByRank
+  // Add values: 1 2 2 3 3 3 4 4 4 4 5 5 5 5 5 (15 values)
+  std::vector<double> values = {1, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 5};
+  status = tdigest_->Add(*ctx_, test_digest_name, values);
+  ASSERT_TRUE(status.ok()) << status.ToString();
+
+  // Test ByRank: rank 0 should be min, increasing ranks should give 
increasing values
+  std::vector<int> ranks = {0, 1, 2, 3, 6, 9, 10, 14, 15};
+  std::vector<double> expected_values = {
+      1.0, 2.0, 2.0, 3.0, 4.0, 4.0, 5.0, 5.0, 
std::numeric_limits<double>::infinity()};
+  result.clear();
+  status = tdigest_->ByRank(*ctx_, test_digest_name, ranks, result);
+  ASSERT_TRUE(status.ok()) << status.ToString();
+  ASSERT_EQ(result.size(), ranks.size());
+
+  for (size_t i = 0; i < result.size(); i++) {
+    if (std::isinf(expected_values[i])) {
+      EXPECT_TRUE(std::isinf(result[i])) << "Expected inf at rank " << 
ranks[i] << ", got " << result[i];
+    } else {
+      EXPECT_DOUBLE_EQ(result[i], expected_values[i])
+          << "ByRank mismatch at rank " << ranks[i] << ": expected " << 
expected_values[i] << ", got " << result[i];
+    }
+  }
+
+  // Test ByRevRank: rank 0 should be max, increasing ranks should give 
decreasing values
+  std::vector<double> expected_revvalues = {
+      5.0, 5.0, 5.0, 5.0, 4.0, 3.0, 3.0, 1.0, 
-std::numeric_limits<double>::infinity()};
+  result.clear();
+  status = tdigest_->ByRevRank(*ctx_, test_digest_name, ranks, result);
+  ASSERT_TRUE(status.ok()) << status.ToString();
+  ASSERT_EQ(result.size(), ranks.size());
+
+  for (size_t i = 0; i < result.size(); i++) {
+    if (std::isinf(expected_revvalues[i])) {
+      EXPECT_TRUE(std::isinf(result[i])) << "Expected inf at revrank " << 
ranks[i] << ", got " << result[i];
+    } else {
+      EXPECT_DOUBLE_EQ(result[i], expected_revvalues[i]) << "ByRevRank 
mismatch at rank " << ranks[i] << ": expected "
+                                                         << 
expected_revvalues[i] << ", got " << result[i];
+    }
+  }
+
+  // Test 3: Test boundary conditions
+  std::vector<int> boundary_ranks = {0, 7, 14, 100};
+  result.clear();
+  status = tdigest_->ByRank(*ctx_, test_digest_name, boundary_ranks, result);
+  ASSERT_TRUE(status.ok()) << status.ToString();
+  EXPECT_EQ(result[0], 1.0) << "Rank 0 should be minimum";
+  EXPECT_TRUE(std::isinf(result[3])) << "Rank >= total_weight should be 
infinity";
 }

Review Comment:
   ```suggestion
   }
   
   ```



##########
tests/gocase/unit/type/tdigest/tdigest_test.go:
##########
@@ -716,4 +747,231 @@ func tdigestTests(t *testing.T, configs 
util.KvrocksServerConfigs) {
                        require.EqualValues(t, expected[i], rank, "REVRANK 
mismatch at index %d", i)
                }
        })
+
+       t.Run("tdigest.byrank and tdigest.byrevrank on empty sketch", func(t 
*testing.T) {
+               key := "tdigest_byrank_on_empty_sketch"
+               require.NoError(t, rdb.Do(ctx, "TDIGEST.CREATE", key, 
"compression", "100").Err())
+
+               // Test BYRANK on empty sketch
+               rsp := rdb.Do(ctx, "TDIGEST.BYRANK", key, "1", "2", "4", "5", 
"0", "1", "20")
+               require.NoError(t, rsp.Err())
+               vals, err := rsp.Slice()
+               require.NoError(t, err)
+               require.Len(t, vals, 7)
+               isRESP3 := configs["resp3-enabled"] == "yes"
+               if isRESP3 {
+                       for i, v := range vals {
+                               rank, ok := v.(float64)
+                               require.True(t, ok, "expected float64 but got 
%T at index %d", v, i)
+                               require.True(t, math.IsNaN(rank), "expected NaN 
but got %v at index %d", rank, i)
+                       }
+               } else {
+                       expected := []string{"nan", "nan", "nan", "nan", "nan", 
"nan", "nan"}
+                       for i, v := range vals {
+                               rank, ok := v.(string)
+                               require.True(t, ok, "expected string but got %T 
at index %d", v, i)
+                               require.EqualValues(t, expected[i], rank, "RANK 
mismatch at index %d", i)
+                       }
+               }
+
+               // Test BYREVRANK on empty sketch
+               rsp = rdb.Do(ctx, "TDIGEST.BYREVRANK", key, "1", "2", "4", "5", 
"0", "1", "20")
+               require.NoError(t, rsp.Err())
+               vals, err = rsp.Slice()
+               require.NoError(t, err)
+               require.Len(t, vals, 7)
+               if isRESP3 {
+                       for i, v := range vals {
+                               rank, ok := v.(float64)
+                               require.True(t, ok, "expected float64 but got 
%T at index %d", v, i)
+                               require.True(t, math.IsNaN(rank), "expected NaN 
but got %v at index %d", rank, i)
+                       }
+               } else {
+                       expected := []string{"nan", "nan", "nan", "nan", "nan", 
"nan", "nan"}
+                       for i, v := range vals {
+                               rank, ok := v.(string)
+                               require.True(t, ok, "expected string but got %T 
at index %d", v, i)
+                               require.EqualValues(t, expected[i], rank, 
"REVRANK mismatch at index %d", i)
+                       }
+               }
+       })
+
+       t.Run("tdigest.byrank and tdigest.byrevrank on non-empty sketch", 
func(t *testing.T) {
+               key := "tdigest_byrank_non_empty"
+               require.NoError(t, rdb.Do(ctx, "TDIGEST.CREATE", key, 
"compression", "100").Err())
+               require.NoError(t, rdb.Do(ctx, "TDIGEST.ADD", key, "1", "2", 
"3", "4", "5").Err())
+               isRESP3 := configs["resp3-enabled"] == "yes"
+
+               rsp := rdb.Do(ctx, "TDIGEST.BYRANK", key, "0", "1", "2", "3", 
"4", "5", "6")
+               require.NoError(t, rsp.Err())
+               vals, err := rsp.Slice()
+               require.NoError(t, err)
+               require.Len(t, vals, 7)
+               if isRESP3 {
+                       // Expected: rank 0 -> 1, rank 1 -> 2, rank 2 -> 3, 
rank 3 -> 4, rank 4 -> 5
+                       // Expected: rank 5, 6 -> out of range (+inf)
+                       expected := []float64{1, 2, 3, 4, 5, math.Inf(1), 
math.Inf(1)}
+                       for i, v := range vals {
+                               rank, ok := v.(float64)
+                               require.True(t, ok, "expected float64 but got 
%T at index %d", v, i)
+                               if math.IsInf(expected[i], 1) {
+                                       require.True(t, math.IsInf(rank, 1), 
"expected +Inf but got %v at index %d", rank, i)
+                               } else {
+                                       require.InDelta(t, expected[i], rank, 
0.1, "BYRANK mismatch at index %d", i)
+                               }
+                       }
+               } else {
+                       expectedStrings := []string{"1", "2", "3", "4", "5", 
"inf", "inf"}
+                       for i, v := range vals {
+                               rank, ok := v.(string)
+                               require.True(t, ok, "expected string but got %T 
at index %d", v, i)
+                               require.Equal(t, expectedStrings[i], rank, 
"BYRANK mismatch at index %d", i)
+                       }
+               }
+
+               rsp = rdb.Do(ctx, "TDIGEST.BYREVRANK", key, "0", "1", "2", "3", 
"4", "5", "6")
+               require.NoError(t, rsp.Err())
+               vals, err = rsp.Slice()
+               require.NoError(t, err)
+               require.Len(t, vals, 7)
+
+               if isRESP3 {
+                       // Expected: rank 0 -> 5, rank 1 -> 4, rank 2 -> 3, 
rank 3 -> 2, rank 4 -> 1
+                       // Expected: rank 5, 6 -> out of range (-inf)
+                       expected := []float64{5, 4, 3, 2, 1, math.Inf(-1), 
math.Inf(-1)}
+                       for i, v := range vals {
+                               rank, ok := v.(float64)
+                               require.True(t, ok, "expected float64 but got 
%T at index %d", v, i)
+                               if math.IsInf(expected[i], -1) {
+                                       require.True(t, math.IsInf(rank, -1), 
"expected -Inf but got %v at index %d", rank, i)
+                               } else {
+                                       require.InDelta(t, expected[i], rank, 
0.1, "BYREVRANK mismatch at index %d", i)
+                               }
+                       }
+               } else {
+                       expectedStrings := []string{"5", "4", "3", "2", "1", 
"-inf", "-inf"}
+                       for i, v := range vals {
+                               rank, ok := v.(string)
+                               require.True(t, ok, "expected string but got %T 
at index %d", v, i)
+                               require.Equal(t, expectedStrings[i], rank, 
"BYREVRANK mismatch at index %d", i)
+                       }
+               }
+       })
+
+       t.Run("tdigest.byrank and tdigest.byrevrank with duplicate values", 
func(t *testing.T) {
+               key := "tdigest_byrank_duplicates"
+               require.NoError(t, rdb.Do(ctx, "TDIGEST.CREATE", key, 
"compression", "100").Err())
+               require.NoError(t, rdb.Do(ctx, "TDIGEST.ADD", key, "1", "2", 
"2", "3", "3", "3").Err())
+               isRESP3 := configs["resp3-enabled"] == "yes"
+
+               rsp := rdb.Do(ctx, "TDIGEST.BYRANK", key, "0", "1", "2", "3", 
"4", "5", "6", "7")
+               require.NoError(t, rsp.Err())
+               vals, err := rsp.Slice()
+               require.NoError(t, err)
+               require.Len(t, vals, 8)
+               // test BYRANK with duplicate values
+               if isRESP3 {
+                       for i, v := range vals {
+                               rank, ok := v.(float64)
+                               require.True(t, ok, "expected float64 but got 
%T at index %d", v, i)
+                               expected := []float64{1, 2, 2, 3, 3, 3}
+                               if i < 6 {
+                                       require.InDelta(t, expected[i], rank, 
0.1, "BYRANK mismatch at index %d", i)
+                               } else {
+                                       require.True(t, math.IsInf(rank, 1), 
"rank %d should be +Inf, got %v", i, rank)
+                               }
+                       }
+               } else {
+                       expectedStrings := []string{"1", "2", "2", "3", "3", 
"3", "inf", "inf"}
+                       for i, v := range vals {
+                               rank, ok := v.(string)
+                               require.True(t, ok, "expected string but got %T 
at index %d", v, i)
+                               require.Equal(t, expectedStrings[i], rank, 
"BYRANK mismatch at index %d", i)
+                       }
+               }
+
+               // test BYREVRANK with duplicate values
+               rsp = rdb.Do(ctx, "TDIGEST.BYREVRANK", key, "0", "1", "2", "3", 
"4", "5", "6", "7")
+               require.NoError(t, rsp.Err())
+               vals, err = rsp.Slice()
+               require.NoError(t, err)
+               require.Len(t, vals, 8)
+               if isRESP3 {
+                       for i, v := range vals {
+                               rank, ok := v.(float64)
+                               require.True(t, ok, "expected float64 but got 
%T at index %d", v, i)
+                               expected := []float64{3, 3, 3, 2, 2, 1}
+                               if i < 6 {
+                                       require.InDelta(t, expected[i], rank, 
0.1, "BYREVRANK mismatch at index %d", i)
+                               } else {
+                                       require.True(t, math.IsInf(rank, -1), 
"rank %d should be -Inf, got %v", i, rank)
+                               }
+                       }
+               } else {
+                       expectedStrings := []string{"3", "3", "3", "2", "2", 
"1", "-inf", "-inf"}
+                       for i, v := range vals {
+                               rank, ok := v.(string)
+                               require.True(t, ok, "expected string but got %T 
at index %d", v, i)
+                               require.Equal(t, expectedStrings[i], rank, 
"BYREVRANK mismatch at index %d", i)
+                       }
+               }
+       })
+
+       t.Run("tdigest.byrank and tdigest.byrevrank with unordered duplicate 
values", func(t *testing.T) {
+               key := "tdigest_byrank_unordered_dup_"
+               require.NoError(t, rdb.Do(ctx, "TDIGEST.CREATE", key, 
"compression", "100").Err())
+               require.NoError(t, rdb.Do(ctx, "TDIGEST.ADD", key, "12", "100", 
"50", "36", "75", "81", "35.5", "46", "36", "8.8", "15", "4", "32.5", "12", 
"8.8", "7", "99", "1").Err())
+               isRESP3 := configs["resp3-enabled"] == "yes"
+
+               rsp := rdb.Do(ctx, "TDIGEST.BYRANK", key, "0", "1", "2", "3", 
"4", "5", "6", "7", "7", "5", "20", "100")
+               require.NoError(t, rsp.Err())
+               vals, err := rsp.Slice()
+               require.NoError(t, err)
+               require.Len(t, vals, 12)
+               if isRESP3 {
+                       for i, v := range vals {
+                               rank, ok := v.(float64)
+                               require.True(t, ok, "expected float64 but got 
%T at index %d", v, i)
+                               expected := []float64{1, 4, 7, 8.8, 8.8, 12, 
12, 15, 15, 12}

Review Comment:
   Same as above.



##########
tests/gocase/unit/type/tdigest/tdigest_test.go:
##########
@@ -716,4 +747,231 @@ func tdigestTests(t *testing.T, configs 
util.KvrocksServerConfigs) {
                        require.EqualValues(t, expected[i], rank, "REVRANK 
mismatch at index %d", i)
                }
        })
+
+       t.Run("tdigest.byrank and tdigest.byrevrank on empty sketch", func(t 
*testing.T) {
+               key := "tdigest_byrank_on_empty_sketch"
+               require.NoError(t, rdb.Do(ctx, "TDIGEST.CREATE", key, 
"compression", "100").Err())
+
+               // Test BYRANK on empty sketch
+               rsp := rdb.Do(ctx, "TDIGEST.BYRANK", key, "1", "2", "4", "5", 
"0", "1", "20")
+               require.NoError(t, rsp.Err())
+               vals, err := rsp.Slice()
+               require.NoError(t, err)
+               require.Len(t, vals, 7)
+               isRESP3 := configs["resp3-enabled"] == "yes"
+               if isRESP3 {
+                       for i, v := range vals {
+                               rank, ok := v.(float64)
+                               require.True(t, ok, "expected float64 but got 
%T at index %d", v, i)
+                               require.True(t, math.IsNaN(rank), "expected NaN 
but got %v at index %d", rank, i)
+                       }
+               } else {
+                       expected := []string{"nan", "nan", "nan", "nan", "nan", 
"nan", "nan"}
+                       for i, v := range vals {
+                               rank, ok := v.(string)
+                               require.True(t, ok, "expected string but got %T 
at index %d", v, i)
+                               require.EqualValues(t, expected[i], rank, "RANK 
mismatch at index %d", i)
+                       }
+               }
+
+               // Test BYREVRANK on empty sketch
+               rsp = rdb.Do(ctx, "TDIGEST.BYREVRANK", key, "1", "2", "4", "5", 
"0", "1", "20")
+               require.NoError(t, rsp.Err())
+               vals, err = rsp.Slice()
+               require.NoError(t, err)
+               require.Len(t, vals, 7)
+               if isRESP3 {
+                       for i, v := range vals {
+                               rank, ok := v.(float64)
+                               require.True(t, ok, "expected float64 but got 
%T at index %d", v, i)
+                               require.True(t, math.IsNaN(rank), "expected NaN 
but got %v at index %d", rank, i)
+                       }
+               } else {
+                       expected := []string{"nan", "nan", "nan", "nan", "nan", 
"nan", "nan"}
+                       for i, v := range vals {
+                               rank, ok := v.(string)
+                               require.True(t, ok, "expected string but got %T 
at index %d", v, i)
+                               require.EqualValues(t, expected[i], rank, 
"REVRANK mismatch at index %d", i)
+                       }
+               }
+       })
+
+       t.Run("tdigest.byrank and tdigest.byrevrank on non-empty sketch", 
func(t *testing.T) {
+               key := "tdigest_byrank_non_empty"
+               require.NoError(t, rdb.Do(ctx, "TDIGEST.CREATE", key, 
"compression", "100").Err())
+               require.NoError(t, rdb.Do(ctx, "TDIGEST.ADD", key, "1", "2", 
"3", "4", "5").Err())
+               isRESP3 := configs["resp3-enabled"] == "yes"
+
+               rsp := rdb.Do(ctx, "TDIGEST.BYRANK", key, "0", "1", "2", "3", 
"4", "5", "6")
+               require.NoError(t, rsp.Err())
+               vals, err := rsp.Slice()
+               require.NoError(t, err)
+               require.Len(t, vals, 7)
+               if isRESP3 {
+                       // Expected: rank 0 -> 1, rank 1 -> 2, rank 2 -> 3, 
rank 3 -> 4, rank 4 -> 5
+                       // Expected: rank 5, 6 -> out of range (+inf)
+                       expected := []float64{1, 2, 3, 4, 5, math.Inf(1), 
math.Inf(1)}
+                       for i, v := range vals {
+                               rank, ok := v.(float64)
+                               require.True(t, ok, "expected float64 but got 
%T at index %d", v, i)
+                               if math.IsInf(expected[i], 1) {
+                                       require.True(t, math.IsInf(rank, 1), 
"expected +Inf but got %v at index %d", rank, i)
+                               } else {
+                                       require.InDelta(t, expected[i], rank, 
0.1, "BYRANK mismatch at index %d", i)
+                               }
+                       }
+               } else {
+                       expectedStrings := []string{"1", "2", "3", "4", "5", 
"inf", "inf"}
+                       for i, v := range vals {
+                               rank, ok := v.(string)
+                               require.True(t, ok, "expected string but got %T 
at index %d", v, i)
+                               require.Equal(t, expectedStrings[i], rank, 
"BYRANK mismatch at index %d", i)
+                       }
+               }
+
+               rsp = rdb.Do(ctx, "TDIGEST.BYREVRANK", key, "0", "1", "2", "3", 
"4", "5", "6")
+               require.NoError(t, rsp.Err())
+               vals, err = rsp.Slice()
+               require.NoError(t, err)
+               require.Len(t, vals, 7)
+
+               if isRESP3 {
+                       // Expected: rank 0 -> 5, rank 1 -> 4, rank 2 -> 3, 
rank 3 -> 2, rank 4 -> 1
+                       // Expected: rank 5, 6 -> out of range (-inf)
+                       expected := []float64{5, 4, 3, 2, 1, math.Inf(-1), 
math.Inf(-1)}
+                       for i, v := range vals {
+                               rank, ok := v.(float64)
+                               require.True(t, ok, "expected float64 but got 
%T at index %d", v, i)
+                               if math.IsInf(expected[i], -1) {
+                                       require.True(t, math.IsInf(rank, -1), 
"expected -Inf but got %v at index %d", rank, i)
+                               } else {
+                                       require.InDelta(t, expected[i], rank, 
0.1, "BYREVRANK mismatch at index %d", i)
+                               }
+                       }
+               } else {
+                       expectedStrings := []string{"5", "4", "3", "2", "1", 
"-inf", "-inf"}
+                       for i, v := range vals {
+                               rank, ok := v.(string)
+                               require.True(t, ok, "expected string but got %T 
at index %d", v, i)
+                               require.Equal(t, expectedStrings[i], rank, 
"BYREVRANK mismatch at index %d", i)
+                       }
+               }
+       })
+
+       t.Run("tdigest.byrank and tdigest.byrevrank with duplicate values", 
func(t *testing.T) {
+               key := "tdigest_byrank_duplicates"
+               require.NoError(t, rdb.Do(ctx, "TDIGEST.CREATE", key, 
"compression", "100").Err())
+               require.NoError(t, rdb.Do(ctx, "TDIGEST.ADD", key, "1", "2", 
"2", "3", "3", "3").Err())
+               isRESP3 := configs["resp3-enabled"] == "yes"
+
+               rsp := rdb.Do(ctx, "TDIGEST.BYRANK", key, "0", "1", "2", "3", 
"4", "5", "6", "7")
+               require.NoError(t, rsp.Err())
+               vals, err := rsp.Slice()
+               require.NoError(t, err)
+               require.Len(t, vals, 8)
+               // test BYRANK with duplicate values
+               if isRESP3 {
+                       for i, v := range vals {
+                               rank, ok := v.(float64)
+                               require.True(t, ok, "expected float64 but got 
%T at index %d", v, i)
+                               expected := []float64{1, 2, 2, 3, 3, 3}
+                               if i < 6 {
+                                       require.InDelta(t, expected[i], rank, 
0.1, "BYRANK mismatch at index %d", i)
+                               } else {
+                                       require.True(t, math.IsInf(rank, 1), 
"rank %d should be +Inf, got %v", i, rank)
+                               }
+                       }
+               } else {
+                       expectedStrings := []string{"1", "2", "2", "3", "3", 
"3", "inf", "inf"}
+                       for i, v := range vals {
+                               rank, ok := v.(string)
+                               require.True(t, ok, "expected string but got %T 
at index %d", v, i)
+                               require.Equal(t, expectedStrings[i], rank, 
"BYRANK mismatch at index %d", i)
+                       }
+               }
+
+               // test BYREVRANK with duplicate values
+               rsp = rdb.Do(ctx, "TDIGEST.BYREVRANK", key, "0", "1", "2", "3", 
"4", "5", "6", "7")
+               require.NoError(t, rsp.Err())
+               vals, err = rsp.Slice()
+               require.NoError(t, err)
+               require.Len(t, vals, 8)
+               if isRESP3 {
+                       for i, v := range vals {
+                               rank, ok := v.(float64)
+                               require.True(t, ok, "expected float64 but got 
%T at index %d", v, i)
+                               expected := []float64{3, 3, 3, 2, 2, 1}
+                               if i < 6 {
+                                       require.InDelta(t, expected[i], rank, 
0.1, "BYREVRANK mismatch at index %d", i)
+                               } else {
+                                       require.True(t, math.IsInf(rank, -1), 
"rank %d should be -Inf, got %v", i, rank)
+                               }
+                       }
+               } else {
+                       expectedStrings := []string{"3", "3", "3", "2", "2", 
"1", "-inf", "-inf"}
+                       for i, v := range vals {
+                               rank, ok := v.(string)
+                               require.True(t, ok, "expected string but got %T 
at index %d", v, i)
+                               require.Equal(t, expectedStrings[i], rank, 
"BYREVRANK mismatch at index %d", i)
+                       }
+               }
+       })
+
+       t.Run("tdigest.byrank and tdigest.byrevrank with unordered duplicate 
values", func(t *testing.T) {
+               key := "tdigest_byrank_unordered_dup_"
+               require.NoError(t, rdb.Do(ctx, "TDIGEST.CREATE", key, 
"compression", "100").Err())
+               require.NoError(t, rdb.Do(ctx, "TDIGEST.ADD", key, "12", "100", 
"50", "36", "75", "81", "35.5", "46", "36", "8.8", "15", "4", "32.5", "12", 
"8.8", "7", "99", "1").Err())
+               isRESP3 := configs["resp3-enabled"] == "yes"
+
+               rsp := rdb.Do(ctx, "TDIGEST.BYRANK", key, "0", "1", "2", "3", 
"4", "5", "6", "7", "7", "5", "20", "100")
+               require.NoError(t, rsp.Err())
+               vals, err := rsp.Slice()
+               require.NoError(t, err)
+               require.Len(t, vals, 12)
+               if isRESP3 {
+                       for i, v := range vals {
+                               rank, ok := v.(float64)
+                               require.True(t, ok, "expected float64 but got 
%T at index %d", v, i)
+                               expected := []float64{1, 4, 7, 8.8, 8.8, 12, 
12, 15, 15, 12}
+                               if i < 10 {
+                                       require.InDelta(t, expected[i], rank, 
0.1, "BYRANK mismatch at index %d", i)
+                               } else {
+                                       require.True(t, math.IsInf(rank, 1), 
"rank %d should be +Inf, got %v", i, rank)
+                               }
+                       }
+               } else {
+                       expectedStrings := []string{"1", "4", "7", "8.8", 
"8.8", "12", "12", "15", "15", "12", "inf", "inf"}
+                       for i, v := range vals {
+                               rank, ok := v.(string)
+                               require.True(t, ok, "expected string but got %T 
at index %d", v, i)
+                               require.Equal(t, expectedStrings[i], rank, 
"BYRANK mismatch at index %d", i)
+                       }
+               }
+
+               // test BYREVRANK with duplicate values
+               rsp = rdb.Do(ctx, "TDIGEST.BYREVRANK", key, "20", "75", "0", 
"1", "2", "3", "4", "5", "6", "7")
+               require.NoError(t, rsp.Err())
+               vals, err = rsp.Slice()
+               require.NoError(t, err)
+               require.Len(t, vals, 10)
+               if isRESP3 {
+                       for i, v := range vals {
+                               rank, ok := v.(float64)
+                               require.True(t, ok, "expected float64 but got 
%T at index %d", v, i)
+                               expected := []float64{0, 0, 100, 99, 81, 75, 
50, 46, 36, 36}

Review Comment:
   Same as above.



##########
tests/gocase/unit/type/tdigest/tdigest_test.go:
##########
@@ -716,4 +747,231 @@ func tdigestTests(t *testing.T, configs 
util.KvrocksServerConfigs) {
                        require.EqualValues(t, expected[i], rank, "REVRANK 
mismatch at index %d", i)
                }
        })
+
+       t.Run("tdigest.byrank and tdigest.byrevrank on empty sketch", func(t 
*testing.T) {
+               key := "tdigest_byrank_on_empty_sketch"
+               require.NoError(t, rdb.Do(ctx, "TDIGEST.CREATE", key, 
"compression", "100").Err())
+
+               // Test BYRANK on empty sketch
+               rsp := rdb.Do(ctx, "TDIGEST.BYRANK", key, "1", "2", "4", "5", 
"0", "1", "20")
+               require.NoError(t, rsp.Err())
+               vals, err := rsp.Slice()
+               require.NoError(t, err)
+               require.Len(t, vals, 7)
+               isRESP3 := configs["resp3-enabled"] == "yes"
+               if isRESP3 {
+                       for i, v := range vals {
+                               rank, ok := v.(float64)
+                               require.True(t, ok, "expected float64 but got 
%T at index %d", v, i)
+                               require.True(t, math.IsNaN(rank), "expected NaN 
but got %v at index %d", rank, i)
+                       }
+               } else {
+                       expected := []string{"nan", "nan", "nan", "nan", "nan", 
"nan", "nan"}
+                       for i, v := range vals {
+                               rank, ok := v.(string)
+                               require.True(t, ok, "expected string but got %T 
at index %d", v, i)
+                               require.EqualValues(t, expected[i], rank, "RANK 
mismatch at index %d", i)
+                       }
+               }
+
+               // Test BYREVRANK on empty sketch
+               rsp = rdb.Do(ctx, "TDIGEST.BYREVRANK", key, "1", "2", "4", "5", 
"0", "1", "20")
+               require.NoError(t, rsp.Err())
+               vals, err = rsp.Slice()
+               require.NoError(t, err)
+               require.Len(t, vals, 7)
+               if isRESP3 {
+                       for i, v := range vals {
+                               rank, ok := v.(float64)
+                               require.True(t, ok, "expected float64 but got 
%T at index %d", v, i)
+                               require.True(t, math.IsNaN(rank), "expected NaN 
but got %v at index %d", rank, i)
+                       }
+               } else {
+                       expected := []string{"nan", "nan", "nan", "nan", "nan", 
"nan", "nan"}
+                       for i, v := range vals {
+                               rank, ok := v.(string)
+                               require.True(t, ok, "expected string but got %T 
at index %d", v, i)
+                               require.EqualValues(t, expected[i], rank, 
"REVRANK mismatch at index %d", i)
+                       }
+               }
+       })
+
+       t.Run("tdigest.byrank and tdigest.byrevrank on non-empty sketch", 
func(t *testing.T) {
+               key := "tdigest_byrank_non_empty"
+               require.NoError(t, rdb.Do(ctx, "TDIGEST.CREATE", key, 
"compression", "100").Err())
+               require.NoError(t, rdb.Do(ctx, "TDIGEST.ADD", key, "1", "2", 
"3", "4", "5").Err())
+               isRESP3 := configs["resp3-enabled"] == "yes"
+
+               rsp := rdb.Do(ctx, "TDIGEST.BYRANK", key, "0", "1", "2", "3", 
"4", "5", "6")
+               require.NoError(t, rsp.Err())
+               vals, err := rsp.Slice()
+               require.NoError(t, err)
+               require.Len(t, vals, 7)
+               if isRESP3 {
+                       // Expected: rank 0 -> 1, rank 1 -> 2, rank 2 -> 3, 
rank 3 -> 4, rank 4 -> 5
+                       // Expected: rank 5, 6 -> out of range (+inf)
+                       expected := []float64{1, 2, 3, 4, 5, math.Inf(1), 
math.Inf(1)}
+                       for i, v := range vals {
+                               rank, ok := v.(float64)
+                               require.True(t, ok, "expected float64 but got 
%T at index %d", v, i)
+                               if math.IsInf(expected[i], 1) {
+                                       require.True(t, math.IsInf(rank, 1), 
"expected +Inf but got %v at index %d", rank, i)
+                               } else {
+                                       require.InDelta(t, expected[i], rank, 
0.1, "BYRANK mismatch at index %d", i)
+                               }
+                       }
+               } else {
+                       expectedStrings := []string{"1", "2", "3", "4", "5", 
"inf", "inf"}
+                       for i, v := range vals {
+                               rank, ok := v.(string)
+                               require.True(t, ok, "expected string but got %T 
at index %d", v, i)
+                               require.Equal(t, expectedStrings[i], rank, 
"BYRANK mismatch at index %d", i)
+                       }
+               }
+
+               rsp = rdb.Do(ctx, "TDIGEST.BYREVRANK", key, "0", "1", "2", "3", 
"4", "5", "6")
+               require.NoError(t, rsp.Err())
+               vals, err = rsp.Slice()
+               require.NoError(t, err)
+               require.Len(t, vals, 7)
+
+               if isRESP3 {
+                       // Expected: rank 0 -> 5, rank 1 -> 4, rank 2 -> 3, 
rank 3 -> 2, rank 4 -> 1
+                       // Expected: rank 5, 6 -> out of range (-inf)
+                       expected := []float64{5, 4, 3, 2, 1, math.Inf(-1), 
math.Inf(-1)}
+                       for i, v := range vals {
+                               rank, ok := v.(float64)
+                               require.True(t, ok, "expected float64 but got 
%T at index %d", v, i)
+                               if math.IsInf(expected[i], -1) {
+                                       require.True(t, math.IsInf(rank, -1), 
"expected -Inf but got %v at index %d", rank, i)
+                               } else {
+                                       require.InDelta(t, expected[i], rank, 
0.1, "BYREVRANK mismatch at index %d", i)
+                               }
+                       }
+               } else {
+                       expectedStrings := []string{"5", "4", "3", "2", "1", 
"-inf", "-inf"}
+                       for i, v := range vals {
+                               rank, ok := v.(string)
+                               require.True(t, ok, "expected string but got %T 
at index %d", v, i)
+                               require.Equal(t, expectedStrings[i], rank, 
"BYREVRANK mismatch at index %d", i)
+                       }
+               }
+       })
+
+       t.Run("tdigest.byrank and tdigest.byrevrank with duplicate values", 
func(t *testing.T) {
+               key := "tdigest_byrank_duplicates"
+               require.NoError(t, rdb.Do(ctx, "TDIGEST.CREATE", key, 
"compression", "100").Err())
+               require.NoError(t, rdb.Do(ctx, "TDIGEST.ADD", key, "1", "2", 
"2", "3", "3", "3").Err())
+               isRESP3 := configs["resp3-enabled"] == "yes"
+
+               rsp := rdb.Do(ctx, "TDIGEST.BYRANK", key, "0", "1", "2", "3", 
"4", "5", "6", "7")
+               require.NoError(t, rsp.Err())
+               vals, err := rsp.Slice()
+               require.NoError(t, err)
+               require.Len(t, vals, 8)
+               // test BYRANK with duplicate values
+               if isRESP3 {
+                       for i, v := range vals {
+                               rank, ok := v.(float64)
+                               require.True(t, ok, "expected float64 but got 
%T at index %d", v, i)
+                               expected := []float64{1, 2, 2, 3, 3, 3}
+                               if i < 6 {
+                                       require.InDelta(t, expected[i], rank, 
0.1, "BYRANK mismatch at index %d", i)
+                               } else {
+                                       require.True(t, math.IsInf(rank, 1), 
"rank %d should be +Inf, got %v", i, rank)
+                               }
+                       }
+               } else {
+                       expectedStrings := []string{"1", "2", "2", "3", "3", 
"3", "inf", "inf"}
+                       for i, v := range vals {
+                               rank, ok := v.(string)
+                               require.True(t, ok, "expected string but got %T 
at index %d", v, i)
+                               require.Equal(t, expectedStrings[i], rank, 
"BYRANK mismatch at index %d", i)
+                       }
+               }
+
+               // test BYREVRANK with duplicate values
+               rsp = rdb.Do(ctx, "TDIGEST.BYREVRANK", key, "0", "1", "2", "3", 
"4", "5", "6", "7")
+               require.NoError(t, rsp.Err())
+               vals, err = rsp.Slice()
+               require.NoError(t, err)
+               require.Len(t, vals, 8)
+               if isRESP3 {
+                       for i, v := range vals {
+                               rank, ok := v.(float64)
+                               require.True(t, ok, "expected float64 but got 
%T at index %d", v, i)
+                               expected := []float64{3, 3, 3, 2, 2, 1}
+                               if i < 6 {
+                                       require.InDelta(t, expected[i], rank, 
0.1, "BYREVRANK mismatch at index %d", i)
+                               } else {
+                                       require.True(t, math.IsInf(rank, -1), 
"rank %d should be -Inf, got %v", i, rank)
+                               }
+                       }
+               } else {
+                       expectedStrings := []string{"3", "3", "3", "2", "2", 
"1", "-inf", "-inf"}
+                       for i, v := range vals {
+                               rank, ok := v.(string)
+                               require.True(t, ok, "expected string but got %T 
at index %d", v, i)
+                               require.Equal(t, expectedStrings[i], rank, 
"BYREVRANK mismatch at index %d", i)
+                       }
+               }
+       })
+
+       t.Run("tdigest.byrank and tdigest.byrevrank with unordered duplicate 
values", func(t *testing.T) {
+               key := "tdigest_byrank_unordered_dup_"
+               require.NoError(t, rdb.Do(ctx, "TDIGEST.CREATE", key, 
"compression", "100").Err())
+               require.NoError(t, rdb.Do(ctx, "TDIGEST.ADD", key, "12", "100", 
"50", "36", "75", "81", "35.5", "46", "36", "8.8", "15", "4", "32.5", "12", 
"8.8", "7", "99", "1").Err())
+               isRESP3 := configs["resp3-enabled"] == "yes"
+
+               rsp := rdb.Do(ctx, "TDIGEST.BYRANK", key, "0", "1", "2", "3", 
"4", "5", "6", "7", "7", "5", "20", "100")
+               require.NoError(t, rsp.Err())
+               vals, err := rsp.Slice()
+               require.NoError(t, err)
+               require.Len(t, vals, 12)
+               if isRESP3 {
+                       for i, v := range vals {
+                               rank, ok := v.(float64)
+                               require.True(t, ok, "expected float64 but got 
%T at index %d", v, i)
+                               expected := []float64{1, 4, 7, 8.8, 8.8, 12, 
12, 15, 15, 12}
+                               if i < 10 {
+                                       require.InDelta(t, expected[i], rank, 
0.1, "BYRANK mismatch at index %d", i)
+                               } else {
+                                       require.True(t, math.IsInf(rank, 1), 
"rank %d should be +Inf, got %v", i, rank)
+                               }
+                       }
+               } else {
+                       expectedStrings := []string{"1", "4", "7", "8.8", 
"8.8", "12", "12", "15", "15", "12", "inf", "inf"}
+                       for i, v := range vals {
+                               rank, ok := v.(string)
+                               require.True(t, ok, "expected string but got %T 
at index %d", v, i)
+                               require.Equal(t, expectedStrings[i], rank, 
"BYRANK mismatch at index %d", i)
+                       }
+               }
+
+               // test BYREVRANK with duplicate values
+               rsp = rdb.Do(ctx, "TDIGEST.BYREVRANK", key, "20", "75", "0", 
"1", "2", "3", "4", "5", "6", "7")
+               require.NoError(t, rsp.Err())
+               vals, err = rsp.Slice()
+               require.NoError(t, err)
+               require.Len(t, vals, 10)
+               if isRESP3 {
+                       for i, v := range vals {
+                               rank, ok := v.(float64)
+                               require.True(t, ok, "expected float64 but got 
%T at index %d", v, i)
+                               expected := []float64{0, 0, 100, 99, 81, 75, 
50, 46, 36, 36}

Review Comment:
   In redis lastes docker image I find that the result is below. The last one 
seems a little different.
   ```
   TDIGEST.BYREVRANK test 20 75 0 1 2 3 4 5 6 7
    1) "-inf"
    2) "-inf"
    3) "100"
    4) "99"
    5) "81"
    6) "75"
    7) "50"
    8) "46"
    9) "36"
   10) "32.5"
   ```



-- 
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