/* * Copyright (c) 2003-onwards Shaven Puppy Ltd * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * * Neither the name of 'Shaven Puppy' nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package com.shavenpuppy.jglib.algorithms; import com.shavenpuppy.jglib.MultiBuffer; /** * Radix Sort algorithm. * * Creation date: (12/21/00 4:14:46 PM) * @author: cas */ public final class BufferRadixSort { /** * The RadixSort can sort things which are Sortable. * They simply have to return an int which is their sort order. */ public interface Sortable { public int sortOrder(); } private final int[] mHistogram = new int[1024]; // Counters for each byte private final int[] mOffset = new int[256]; // Offsets (nearly a cumulative distribution function) private int mCurrentSize = -1; // Current size of the indices list private int[] mIndices; // Two lists, swapped each pass private int[] mIndices2; /** * RadixSort constructor comment. */ public BufferRadixSort() { super(); } /** * Returns the sorted indexes. * Creation date: (22/12/2000 01:05:01) */ public int[] getIndices() { return mIndices; } /** * Resets the indices. Returns a self-reference. * Creation date: (12/21/00 4:26:11 PM) */ public BufferRadixSort resetIndices() { for (int i = 0; i < mCurrentSize; i++) { mIndices[i] = i; } return this; } /** * Main sort routine * Input : input a list of integer values to sort. Size is limit of ints field. * Output : mIndices, a list of indices in sorted order, i.e. in the order you may process your data * Return : Self-Reference * Exception: - * Remark : this one is for integer values * Creation date: (12/21/00 4:29:01 PM) */ public BufferRadixSort sort(MultiBuffer input) { if (input == null) { throw new IllegalArgumentException("Null array input to radix sort"); } final int length = input.ints.limit(); if (length == 0) { return this; } // Resize lists if needed if(length > mCurrentSize) { mIndices = new int[length]; mIndices2 = new int[length]; mCurrentSize = length; // Initialize indices so that the input buffer is read in sequential order resetIndices(); } // Clear counters java.util.Arrays.fill(mHistogram, 0); // Create histograms (counters). Counters for all passes are created in one run. // Pros: read input buffer once instead of four times // Cons: mHistogram is 4Kb instead of 1Kb // We must take care of signed/unsigned values for temporal coherence.... // Temporal coherence boolean AlreadySorted = true; // Optimism... int iC = 0; // Prepare to count. // We must get the incoming integer array as a sequence of bytes int pC = 0; int pLen = length << 2; int h0= 0; // Histogram for first pass (LSB) int h1= 256; // Histogram for second pass int h2= 512; // Histogram for third pass int h3= 768; // Histogram for last pass (MSB) // Temporal coherence int PrevVal = input.ints.get(mIndices[0]); while(pC!=pLen) { // Temporal coherence int Val = input.ints.get(mIndices[iC++]); // Read input buffer in previous sorted order if(Val<PrevVal) { AlreadySorted = false; break; } // Check whether already sorted or not PrevVal = Val; // Update for next iteration // Create histograms mHistogram[h0 + (input.bytes.get(pC++) & 0xFF)]++; mHistogram[h1 + (input.bytes.get(pC++) & 0xFF)]++; mHistogram[h2 + (input.bytes.get(pC++) & 0xFF)]++; mHistogram[h3 + (input.bytes.get(pC++) & 0xFF)]++; } // If all input values are already sorted, we just have to return and leave the previous list unchanged. // That way the routine may take advantage of temporal coherence, for example when used to sort transparent faces. if(AlreadySorted) { return this; } // otherwise continue building histograms while(pC!=pLen) { // Create histograms mHistogram[h0 + (input.bytes.get(pC++) & 0xFF)]++; mHistogram[h1 + (input.bytes.get(pC++) & 0xFF)]++; mHistogram[h2 + (input.bytes.get(pC++) & 0xFF)]++; mHistogram[h3 + (input.bytes.get(pC++) & 0xFF)]++; } // Compute #negative values involved if needed int NbNegativeValues = 0; // An efficient way to compute the number of negatives values we'll have to deal with is simply to sum the 128 // last values of the last histogram. Last histogram because that's the one for the Most Significant Byte, // responsible for the sign. 128 last values because the 128 first ones are related to positive numbers. for(int i = 128; i < 256; i++) { NbNegativeValues += mHistogram[768 + i]; // 768 for last histogram, 128 for negative part } // Radix sort, j is the pass number (0=LSB, 3=MSB) for(int j = 0; j < 4; j++) { // Reset flag. The sorting pass is supposed to be performed. (default) boolean PerformPass = true; // Check pass validity [some cycles are lost there in the generic case, but that's ok, just a little loop] for(int i = 0; i < 256; i++) { // If all values have the same byte, sorting is useless. It may happen when sorting bytes or words instead of dwords. // This routine actually sorts words faster than dwords, and bytes faster than words. Standard running time (O(4*n))is // reduced to O(2*n) for words and O(n) for bytes. Running time for floats depends on actual values... int CurCount = mHistogram[(j << 8) + i]; if(CurCount == length) { PerformPass=false; break; } // If at least one count is not null, we suppose the pass must be done. Hence, this test takes very few CPU time in the generic case. if(CurCount != 0) { break; } } // Sometimes the fourth (negative) pass is skipped because all numbers are negative and the MSB is 0xFF (for example). This is // not a problem, numbers are correctly sorted anyway. if (PerformPass) { if(j != 3) { // Here we deal with positive values only // Create offsets mOffset[0] = 0; for(int i = 1; i < 256; i++) { mOffset[i] = mOffset[i-1] + mHistogram[(j<<8) + i - 1]; } } else { // This is a special case to correctly handle negative integers. They're sorted in the right order but at the wrong place. // Create biased offsets, in order for negative numbers to be sorted as well mOffset[0] = NbNegativeValues; // First positive number takes place after the negative ones for(int i=1;i<128;i++) { mOffset[i] = mOffset[i-1] + mHistogram[(j<<8) + i - 1]; // 1 to 128 for positive numbers } // Fixing the wrong place for negative values mOffset[128] = 0; for(int i=129;i<256;i++) { mOffset[i] = mOffset[i-1] + mHistogram[(j<<8) + i - 1]; } } // Perform Radix Sort pC = j; for (int i = 0; i < length; i ++) { int id = mIndices[i]; int pindex = (id<<2) + pC; int mOffsetindex = input.bytes.get(pindex) & 0xFF; int mIndices2index = mOffset[mOffsetindex]++; mIndices2[mIndices2index] = id; // mIndices2[mOffset[p[(id<<2) + pC]]++] = id; } // Swap pointers for next pass int[] Tmp = mIndices; mIndices = mIndices2; mIndices2 = Tmp; } } return this; } }