/*
* © Copyright IBM Corp. 2012-2013
*
* 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.
*/
package com.ibm.commons.util;
import java.util.Vector;
/**
* Easy to use QuickSort set of classes for sorting data.
* <p>
* The QuickSort class is an abstract class that requires some of its methods to be
* overridden to give actual access to the collection.
* </p>
* @ibm-api
*/
public abstract class QuickSort {
/**
* Sort the entire collection.
* @ibm-api
*/
public void sort() {
qSortHelp( 0, getCount() );
}
/**
* Sort a subpart of the collection.
* @ibm-api
*/
public void sort( int first, int length ) {
qSortHelp( first, length );
}
/**
* Returns the number of elements in the collection.
* @ibm-api
*/
public abstract int getCount();
/**
* Compare 2 elements in the collection, based on their index.
* @ibm-api
*/
public abstract int compare(int idx1, int idx2);
/**
* Exchange 2 elements in the collection, based on their index.
* @ibm-api
*/
public abstract void exchange(int idx1, int idx2);
private final void qSortHelp( int pivotP, int nElem ) {
int leftP, rightP, pivotEnd, pivotTemp, leftTemp;
int lNum;
int retval;
tailRecursion:
do { // Tail recursion
// Shell sort on smallest array
if( nElem<=7 ) {
// Specific case of 2 or less items
if(nElem == 2) {
if(compare(pivotP, rightP = 1 + pivotP) > 0) {
exchange (pivotP, rightP);
}
return;
}
// Shell sort
for( int i=0; i<nElem; i++ ) {
for( int j=i+1; j<nElem; j++ ) {
if( compare(i+pivotP, j+pivotP)>0 ) {
exchange(i+pivotP,j+pivotP);
}
}
}
return;
}
rightP = (nElem - 1) + pivotP;
leftP = (nElem >> 1) + pivotP;
// sort the pivot, left, and right elements for "median of 3"
if(compare(leftP, rightP) > 0) {
exchange (leftP, rightP);
}
if(compare(leftP, pivotP) > 0) {
exchange (leftP, pivotP);
} else {
if(compare(pivotP, rightP) > 0) {
exchange (pivotP, rightP);
}
}
if(nElem == 3) {
exchange (pivotP, leftP);
return;
}
// now for the classic Hoare algorithm
leftP = pivotEnd = pivotP + 1;
mainloop:
do {
while((retval = compare(leftP, pivotP)) <= 0) {
if(retval == 0) {
exchange(leftP, pivotEnd);
pivotEnd += 1;
}
if( leftP<rightP )
leftP += 1;
else
break mainloop;
}
while(leftP<rightP) {
if((retval = compare(pivotP, rightP)) < 0)
rightP -= 1;
else {
exchange(leftP, rightP);
if(retval != 0) {
leftP += 1;
rightP -= 1;
}
break;
}
}
} while(leftP<rightP);
if(compare(leftP, pivotP) <= 0) {
leftP = leftP + 1;
}
leftTemp = leftP - 1;
pivotTemp = pivotP;
while((pivotTemp < pivotEnd) && (leftTemp >= pivotEnd)) {
exchange(pivotTemp, leftTemp);
pivotTemp += 1;
leftTemp -= 1;
}
lNum = leftP - pivotEnd;
nElem = (nElem + pivotP) - leftP;
// Sort smaller partition first to reduce stack usage
if(nElem < lNum) {
qSortHelp(leftP, nElem);
nElem = lNum;
} else {
qSortHelp(pivotP, lNum);
pivotP = leftP;
}
} while(true); //goto tailRecursion;
}
/**
* Quicksort class used to sort an array of integers.
* @ibm-api
*/
public static class IntArray extends QuickSort {
public IntArray( int[] array, int first, int count ) {
this.array = array;
this.first = first;
this.count = count;
}
public IntArray( int[] array ) {
this( array, 0, array.length );
}
public int getCount() {
return count;
}
public int compare(int idx1, int idx2) {
return array[first+idx1]-array[first+idx2];
}
public void exchange(int idx1, int idx2) {
int tmp = array[first+idx1];
array[first+idx1] = array[first+idx2];
array[first+idx2] = tmp;
}
private int[] array;
private int first;
private int count;
}
/**
* Quicksort class used to sort an array of objects.
* @ibm-api
*/
public static class ObjectArray extends QuickSort {
public ObjectArray( Object[] array, int first, int count ) {
this.array = array;
this.first = first;
this.count = count;
}
public ObjectArray( Object[] array ) {
this( array, 0, array!=null ? array.length : 0 );
}
public int getCount() {
return count;
}
public void exchange(int idx1, int idx2) {
Object tmp = array[first+idx1];
array[first+idx1] = array[first+idx2];
array[first+idx2] = tmp;
}
public Object getObject(int index) {
return array[index];
}
public int compare(int idx1, int idx2) {
return compare( array[idx1], array[idx2] );
}
public int compare(Object o1, Object o2) {
if( o1==null && o2==null ) {
return 0;
}
if( o2==null ) {
return 1;
}
if( o1==null ) {
return -1;
}
return o1.toString().compareTo(o2.toString());
}
private Object[] array;
private int first;
private int count;
}
/**
* Quicksort class used to sort an array of Strings.
* @ibm-api
*/
public static class StringArray extends QuickSort {
public StringArray( String[] array, int first, int count, boolean ignoreCase ) {
this.array = array;
this.first = first;
this.count = count;
this.ignoreCase = ignoreCase;
}
public StringArray( String[] array, boolean ignoreCase ) {
this( array, 0, array!=null ? array.length : 0, ignoreCase );
}
public StringArray( String[] array) {
this( array, false );
}
public int getCount() {
return count;
}
public int compare(int idx1, int idx2) {
return compare( array[first+idx1], array[first+idx2] );
}
public int compare(String s1, String s2) {
if( s1==null && s2==null ) {
return 0;
}
if( s2==null ) {
return 1;
}
if( s1==null ) {
return -1;
}
if( ignoreCase ) {
return s1.compareToIgnoreCase(s2);
} else {
return s1.compareTo(s2);
}
}
public void exchange(int idx1, int idx2) {
String tmp = array[first+idx1];
array[first+idx1] = array[first+idx2];
array[first+idx2] = tmp;
}
private String[] array;
private int first;
private int count;
private boolean ignoreCase;
}
/**
* Quicksort class used to sort a Java List.
* @ibm-api
*/
public static class JavaList extends QuickSort {
public JavaList( java.util.List list ) {
this.list = list;
}
public int getCount() {
return list.size();
}
public int compare(int idx1, int idx2) {
Object o1 = list.get(idx1);
Object o2 = list.get(idx2);
return compare( o1, o2 );
}
public int compare(Object o1, Object o2) {
String s1 = o1!=null ? o1.toString() : "";
String s2 = o2!=null ? o2.toString() : "";
return s1.compareToIgnoreCase(s2.toString());
}
public void exchange(int idx1, int idx2) {
Object tmp = list.get(idx1);
list.set(idx1,list.get(idx2));
list.set(idx2,tmp);
}
private java.util.List list;
}
/**
* Quicksort class used to sort a Java Vector.
* @ibm-api
*/
public static class JavaVector extends QuickSort {
public JavaVector( Vector vector ) {
this.vector = vector;
}
public int getCount() {
return vector.size();
}
public int compare(int idx1, int idx2) {
Object o1 = vector.elementAt(idx1);
Object o2 = vector.elementAt(idx2);
return compare( o1, o2 );
}
public int compare(Object o1, Object o2) {
return o1.toString().compareToIgnoreCase(o2.toString());
}
public void exchange(int idx1, int idx2) {
Object tmp = vector.elementAt(idx1);
vector.setElementAt(vector.elementAt(idx2),idx1);
vector.setElementAt(tmp,idx2);
}
private Vector vector;
}
/*
public static void main( String[] args ) {
Debug.showGUI(true);
checkString();
}
private static void checkInt() {
int LOOP = 3;
int COUNT = 500000;
int[] items = new int[COUNT];
for( int l=0; l<LOOP; l++ ) {
for( int i=0; i<COUNT; i++ ) {
items[i] = (int)(rnd.nextDouble()*50000);
}
TDiag.trace( "Before sorting={0}", items ); // $NON-NLS-1$
long begin = System.currentTimeMillis();
(new TQuickSort.IntArray(items)).sort();
long end = System.currentTimeMillis();
TDiag.trace( "Sorting {0} items in {1}ms ({2})", TString.toString(COUNT), TString.toString(end-begin), items ); // $NON-NLS-1$
checkSorted(items);
}
}
private static void checkSorted( int[] items ) {
for( int i=0; i<items.length-1; i++ ) {
if( items[i]>items[i+1] ) {
TDiag.trace( "Invalid sort at {0} for array {1}", TString.toString(i), TString.toString(items) ); // $NON-NLS-1$
return;
}
}
}
private static void checkString() {
int LOOP = 3;
int COUNT = 10000;
String[] items = new String[COUNT];
for( int l=0; l<LOOP; l++ ) {
TStringBuffer buffer = new TStringBuffer();
for( int i=0; i<COUNT; i++ ) {
// The string is between 10 & 30 chrs
int length = (int)(rnd.nextDouble()*20)+10;
buffer.clear();
for( int j=0; j<length; j++ ) {
buffer.append( chars.charAt((int)(rnd.nextDouble()*chars.length())) );
}
items[i] = buffer.toString();
}
TDiag.trace( "Before sorting={0}", items ); // $NON-NLS-1$
long begin = System.currentTimeMillis();
(new TQuickSort.StringArray(items,false)).sort();
long end = System.currentTimeMillis();
TDiag.trace( "Sorting {0} items in {1}ms ({2})", TString.toString(COUNT), TString.toString(end-begin), items ); // $NON-NLS-1$
checkSorted(items);
}
}
private static void checkSorted( String[] items ) {
for( int i=0; i<items.length-1; i++ ) {
if( items[i].compareTo(items[i+1])>0 ) {
TDiag.trace( "Invalid sort at {0} for array {1}", TString.toString(i), TString.toString(items) ); // $NON-NLS-1$
return;
}
}
}
private static String chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; // $NON-NLS-1$
private static Random rnd = new Random(System.currentTimeMillis());
*/
}