static void iterTest() {
    iterateFromTest();

    final ConcurrentDoubleOrderedListMap<String> map = new ConcurrentDoubleOrderedListMap<String>();
    for (int i = -1000; i < 1001; i++) {
      map.put(i, "" + i);
    }

    DoubleObjectIterator<String> iter = map.iterator(11, 20, true, true);
    double sum = 0;
    while (iter.hasNext()) {
      iter.next();
      sum += iter.key();
    }
    assert sum == (11 + 20) * 5 : "sum:" + sum + ", != " + (11 + 20) * 5;

    iter = map.iterator(-1000, 1, true, false);
    sum = 0;
    while (iter.hasNext()) {
      iter.next();
      sum += iter.key();
    }
    assert sum == -1000 * (1000 + 1) / 2;

    iter = map.iterator(-1000, -995, true, true);
    sum = 0;
    while (iter.hasNext()) {
      iter.next();
      sum += iter.key();
    }
    assert sum == -(1000 + 999 + 998 + 997 + 996 + 995);

    iter = map.iteratorFrom(995, true);
    sum = 0;
    while (iter.hasNext()) {
      iter.next();
      sum += iter.key();
    }
    assert sum == (995 + 996 + 997 + 998 + 999 + 1000);

    iter = map.iteratorTo(-995, false);
    sum = 0;
    while (iter.hasNext()) {
      iter.next();
      sum += iter.key();
    }
    assert sum == -(1000 + 999 + 998 + 997 + 996);

    map.doSanityCheck();
    map.clear();
    System.out.println("done");
  }
 DoubleObjectIterator(
     ConcurrentDoubleOrderedListMap map, double lo, double hi, boolean loIncl, boolean hiIncl) {
   low = lo;
   high = hi;
   orderedMap = map;
   lowInclusive = loIncl;
   highInclusive = hiIncl;
   if (low == Double.MIN_VALUE) {
     current = orderedMap.safeNext(orderedMap.head);
   } else {
     current = orderedMap.ceilingNode(low);
   }
   pos = 0;
   if (current == null) {
     len = 0;
   } else {
     pos = findBeginIndex(current, low, lowInclusive);
     len = findEndIndex(current, high, highInclusive) + 1;
   }
 }
  static void iterateFromTest() {
    final ConcurrentDoubleOrderedListMap<String> map1 =
        new ConcurrentDoubleOrderedListMap<String>();
    map1.put(25, "25");
    map1.put(30, "30");
    map1.put(31, "31");
    DoubleObjectIterator<String> it = map1.iteratorFrom(21, false);
    double sum = 0;
    while (it.hasNext()) {
      it.next();
      sum += it.key();
    }
    it.cleanup();
    assert sum == 25 + 30 + 31;

    it = map1.iteratorFrom(25, false);
    sum = 0;
    while (it.hasNext()) {
      it.next();
      sum += it.key();
    }
    it.cleanup();
    assert sum == 30 + 31;

    it = map1.iteratorFrom(25, true);
    sum = 0;
    while (it.hasNext()) {
      it.next();
      sum += it.key();
    }
    it.cleanup();
    assert sum == 25 + 30 + 31;
  }
    public boolean hasNext() {
      if (pos < len) {
        return true;
      } else {
        assert pos == len;
        if (current == null) return false;
        Node next = orderedMap.safeNext(current);
        orderedMap.release(current);
        current = next;
        pos = 0;
        if (current == null) {
          return false;
        }

        len = findEndIndex(current, high, highInclusive) + 1;
        if (len == 0) {
          orderedMap.release(current);
          current = null;
          return false;
        }
        return true;
      }
    }
 public void cleanup() {
   if (current == null) return;
   orderedMap.release(current);
   current = null;
 }
  static void parTest() {
    final int N = 100000;
    final ConcurrentDoubleOrderedListMap<String> map = new ConcurrentDoubleOrderedListMap<String>();
    final double[] keys = new double[N];

    final Random r = new Random();
    for (int i = 0; i < N; i++) {
      int x = r.nextInt();
      keys[i] = x;
      map.put(x, "" + x);
    }
    map.doSanityCheck();
    System.out.println("Start parallel...");

    long s = System.currentTimeMillis();
    for (int i = 0; i < N; i++) {
      map.get(keys[i]);
    }
    long e = System.currentTimeMillis();
    System.out.println("Exec time:" + (e - s) + "ms");

    final int nReader = 8;
    final int nReplacer = 4;
    final int nWriter = 4;
    final int nThread = nReader + nReplacer + nWriter;
    for (int i = 0; i < 5; i++) {
      ParUtil.parallel(
          nThread,
          new ParUtil.CallableBlock() {
            @Override
            public void call(int index) {
              if (index < nReader) {
                for (int j = 0; j < 35; j++) {
                  for (int i = index; i < N; i += nReader) {
                    String val = map.get(keys[i]);
                    assert val != null && val.equals("" + keys[i])
                        : "key:" + keys[i] + ", val:" + val;
                  }
                }
                // System.out.println("Finished reader thread:" + index);
              } else if (index < nReader + nReplacer) {
                for (int j = 0; j < 5; j++) {
                  for (int i = index; i < N; i += nReplacer) {
                    boolean replaced = map.replace(keys[i], "" + keys[i], "" + keys[i]);
                    assert replaced;
                  }
                }
                // System.out.println("Finished writer(replace) thread:" + index);
              } else {
                for (int i = 0; i < 2 * N; i++) {
                  int x = r.nextInt();
                  map.putAtomic(x, "" + x);
                }
                // System.out.println("Finished writer thread:" + index);
              }
            }
          });
      System.out.println("Finished all parallel... i:" + i);
      map.doSanityCheck();
    }
    /*
    System.out.println("Node.pool size:"+Node.pool.size());
    while(true) {
        Node n = Node.pool.poll();
        if (n==null) break;
        System.out.println("n.refcnt:"+n.refcnt);
    }
    System.out.println("fill ratio:"+map._fillRatio());
    */
  }