/* * QSort.java * * Copyright (c) 1997 Cornell University. * Copyright (c) 1997 Sun Microsystems, Inc. * * See the file "license.terms" for information on usage and * redistribution of this file, and for a DISCLAIMER OF ALL * WARRANTIES. * * Included in SQLite3 port to C# for use in testharness only; 2008 Noah B Hart * * RCS @(#) $Id: QSort.java,v 1.2 1999/05/09 01:14:07 dejong Exp $ * */ using System.Text; namespace tcl.lang { /* * This file is adapted from the JDK 1.0 QSortAlgorithm.java demo program. * Original copyright notice is preserveed below. * * @(#)QSortAlgorithm.java 1.3 29 Feb 1996 James Gosling * * Copyright (c) 1994-1996 Sun Microsystems, Inc. All Rights Reserved. * * Permission to use, copy, modify, and distribute this software * and its documentation for NON-COMMERCIAL or COMMERCIAL purposes and * without fee is hereby granted. * Please refer to the file http://www.javasoft.com/copy_trademarks.html * for further important copyright and trademark information and to * http://www.javasoft.com/licensing.html for further important * licensing information for the Java (tm) Technology. * * SUN MAKES NO REPRESENTATIONS OR WARRANTIES. ABOUT THE SUITABILITY OF * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. * * THIS SOFTWARE IS NOT DESIGNED OR INTENDED FOR USE OR RESALE AS ON-LINE * CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING FAIL-SAFE * PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES, AIRCRAFT * NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT LIFE * SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE * SOFTWARE COULD LEAD DIRECTLY TO DEATH, PERSONAL INJURY, OR SEVERE * PHYSICAL OR ENVIRONMENTAL DAMAGE ("HIGH RISK ACTIVITIES"). SUN * SPECIFICALLY DISCLAIMS ANY EXPRESS OR IMPLIED WARRANTY OF FITNESS FOR * HIGH RISK ACTIVITIES. */ /// Sorts an array of TclObjects. sealed class QSort { internal const int ASCII = 0; internal const int INTEGER = 1; internal const int REAL = 2; internal const int COMMAND = 3; internal const int DICTIONARY = 4; // Data used during sort. private int sortMode; private int sortIndex; private bool sortIncreasing; private string sortCommand; private Interp sortInterp; /// This is a generic version of C.A.R Hoare's Quick Sort /// algorithm. This will handle arrays that are already /// sorted, and arrays with duplicate keys.
/// /// If you think of a one dimensional array as going from /// the lowest index on the left to the highest index on the right /// then the parameters to this function are lowest index or /// left and highest index or right. The first time you call /// this function it will be with the parameters 0, a.length - 1. /// ///
/// an integer array /// /// left boundary of array partition /// /// right boundary of array partition /// private void quickSort( TclObject[] a, int lo0, int hi0 ) { int lo = lo0; int hi = hi0; TclObject mid; if ( hi0 > lo0 ) { // Arbitrarily establishing partition element as the midpoint of // the array. mid = a[( lo0 + hi0 ) / 2]; // loop through the array until indices cross while ( lo <= hi ) { // find the first element that is greater than or equal to // the partition element starting from the left Index. while ( ( lo < hi0 ) && ( compare( a[lo], mid ) < 0 ) ) { ++lo; } // find an element that is smaller than or equal to // the partition element starting from the right Index. while ( ( hi > lo0 ) && ( compare( a[hi], mid ) > 0 ) ) { --hi; } // if the indexes have not crossed, swap if ( lo <= hi ) { swap( a, lo, hi ); ++lo; --hi; } } // If the right index has not reached the left side of array // must now sort the left partition. if ( lo0 < hi ) { quickSort( a, lo0, hi ); } // If the left index has not reached the right side of array // must now sort the right partition. if ( lo < hi0 ) { quickSort( a, lo, hi0 ); } } } /// Swaps two items in the array. /// /// /// the array. /// /// index of first item. /// /// index of first item. /// private static void swap( TclObject[] a, int i, int j ) { TclObject T; T = a[i]; a[i] = a[j]; a[j] = T; } /// Starts the quick sort with the given parameters. /// /// /// if cmd is specified, it is evaluated inside this /// interp. /// /// the array of TclObject's to sort. /// /// the sortng mode. /// /// true if the sorted array should be in increasing /// order. /// /// the command to use for comparing items. It is used only /// if sortMode is COMMAND. /// /// true if the result should contain no duplicates. /// /// the length of the sorted array. This length may be different /// from the length if array a due to the unique option. /// /// TclException if an error occurs during sorting. /// internal int sort( Interp interp, TclObject[] a, int mode, int index, bool increasing, string cmd, bool unique ) { sortInterp = interp; sortMode = mode; sortIndex = index; sortIncreasing = increasing; sortCommand = cmd; quickSort( a, 0, a.Length - 1 ); if ( !unique ) { return a.Length; } int uniqueIx = 1; for ( int ix = 1; ix < a.Length; ix++ ) { if ( compare( a[ix], a[uniqueIx - 1] ) == 0 ) { a[ix].release(); } else { if ( ix != uniqueIx ) { a[uniqueIx] = a[ix]; a[uniqueIx].preserve(); } uniqueIx++; } } return uniqueIx; } /// Compares the order of two items in the array. /// /// /// first item. /// /// second item. /// /// 0 if they are equal, 1 if obj1 > obj2, -1 otherwise. /// /// /// TclException if an error occurs during sorting. /// private int compare( TclObject obj1, TclObject obj2 ) { int index; int code = 0; if ( sortIndex != -1 ) { // The "-index" option was specified. Treat each object as a // list, extract the requested element from each list, and // compare the elements, not the lists. The special index "end" // is signaled here with a negative index (other than -1). TclObject obj; if ( sortIndex < -1 ) { index = TclList.getLength( sortInterp, obj1 ) - 1; } else { index = sortIndex; } obj = TclList.index( sortInterp, obj1, index ); if ( obj == null ) { throw new TclException( sortInterp, "element " + index + " missing from sublist \"" + obj1 + "\"" ); } obj1 = obj; if ( sortIndex < -1 ) { index = TclList.getLength( sortInterp, obj2 ) - 1; } else { index = sortIndex; } obj = TclList.index( sortInterp, obj2, index ); if ( obj == null ) { throw new TclException( sortInterp, "element " + index + " missing from sublist \"" + obj2 + "\"" ); } obj2 = obj; } switch ( sortMode ) { case ASCII: // ATK C# CompareTo use option // similar to -dictionary but a > A code = System.Globalization.CultureInfo.InvariantCulture.CompareInfo.Compare( obj1.ToString(), obj2.ToString(), System.Globalization.CompareOptions.Ordinal ); // code = obj1.ToString().CompareTo(obj2.ToString()); break; case DICTIONARY: code = doDictionary( obj1.ToString(), obj2.ToString() ); break; case INTEGER: try { int int1 = TclInteger.get( sortInterp, obj1 ); int int2 = TclInteger.get( sortInterp, obj2 ); if ( int1 > int2 ) { code = 1; } else if ( int2 > int1 ) { code = -1; } } catch ( TclException e1 ) { sortInterp.addErrorInfo( "\n (converting list element from string to integer)" ); throw e1; } break; case REAL: try { double f1 = TclDouble.get( sortInterp, obj1 ); double f2 = TclDouble.get( sortInterp, obj2 ); if ( f1 > f2 ) { code = 1; } else if ( f2 > f1 ) { code = -1; } } catch ( TclException e2 ) { sortInterp.addErrorInfo( "\n (converting list element from string to real)" ); throw e2; } break; case COMMAND: StringBuilder sbuf = new StringBuilder( sortCommand ); Util.appendElement( sortInterp, sbuf, obj1.ToString() ); Util.appendElement( sortInterp, sbuf, obj2.ToString() ); try { sortInterp.eval( sbuf.ToString(), 0 ); } catch ( TclException e3 ) { sortInterp.addErrorInfo( "\n (user-defined comparison command)" ); throw e3; } try { code = TclInteger.get( sortInterp, sortInterp.getResult() ); } catch ( TclException e ) { sortInterp.resetResult(); TclException e4 = new TclException( sortInterp, "comparison command returned non-numeric result" ); throw e4; } break; default: throw new TclRuntimeError( "Unknown sortMode " + sortMode ); } if ( sortIncreasing ) { return code; } else { return -code; } } /// Compares the order of two strings in "dictionary" order. /// /// /// first item. /// /// second item. /// /// 0 if they are equal, 1 if obj1 > obj2, -1 otherwise. /// private int doDictionary( string str1, string str2 ) { int diff = 0, zeros; int secondaryDiff = 0; bool cont = true; int i1 = 0, i2 = 0; int len1 = str1.Length; int len2 = str2.Length; while ( cont ) { if ( i1 >= len1 || i2 >= len2 ) { break; } if ( System.Char.IsDigit( str2[i2] ) && System.Char.IsDigit( str1[i1] ) ) { // There are decimal numbers embedded in the two // strings. Compare them as numbers, rather than // strings. If one number has more leading zeros than // the other, the number with more leading zeros sorts // later, but only as a secondary choice. zeros = 0; while ( ( i2 < ( len2 - 1 ) ) && ( str2[i2] == '0' ) ) { i2++; zeros--; } while ( ( i1 < ( len1 - 1 ) ) && ( str1[i1] == '0' ) ) { i1++; zeros++; } if ( secondaryDiff == 0 ) { secondaryDiff = zeros; } // The code below compares the numbers in the two // strings without ever converting them to integers. It // does this by first comparing the lengths of the // numbers and then comparing the digit values. diff = 0; while ( true ) { if ( i1 >= len1 || i2 >= len2 ) { cont = false; break; } if ( diff == 0 ) { diff = str1[i1] - str2[i2]; } i1++; i2++; if ( i1 >= len1 || i2 >= len2 ) { cont = false; break; } if ( !System.Char.IsDigit( str2[i2] ) ) { if ( System.Char.IsDigit( str1[i1] ) ) { return 1; } else { if ( diff != 0 ) { return diff; } break; } } else if ( !System.Char.IsDigit( str1[i1] ) ) { return -1; } } continue; } diff = str1[i1] - str2[i2]; if ( diff != 0 ) { if ( System.Char.IsUpper( str1[i1] ) && System.Char.IsLower( str2[i2] ) ) { diff = System.Char.ToLower( str1[i1] ) - str2[i2]; if ( diff != 0 ) { return diff; } else if ( secondaryDiff == 0 ) { secondaryDiff = -1; } } else if ( System.Char.IsUpper( str2[i2] ) && System.Char.IsLower( str1[i1] ) ) { diff = str1[i1] - System.Char.ToLower( str2[i2] ); if ( diff != 0 ) { return diff; } else if ( secondaryDiff == 0 ) { secondaryDiff = 1; } } else { return diff; } } i1++; i2++; } if ( i1 >= len1 && i2 < len2 ) { if ( !System.Char.IsDigit( str2[i2] ) ) { return 1; } else { return -1; } } else if ( i2 >= len2 && i1 < len1 ) { if ( !System.Char.IsDigit( str1[i1] ) ) { return -1; } else { return 1; } } if ( diff == 0 ) { diff = secondaryDiff; } return diff; } } }