blob: eedabd3b67ab4ccd4b0271bfc80fd6052c5657a2 [file] [log] [blame]
/*
*
* Copyright (c) 1994, 2009
* Hewlett-Packard Company
*
* Permission to use, copy, modify, distribute and sell this software
* and its documentation for any purpose is hereby granted without fee,
* provided that the above copyright notice appear in all copies and
* that both that copyright notice and this permission notice appear
* in supporting documentation. Hewlett-Packard Company makes no
* representations about the suitability of this software for any
* purpose. It is provided "as is" without express or implied warranty.
*
* Copyright (c) 1997
* Silicon Graphics Computer Systems, Inc.
*
* Permission to use, copy, modify, distribute and sell this software
* and its documentation for any purpose is hereby granted without fee,
* provided that the above copyright notice appear in all copies and
* that both that copyright notice and this permission notice appear
* in supporting documentation. Silicon Graphics makes no
* representations about the suitability of this software for any
* purpose. It is provided "as is" without express or implied warranty.
*
* Contributions:
* IBM - Ported the code to Java
*/
package org.eclipse.ui.internal.views.markers;
import java.util.Arrays;
import java.util.Comparator;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
/**
* @since 3.5
*
* @author Hitesh
*/
public class MarkerSortUtil {
/*
* Note: partial quicksort or introsort would not be of much use here as the
* sorting direction can be reversed easily from the UI. These would perform
* at quadratic complexities in these conditions. The code below is based on
* a variant of Modified HeapSort.Runs in O(NlogN) in worst case.
*/
/*
* Increasing BATCH_SIZE increases memory consumption but increases speed,
* and vice-versa.This indirectly controls the number of active caches in
* MarkerEntry[] array passed for sorting.
*/
private static int BATCH_SIZE = 10000;
/*
* For n/k ratios less than this , we will use Arrays.Sort(). The heapsort
* performs nearly as good as mergesort for small data.We can still benefit
* from the mergesort - Arrays.Sort(). When the number of elements to be sorted,
* are almost as much as the elements we have.
*/
private static float MERGE_OR_HEAP_SWITCH=1.5f;
/**
* Sorts [first,middle] in the array of [first,last] using a variant of
* modified heapsort, such that
* array[first]<array[first+1]<...<array[middle] and
* array[middle]<arra[middle+1||middle+2|| ....last]
*
* @param array
* @param first
* @param middle
* @param last
* @param comparator
*/
private static void partiallySort(MarkerEntry[] array, int first,
int middle, int last, Comparator comparator) {
heapify(array, first, middle, comparator);
adjustMaxElement(array, first, middle, last, comparator);
heapToSortedArray(array, first, middle, comparator);
}
/**
* Swap the max heap element with any greater elements in rest of the array
*
* @param heapArray
* @param first
* @param heapSize
* @param last
* @param comparator
*/
private static void adjustMaxElement(MarkerEntry[] heapArray, int first,
int heapSize, int last, Comparator comparator) {
/*
* we do not clear caches for heap elements when re-adjusting and
* sorting this will ensure sorting remains fast
*/
int current = heapSize;
while (current <= last) {
if (comparator.compare(heapArray[current], heapArray[first]) < 0) {
MarkerEntry tmp = heapArray[current];
heapArray[current] = heapArray[first];
heapArray[first] = tmp;
adjustHeap(heapArray, first, first, heapSize, comparator);
}
// clear cache of the one not in heap
heapArray[current].clearCache();
++current;
}
}
/**
* Re-adjust the elements in the heap to maintain heap-property
*
* Note: caches are not cleared in this method, as it would offset
* to a certain extent the benefit of caching in sorting.
*
* @param array
* @param first
* @param position
* @param last
* @param comparator
*/
private static void adjustHeap(MarkerEntry[] array, int first,
int position, int last, Comparator comparator) {
MarkerEntry hole = array[position];
int holeIndex = position;
holeIndex = leafSearch(array, first, holeIndex, last, comparator);
holeIndex = bottomUpSearch(array, first, holeIndex,position,hole,last, comparator);
array[holeIndex] = hole;
}
/**
* Percolate down the Heap: adjust left ,right, self nodes for heap starting
* from hole all the way down the heap
*
* @param array
* @param first
* @param position
* @param last
* @param comparator
* @return new holeIndex
*/
private static int leafSearch(MarkerEntry[] array, int first, int position,
int last, Comparator comparator) {
int holeOffset = position - first;
int len = last - first;
int childOffset = 2 * holeOffset + 2;
//
while (childOffset < len) {
if (comparator.compare(array[first + childOffset], array[first
+ (childOffset - 1)]) < 0)
--childOffset;
array[first + holeOffset] = array[first + childOffset];
holeOffset = childOffset++;
childOffset *= 2;
}
if (childOffset-- == len) {
array[first + holeOffset] = array[first + childOffset];
holeOffset = childOffset;
}
return holeOffset + first;
}
/**
* percolate up the Heap: add the hole element back to heap at the right
* position, all the way up the heap between fromIndex and toIndex
*
* @param array
* @param first
* @param fromIndex
* @param toIndex
* @param hole
* @param last
* @param comparator
* @return new holeIndex
*/
private static int bottomUpSearch(MarkerEntry[] array, int first, int fromIndex,
int toIndex, MarkerEntry hole, int last, Comparator comparator) {
int holeOffset = fromIndex - first;
int parent = (holeOffset - 1) / 2;
int top = toIndex - first;
while (holeOffset != top
&& (comparator.compare(array[first + parent], hole) < 0)) {
array[first + holeOffset] = array[first + parent];
holeOffset = parent;
parent = (holeOffset - 1) / 2;
}
/*
* Using Binary search to locate the parent to replace.
* This is worse compared to linear search as most of the
* holes would replace only a few parents above them.
* This code has been left commented for future examination.
* */
/*
int top = position - first;
int lowParent = 1;
int highParent = 0;
int from = holeOffset;
while (from > top) {
from = (from - 1) / 2;
highParent++;
}
if (from != top)
highParent--;// Between, exculding top//n++;
int parentReplaceCount = -1;
// start at lower ranges because of heap property
int midParent = lowParent + (highParent - lowParent) / 4;
while (lowParent <= highParent) {
// mth parent
int currenParentIndex = holeOffset;
int mth = midParent;
while (mth > 0) {
currenParentIndex = (currenParentIndex - 1) / 2;
mth--;
}
int value = comparator.compare(array[first + currenParentIndex], hole);
if (value < 0) {
parentReplaceCount = midParent;
lowParent = midParent + 1;
} else if (value > 0) {
highParent = midParent - 1;
} else {
// we are looking for just lower
// not exact match
highParent = midParent - 1;
}
midParent = (highParent + lowParent) / 2;
}
int parent = (holeOffset - 1) / 2;
for (int i = 1; i <= parentReplaceCount; i++) {
array[first + holeOffset] = array[first + parent];
holeOffset = parent;
parent = (holeOffset - 1) / 2;
}
*/
return first + holeOffset;
}
/**
* Makes a heap in the array
* @param array
* @param first
* @param last
* @param comparator
*/
private static void heapify(MarkerEntry[] array, int first, int last,
Comparator comparator) {
if (last - first < 2)
return;
int parent = (last - first - 2) / 2;
do
adjustHeap(array, first, first + parent, last, comparator);
while (parent-- != 0);
}
/**
* Converts a heap array to sorted array
*
* @param array
* @param first
* @param last
* @param comparator
*
*/
private static void heapToSortedArray(MarkerEntry[] array, int first,
int last, Comparator comparator) {
//TODO:Use mergesort to convert the heap to sorted array?
while (last - first > 1) {
// clear cache sorted and present at the end
array[last].clearCache();
// leave out the max elements at the end
MarkerEntry tmp = array[--last];
array[last] = array[first];
array[first] = tmp;
// readjust for next max
adjustHeap(array, first, first, last, comparator);
}
array[first+1].clearCache();
array[first].clearCache();
}
/**
* Sorts [from,first+k-1] in the array of [from,to] using a variant of
* modified heapsort, such that
* array[from]<array[from+1]<...<array[from+k-1] and
* array[from+k-1]<arra[from+k||from+k+1||from+k+2|| ....to]
*
* Note: if k is greater than a number,the sorting happens in batches of
* that number, this for performance reasons.
*
* @param entries
* @param comparator
* @param from
* @param to
* @param k
* @param monitor
*/
public static void sortStartingKElement(MarkerEntry[] entries,
Comparator comparator, int from, int to, int k,IProgressMonitor monitor) {
// check range valid
int last = from + k-1;
if (entries.length == 0 || from < 0 || from >= to || last < from
|| last > to || to > entries.length - 1 || to < 0)
return;
int n=to-from+1;
if (n <= BATCH_SIZE && (((float) n / k) <= MERGE_OR_HEAP_SWITCH)
/*|| ((float) n / k) <= MERGE_OR_HEAP_SWITCH*/) {
// use arrays sort
Arrays.sort(entries, from, to + 1, comparator);
// clear cache for first to middle since we are done with sort
for (int i = from; i <= to; i++) {
entries[i].clearCache();
}
return;
}
// do it in blocks of BATCH_SIZE so we get a chance
// of clearing caches to keep memory usage to a minimum
//we choose k-1 so that last batch includes last element
//in case k is a multiple of BATCH_SIZE
int totalBatches = (k-1) / BATCH_SIZE;
int batchCount = 0;
while (totalBatches > 0) {
if(monitor.isCanceled()){
return;
}
int fromTemp = from + batchCount * BATCH_SIZE;
int toTemp = from + (batchCount + 1) * BATCH_SIZE;
partiallySort(entries, fromTemp, toTemp, to, comparator);
batchCount++;
totalBatches--;
}
if(monitor.isCanceled()){
return;
}
if (last >= from + batchCount * BATCH_SIZE) {
// the last remaining enteries
if (last == to) {
partiallySort(entries, from + batchCount * BATCH_SIZE, last,
to, comparator);
} else {
partiallySort(entries, from + batchCount * BATCH_SIZE, last+1,
to, comparator);
}
}
}
/**
* @param fArray1
* @param comparator
* @param from
* @param k
* @param limit
*/
public static void sortStartingKElement(MockMarkerEntry[] fArray1,
Comparator comparator, int from, int k, int limit) {
sortStartingKElement(fArray1, comparator, from, k, limit,new NullProgressMonitor());
}
/**
* Sorts [0,k-1] in the array of [0,entries.length-1] using a variant of
* modified heapsort, such that
* array[0]<array[1]<...<array[k-1] and
* array[k-1]<arra[k||k+1||k+2|| ....entries.length-1]
*
* Note: if k is greater than a number,the sorting happens in batches of
* that number, this for performance reasons.
*
* @param entries
* @param comparator
* @param k
* @param monitor
*/
public static void sortStartingKElement(MarkerEntry[] entries,
Comparator comparator, int k,IProgressMonitor monitor) {
sortStartingKElement(entries, comparator, 0, entries.length - 1, k,monitor);
}
/**
*
* Sorts [from,first+k-1] in the array of [from,entries.length-1] using a variant of
* modified heapsort, such that
* array[from]<array[from+1]<...<array[from+k-1] and
* array[from+k-1]<arra[from+k||from+k+1||from+k+2|| ....entries.length-1]
*
* Note: if k is greater than a number,the sorting happens in batches of
* that number, this for performance reasons.
*
* @param entries
* @param comparator
* @param from
* @param k
* @param monitor
*/
public static void sortStartingKElement(MarkerEntry[] entries,
Comparator comparator, int from, int k, IProgressMonitor monitor) {
sortStartingKElement(entries, comparator, from, entries.length - 1, k,monitor);
}
}