package uk.ac.imperial.lsds.seep.gc14.util;
/*
* Copyright (C) 2010 Zhenya Leonov
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*
* CHANGES TO THE ORIGINAL CODE:
*
* This version of the original skiplist has been stripped down to the essentials
* needed to add, remove, and search in a list of float values.
*
*/
import java.io.Serializable;
import java.util.Random;
public final class SkipList implements Serializable{
// Made the list serializable
private static final long serialVersionUID = 1L;
private static final double P = .5;
private static final int MAX_LEVEL = 128;
private transient int size = 0;
private transient int level = 1;
private transient Random random = new Random();
private transient Node head = new Node(-1, MAX_LEVEL);
private transient Node[] update = new Node[MAX_LEVEL];
private transient int[] index = new int[MAX_LEVEL];
private transient Node medianPointer = null;
public SkipList() {
//Reinitialise transient structures
size = 0;
level = 1;
random = new Random();
head = new Node(-1, MAX_LEVEL);
update = new Node[MAX_LEVEL];
index = new int[MAX_LEVEL];
medianPointer = null;
for (int i = 0; i < MAX_LEVEL; i++) {
head.next[i] = head;
head.dist[i] = 1;
}
head.prev = head;
}
public boolean add(float e) {
final int newLevel = randomLevel();
Node x = head;
Node y = head;
int i;
int idx = 0;
for (i = level - 1; i >= 0; i--) {
while (x.next[i] != y
&& x.next[i].element < e) {
idx += x.dist[i];
x = x.next[i];
}
y = x.next[i];
update[i] = x;
index[i] = idx;
}
if (newLevel > level) {
for (i = level; i < newLevel; i++) {
head.dist[i] = size + 1;
update[i] = head;
}
level = newLevel;
}
x = new Node(e, newLevel);
for (i = 0; i < level; i++) {
if (i > newLevel - 1)
update[i].dist[i]++;
else {
x.next[i] = update[i].next[i];
update[i].next[i] = x;
x.dist[i] = index[i] + update[i].dist[i] - idx;
update[i].dist[i] = idx + 1 - index[i];
}
}
x.prev = update[0];
x.next().prev = x;
size++;
if (medianPointer == null)
medianPointer = x;
else {
// inserted left or right of median?
if (e <= medianPointer.element) {
// if inserted left and even number of elements: move pointer left
if (size % 2 == 0)
medianPointer = medianPointer.prev;
}
else {
// if inserted right and uneven number of elements: move pointer right
if (size % 2 == 1)
medianPointer = medianPointer.next();
}
}
return true;
}
public float get(int index) {
return search(index).element;
}
public boolean remove(float element) {
int is = this.size();
Node curr = head;
for (int i = level - 1; i >= 0; i--) {
while (curr.next[i] != head
&& curr.next[i].element < element)
curr = curr.next[i];
update[i] = curr;
}
curr = curr.next();
// System.out.println("curr: "+curr+" == "+head);
// System.out.println("curr: "+curr.element+" != "+element);
if (curr == head || Math.floor(curr.element) != Math.floor(element)){
System.out.println("ERROR");
System.exit(1);
return false;
}
if (size == 1)
medianPointer = null;
else {
if (medianPointer == curr)
medianPointer = curr.prev;
else {
// removed left or right of median?
if (curr.element <= medianPointer.element) {
// if removed left and uneven number of elements: move pointer right
if (size % 2 == 1)
medianPointer = medianPointer.next();
}
else {
// if removed right and even number of elements: move pointer left
if (size % 2 == 1)
medianPointer = medianPointer.prev;
}
}
}
delete(curr, update);
curr = null;
// System.gc();
int es = this.size();
if (es-is == 0)
System.out.println("ERROR");
return true;
}
public float getMedian() {
if (size < 1)
return 0f;
float median = ((size % 2) == 0) ?
(medianPointer.element + medianPointer.next().element)/2f :
medianPointer.element;
return median;
}
public int size() {
return size;
}
private static class Node {
private float element;
private Node prev;
private final Node[] next;
private final int[] dist;
private Node(final float element, final int size) {
this.element = element;
next = new Node[size];
dist = new int[size];
}
private Node next(){
return next[0];
}
}
private int randomLevel() {
int randomLevel = 1;
while (randomLevel < MAX_LEVEL - 1 && random.nextDouble() < P)
randomLevel++;
return randomLevel;
}
private void delete(final Node node, final Node[] update) {
for (int i = 0; i < level; i++)
if (update[i].next[i] == node) {
update[i].next[i] = node.next[i];
update[i].dist[i] += node.dist[i] - 1;
} else
update[i].dist[i]--;
node.next().prev = node.prev;
while (head.next[level - 1] == head && level > 1)
level--;
size--;
}
private Node search(final int index) {
Node curr = head;
int idx = -1;
for (int i = level - 1; i >= 0; i--)
while (idx + curr.dist[i] <= index) {
idx += curr.dist[i];
curr = curr.next[i];
}
return curr;
}
}