Node ceilingNode(double key) {
   for (; ; ) {
     Node b = skipListMap.findGrandPredecessor(key, refCntCallback);
     if (b == null) b = head;
     if (b != head && key <= b.last()) {
       return b;
     }
     assert b == head || b.last() < key;
     Node n = safeNext(b);
     release(b);
     if (n == null) {
       return null;
     } else if (key <= n.last()) {
       return n;
     }
     Node f = safeNext(n);
     release(n);
     if (f == null) {
       return null;
     } else {
       if (isInconsistentNextNode(f, key)) {
         release(f);
         continue;
       }
       return f;
     }
   }
 }
 static Node safeNext(Node b) {
   while (true) {
     Node n = b.next;
     if (n == null) return null;
     n.incRefCnt();
     if (n == b.next) return n;
     else release(n);
   }
 }
    int findBeginIndex(Node n, double key, boolean inclusive) {
      boolean exclusive = !inclusive;
      if (key <= n.first()) {
        if (key == n.first() && exclusive) return 1;
        return 0;
      }
      int pos = n.findKeyIndex(key);
      if (pos < 0) pos = -(pos + 1);
      else if (!inclusive) pos++;

      return pos;
    }
    // concurrent read/write access is allowed
    boolean appendNewAtomic(double key, Object value, ConcurrentDoubleOrderedListMap orderedMap) {
      synchronized (this) {
        if (isMarked()) return false;
        if (next != null) return false;

        Node n = Node.alloc();
        n.put(key, value, orderedMap);
        // assert n.len()==1;
        n.next = null;
        boolean success = casNext(null, n);
        assert success;
        return true;
      }
    }
 private boolean emptySlotInNextTwo() {
   if (next == null) return false;
   if (!next.isFull()) return true;
   if (next.next == null) return false;
   if (!next.next.isFull()) return true;
   return false;
 }
    // concurrent read/write access is allowed
    Object putAtomic(
        double key,
        Object value,
        Node b,
        boolean onlyIfAbsent,
        ConcurrentDoubleOrderedListMap orderedMap) {
      synchronized (b) {
        if (b.isMarked()) return Retry;
        synchronized (this) {
          if (isMarked()) return Retry;

          int len = len();
          int pos = Arrays.binarySearch(keys, 0, len, key);
          if (pos >= 0) {
            Object old = vals[pos];
            if (onlyIfAbsent) {
              if (old == null) {
                vals[pos] = value;
                return null;
              } else {
                return old;
              }
            }
            vals[pos] = value;
            return old;
          }
          pos = -(pos + 1);
          putAtomicReally(b, pos, key, value, orderedMap);
          return null;
        }
      }
    }
 private boolean isInconsistentNextNode(Node f, double key) {
   /**
    * With skipListMap.findGrandPredecessor, we expect the key of the target node (next of next
    * node of returned node) to be greater than or equal to the given key. If not, there is
    * inconsistency between skipListMap and this ordered list map.
    *
    * @see Node#putAtomicReally for the cause of this inconsistency
    */
   return f.next != null && f.first() < key;
 }
 public void cancel(Node n) {
   // It's possible (although very unlikely) that this release operation sets n.refcnt to 0,
   // in which case n.refcnt could have been incremented from 0 by found(Node n) above.
   // Hence, n.refcnt can be decremented to 0 two times; so even when n.refcnt is 0,
   // we don't decrement n.next.refcnt immediately since it can double-decrement
   // n.next.refcnt.
   // We decrement n.next.refcnt in Node.alloc(), where we know that only a single thread is
   // accessing the node.
   Node.release(n);
 }
 static Node alloc() {
   final int threshold = 2;
   int tryCnt = 0;
   while (true) {
     tryCnt++;
     Node n = pool.poll();
     if (n == null) {
       return new Node();
     } else {
       if (n.getRefCnt() > 0) {
         pool.add(n);
         if (tryCnt <= threshold) continue;
         else return new Node();
       } else {
         if (n.next != null) n.next.decRefCnt();
         n.init();
         return n;
       }
     }
   }
 }
 public void clear() {
   for (Node n = head.next; n != null; n = n.next) {
     Node.release(n);
     assert n.getRefCnt() == 0;
     n.clearEntry();
     Node.free(n);
   }
   head.next = null;
 }
    int findEndIndex(Node n, double key, boolean inclusive) {
      boolean exclusive = !inclusive;
      if (key < n.first()) return -1;
      if (key >= n.last()) {
        if (key == n.last() && exclusive) return n.len() - 2;
        return n.len() - 1;
      }
      int pos = n.findKeyIndex(key);
      if (pos < 0) pos = -(pos + 1);
      else if (!inclusive) pos--;

      if (pos == n.len()) pos--;
      return pos;
    }
 public void doSanityCheck() {
   int count = 0;
   double prevKey = Double.MIN_VALUE;
   Node n = safeNext(head);
   while (n != null) {
     count++;
     if (!skipListMap.containsKey(n.first())) {
       System.out.println("Node n not in skiplistMap:" + n.first());
       n._print();
     }
     if (n.first() < prevKey) {
       System.out.println("Error in ordering, prevKey:" + prevKey + ", n:" + n);
     }
     prevKey = n.first();
     Node next = safeNext(n);
     release(n);
     n = next;
   }
   if (skipListMap.size() != count) {
     System.out.println("skipListMap.size:" + skipListMap.size() + ", count:" + count);
   }
 }
 /*
  * Methods for fetching next pointer of a node, preventing the node from being reclaimed by the pool.
  * release() simply decrements the reference count of the node.
  */
 Node safeNext(Node b) {
   return Node.safeNext(b);
 }
 public void found(Node n) {
   n.incRefCnt();
 }
 void release(Node b, Node n) {
   Node.release(n);
   Node.release(b);
 }
    // only used by putAtomic and PutAtomicIfAbsent. Inside synchronized(b) and synchronized(this).
    private void putAtomicReally(
        Node b, int pos, double key, Object value, ConcurrentDoubleOrderedListMap orderedMap) {
      int len = len();
      if (len + 1 <= keys.length) {
        if (pos == len) { // in-place append in the current node
          keys[pos] = key;
          vals[pos] = value;
          length = len + 1;
          if (pos == 0) {
            orderedMap.skipListMap.put(keys[0], this);
          }
        } else { // copied to a new node, replacing the current node
          mark();
          Node n = Node.alloc();
          n.next = this.next;
          if (next != null) next.incRefCnt();
          double[] nkeys = n.keys;
          Object[] nvals = n.vals;
          n.length = len + 1;
          nodeCopy(keys, vals, 0, nkeys, nvals, 0, pos);
          nkeys[pos] = key;
          nvals[pos] = value;
          nodeCopy(keys, vals, pos, nkeys, nvals, pos + 1, len - pos);

          orderedMap.skipListMap.put(nkeys[0], n);
          b.casNext(this, n); // should always succeed.
          if (pos == 0) {
            orderedMap.skipListMap.remove(keys[0], this);
          }
          release(this);
          free(this);
        }
      } else { // requires 2 new nodes, to replace the current node
        mark();
        Node n1 = Node.alloc();
        double[] n1keys = n1.keys;
        Object[] n1vals = n1.vals;
        Node n2 = Node.alloc();
        double[] n2keys = n2.keys;
        Object[] n2vals = n2.vals;
        int l1 = len / 2, l2 = len - l1;
        if (pos < l1) { // key, value stored in n1
          n1.length = l1 + 1;
          n2.length = l2;

          nodeCopy(keys, vals, 0, n1keys, n1vals, 0, pos);
          n1keys[pos] = key;
          n1vals[pos] = value;
          nodeCopy(keys, vals, pos, n1keys, n1vals, pos + 1, l1 - pos);
          nodeCopy(keys, vals, l1, n2keys, n2vals, 0, l2);

          n1.next = n2;
          n2.next = this.next;
          if (next != null) next.incRefCnt();

          orderedMap.skipListMap.put(n1keys[0], n1);
          orderedMap.skipListMap.put(n2keys[0], n2);
          b.casNext(this, n1); // should always succeed.
          if (pos == 0) {
            orderedMap.skipListMap.remove(keys[0], this);
          }
          release(this);
          free(this);
        } else { // key,value is stored in n2
          n1.length = l1;
          n2.length = l2 + 1;
          int newpos = pos - l1;

          nodeCopy(keys, vals, 0, n1keys, n1vals, 0, l1);

          nodeCopy(keys, vals, l1, n2keys, n2vals, 0, newpos);
          n2keys[newpos] = key;
          n2vals[newpos] = value;
          nodeCopy(keys, vals, pos, n2keys, n2vals, newpos + 1, l2 - newpos);

          n1.next = n2;
          n2.next = this.next;
          if (next != null) next.incRefCnt();

          orderedMap.skipListMap.put(n1keys[0], n1);
          orderedMap.skipListMap.put(n2keys[0], n2);
          b.casNext(this, n1); // should always succeed.
          release(this);
          free(this);
        }
      }
    }
 void release(Node b, Node n, Node f) {
   Node.release(f);
   Node.release(n);
   Node.release(b);
 }
 public void _print() {
   for (Node n = head; n != null; n = n.next) {
     n._print();
   }
 }
    // no concurrent read/write access is assumed
    private void putReally(
        int pos, double key, Object value, ConcurrentDoubleOrderedListMap orderedMap) {
      int len = len();
      if (len + 1 <= keys.length) { // inserted in the current node
        nodeCopy(keys, vals, pos, keys, vals, pos + 1, len - pos);
        keys[pos] = key;
        vals[pos] = value;
        length = len + 1;
        if (pos == 0) {
          orderedMap.skipListMap.put(keys[0], this);
          if (len != 0) orderedMap.skipListMap.remove(keys[1], this);
        }
      } else if (emptySlotInNextTwo()) {
        if (pos == len) {
          next.put(key, value, orderedMap);
          return;
        }
        next.put(keys[len - 1], vals[len - 1], orderedMap);
        nodeCopy(keys, vals, pos, keys, vals, pos + 1, len - pos - 1);
        keys[pos] = key;
        vals[pos] = value;
        if (pos == 0) {
          orderedMap.skipListMap.remove(keys[1], this);
          orderedMap.skipListMap.put(keys[0], this);
        }
      } else { // current node is full, so requires a new node
        Node n = Node.alloc();
        double[] nkeys = n.keys;
        Object[] nvals = n.vals;
        int l1 = len / 2, l2 = len - l1;
        if (next == null && pos == len) { // this is the last node, simply add to the new node.
          nkeys[0] = key;
          nvals[0] = value;
          n.length = 1;
          orderedMap.skipListMap.put(nkeys[0], n);
        } else if (pos < l1) { // key,value is stored in the current node
          length = l1 + 1;
          n.length = l2;
          nodeCopy(keys, vals, l1, nkeys, nvals, 0, l2);

          nodeCopy(keys, vals, pos, keys, vals, pos + 1, l1 - pos);
          keys[pos] = key;
          vals[pos] = value;
          if (pos == 0) {
            orderedMap.skipListMap.remove(keys[1]);
            orderedMap.skipListMap.put(keys[0], this);
          }
          orderedMap.skipListMap.put(nkeys[0], n);
        } else { // key,value is stored in the new node
          length = l1;
          n.length = l2 + 1;
          int newpos = pos - l1;

          nodeCopy(keys, vals, l1, nkeys, nvals, 0, newpos);
          nkeys[newpos] = key;
          nvals[newpos] = value;
          nodeCopy(keys, vals, pos, nkeys, nvals, newpos + 1, l2 - newpos);

          orderedMap.skipListMap.put(nkeys[0], n);
        }
        n.next = this.next;
        this.next = n;
      }
    }
 static void release(Node n) {
   n.decRefCnt();
 }
 void release(Node n) {
   Node.release(n);
 }
  V putAtomic(double key, V value, boolean onlyIfAbsent) {
    if (value == null) throw new NullPointerException();

    for (; ; ) {
      Node b = skipListMap.findGrandPredecessor(key, refCntCallback);
      if (b == null) b = head;
      if (b != head && b.first() <= key && key <= b.last()) {
        release(b);
        continue;
      }
      assert b == head || b.last() < key;
      Node n = safeNext(b);
      if (n == null) {
        if (b.appendNewAtomic(key, value, this)) {
          release(b);
          return null;
        } else {
          release(b);
          continue;
        }
      } else if (n.first() <= key && key <= n.last()) {
        Object val = n.putAtomic(key, value, b, onlyIfAbsent, this);
        release(b, n);
        if (val == Retry) continue;
        return (V) val;
      }
      Node f = safeNext(n);
      if (f == null) {
        Object val = n.putAtomic(key, value, b, onlyIfAbsent, this);
        release(b, n);
        if (val == Retry) continue;
        return (V) val;
      } else {
        if (f.first() == key) {
          Object val = f.putAtomic(key, value, n, onlyIfAbsent, this);
          release(b, n, f);
          if (val == Retry) continue;
          return (V) val;
        } else {
          if (key > f.first()) { // inconsistent read, retry
            release(b, n, f);
            continue;
          }
          Object val = n.putAtomic(key, value, b, onlyIfAbsent, this);
          release(b, n, f);
          if (val == Retry) continue;
          return (V) val;
        }
      }
    }
  }
 // no concurrent read/write is assumed
 public V put(double key, V value) {
   if (value == null) throw new NullPointerException();
   // not incrementing b's refcnt, so need not release it.
   Node b = skipListMap.findGrandPredecessor(key);
   if (b == null) b = head;
   Node n = b.next;
   if (n == null) {
     n = Node.alloc();
     n.put(key, value, this);
     n.next = null;
     b.next = n;
     return null;
   }
   Node f = n.next;
   if (f == null) { // put (key,value) into node n
     Object val = n.put(key, value, this);
     return (V) val;
   } else {
     assert f.len() > 0 && f.first() >= key;
     if (f.first() == key) {
       Object val = f.put(key, value, this);
       return (V) val;
     } else {
       Object val = n.put(key, value, this);
       return (V) val;
     }
   }
 }
 V doReplace(double key, V old, V value) {
   for (; ; ) {
     Node b = skipListMap.findGrandPredecessor(key, refCntCallback);
     if (b == null) b = head;
     if (b != head && b.first() <= key && key <= b.last()) {
       // can happen if there's inconsistency between skipListMap and orderedMap(this).
       // The inconsistency can happen from Node.putAtomicReally(),
       // where it adds to, and removes from skiplistMap.
       Object val = b.replace(key, old, value);
       release(b);
       if (val == Retry) continue;
       return (V) val;
     }
     assert b == head || b.last() < key;
     Node n = safeNext(b);
     release(b);
     if (n == null) {
       return null;
     } else if (n.first() <= key && key <= n.last()) {
       Object val = n.replace(key, old, value);
       release(n);
       if (val == Retry) continue;
       return (V) val;
     }
     Node f = safeNext(n);
     release(n);
     if (f == null) {
       return null;
     } else {
       if (isInconsistentNextNode(f, key)) {
         release(f);
         continue;
       }
       Object val = f.replace(key, old, value);
       release(f);
       if (val == Retry) continue;
       return (V) val;
     }
   }
 }