boolean remove(Object value, Codec codec, RedisConnection conn) {
    while (true) {
      conn.sync(RedisCommands.WATCH, getName());
      BinarySearchResult<V> res = binarySearch((V) value, codec, conn);
      if (res.getIndex() < 0) {
        conn.sync(RedisCommands.UNWATCH);
        return false;
      }

      if (res.getIndex() == 0) {
        conn.sync(RedisCommands.MULTI);
        conn.sync(codec, RedisCommands.LPOP, getName());
        if (((List<Object>) conn.sync(codec, RedisCommands.EXEC)).size() == 1) {
          return true;
        }
      }

      List<Object> tail =
          conn.sync(codec, RedisCommands.LRANGE, getName(), res.getIndex() + 1, size());
      conn.sync(RedisCommands.MULTI);
      conn.sync(RedisCommands.LTRIM, getName(), 0, res.getIndex() - 1);
      if (tail.isEmpty()) {
        if (((List<Object>) conn.sync(codec, RedisCommands.EXEC)).size() == 1) {
          return true;
        }
      } else {
        tail.add(0, getName());
        conn.sync(codec, RedisCommands.RPUSH, tail.toArray());
        if (((List<Object>) conn.sync(codec, RedisCommands.EXEC)).size() == 2) {
          return true;
        }
      }
    }
  }
  /**
   * Binary search algorithm
   *
   * @param value
   * @param connection
   * @param lowerIndex
   * @param upperIndex
   * @return
   */
  private BinarySearchResult<V> binarySearch(
      V value, Codec codec, RedisConnection connection, int lowerIndex, int upperIndex) {
    while (lowerIndex <= upperIndex) {
      int index = lowerIndex + (upperIndex - lowerIndex) / 2;

      V res = getAtIndex(codec, index, connection);
      int cmp = comparator.compare(value, res);

      if (cmp == 0) {
        BinarySearchResult<V> indexRes = new BinarySearchResult<V>();
        indexRes.setIndex(index);
        return indexRes;
      } else if (cmp < 0) {
        upperIndex = index - 1;
      } else {
        lowerIndex = index + 1;
      }
    }

    BinarySearchResult<V> indexRes = new BinarySearchResult<V>();
    indexRes.setIndex(-(lowerIndex + 1));
    return indexRes;
  }
  boolean add(V value, Codec codec, RedisConnection connection) {
    while (true) {
      connection.sync(RedisCommands.WATCH, getName(), getComparatorKeyName());

      checkComparator(connection);

      Long version = getCurrentVersion(codec, connection);
      BinarySearchResult<V> res = binarySearch(value, codec, connection);
      if (res.getIndex() < 0) {
        //                System.out.println("index: " + res.getIndex() + " value: " + value);
        if (!version.equals(getCurrentVersion(codec, connection))) {
          connection.sync(RedisCommands.UNWATCH);
          continue;
        }
        //                NewScore newScore = calcNewScore(res.getIndex(), connection);
        //                if (!version.equals(getCurrentVersion(simpleConnection))) {
        //                    connection.unwatch();
        //                    continue;
        //                }
        //
        //                String leftScoreKey = getScoreKeyName(newScore.getLeftScore());
        //                String rightScoreKey = getScoreKeyName(newScore.getRightScore());
        //
        //                if (simpleConnection.setnx(leftScoreKey, 1)) {
        //                    if (!version.equals(getCurrentVersion(simpleConnection))) {
        //                        connection.unwatch();
        //
        //                        connection.del(leftScoreKey);
        //                        continue;
        //                    }
        //                    if (rightScoreKey != null) {
        //
        //                        if (!simpleConnection.setnx(rightScoreKey, 1)) {
        //                            connection.unwatch();
        //
        //                            connection.del(leftScoreKey);
        //                            continue;
        //                        }
        //                    }
        //                } else {
        //                    connection.unwatch();
        //                    continue;
        //                }

        V pivot = null;
        boolean before = false;
        int index = -(res.getIndex() + 1);

        if (index < size()) {
          before = true;
          pivot = connection.sync(codec, RedisCommands.LINDEX, getName(), index);
        }

        connection.sync(RedisCommands.MULTI);
        if (index >= size()) {
          connection.sync(codec, RedisCommands.RPUSH, getName(), value);
        } else {
          connection.sync(
              codec, RedisCommands.LINSERT, getName(), before ? "BEFORE" : "AFTER", pivot, value);
        }
        //                System.out.println("adding: " + newScore.getScore() + " " + value);
        //                connection.zadd(getName(), newScore.getScore(), value);
        //                if (rightScoreKey != null) {
        //                    connection.del(leftScoreKey, rightScoreKey);
        //                } else {
        //                    connection.del(leftScoreKey);
        //                }
        connection.sync(RedisCommands.INCR, getCurrentVersionKey());
        List<Object> re = connection.sync(codec, RedisCommands.EXEC);
        if (re.size() == 2) {
          //                    System.out.println("index: " + index + " value: " + value + " pivot:
          // " + pivot);
          return true;
          //                    Number val = (Number) re.get(0);
          //                    Long delCount = (Long) re.get(1);
          //                    if (rightScoreKey != null) {
          //                        if (delCount != 2) {
          //                            throw new IllegalStateException();
          //                        }
          //                    } else {
          //                        if (delCount != 1) {
          //                            throw new IllegalStateException();
          //                        }
          //                    }
          //                    return val != null && val.intValue() > 0;
        }
      } else {
        connection.sync(RedisCommands.UNWATCH);
        return false;
      }
    }
  }