//# Sort.tcc: Sort objects on one or more keys //# Copyright (C) 1995,1996,1997,1998,1999,2000,2001 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library is distributed in the hope that it will be useful, but WITHOUT //# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or //# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# //# $Id$ #ifndef CASA_SORT_TCC #define CASA_SORT_TCC //# Includes #include #include #include #ifdef _OPENMP #include #endif namespace casacore { //# NAMESPACE CASACORE - BEGIN template T Sort::doSort (Vector& indexVector, T nrrec, int opt, Bool doTryGenSort) const { if (nrrec == 0) { return nrrec; } //# Try if we can use the faster GenSort when we have one key only. if (doTryGenSort && nrkey_p == 1) { uInt n = keys_p[0]->tryGenSort (indexVector, nrrec, opt); if (n > 0) { return n; } } indexVector.resize (nrrec); indgen (indexVector); // Pass the sort function a C-array of indices, because indexing // in there is (much) faster than in a vector. Bool del; T* inx = indexVector.getStorage (del); // Choose the sort required. int nodup = opt & NoDuplicates; int type = opt - nodup; // Determine default sort to use. int nthr = 1; #ifdef _OPENMP nthr = omp_get_max_threads(); // Do not use more threads than there are values. if (uInt(nthr) > nrrec) nthr = nrrec; #endif if (type == DefaultSort) { type = (nrrec<1000 || nthr==1 ? QuickSort : ParSort); } T n = 0; switch (type) { case QuickSort: if (nodup) { n = quickSortNoDup (nrrec, inx); }else{ n = quickSort (nrrec, inx); } break; case HeapSort: if (nodup) { n = heapSortNoDup (nrrec, inx); }else{ n = heapSort (nrrec,inx); } break; case InsSort: if (nodup) { n = insSortNoDup (nrrec, inx); }else{ n = insSort (nrrec, inx); } break; case ParSort: n = parSort (nthr, nrrec, inx); if (nodup) { n = insSortNoDup (nrrec, inx); } break; default: throw SortInvOpt(); } indexVector.putStorage (inx, del); // If n < nrrec, some duplicates have been deleted. // This means we have to resize the Vector. if (n < nrrec) { indexVector.resize (n, True); } return n; } template T Sort::doUnique (Vector& uniqueVector, T nrrec) const { Vector indexVector(nrrec); indgen (indexVector); return doUnique (uniqueVector, indexVector); } template T Sort::doUnique (Vector& uniqueVector, const Vector& indexVector) const { Vector changeKey; return doUnique (uniqueVector, changeKey, indexVector); } template T Sort::doUnique (Vector& uniqueVector, Vector& changeKey, const Vector& indexVector) const { T nrrec = indexVector.nelements(); uniqueVector.resize (nrrec); changeKey.resize (nrrec); if (nrrec == 0) { return 0; } // Pass the sort function a C-array of indices, because indexing // in there is (much) faster than in a vector. Bool delInx, delUniq, delChange; const T* inx = indexVector.getStorage (delInx); T* uniq = uniqueVector.getStorage (delUniq); size_t* change = changeKey.getStorage (delChange); uniq[0] = 0; T nruniq = 1; size_t idxComp; for (T i=1; i T Sort::parSort (int nthr, T nrrec, T* inx) const { Block index(nrrec+1); Block tinx(nthr+1); Block np(nthr); // Determine ordered parts in the array. // It is done in parallel, whereafter the parts are combined. int step = nrrec/nthr; for (int i=0; i T Sort::quickSortNoDup (T nrrec, T* inx) const { qkSort (nrrec, inx); return insSortNoDup (nrrec, inx); } template void Sort::qkSort (T nr, T* inx) const { // If the nr of elements to be sorted is less than N, it is // better not to use quicksort anymore (according to Sedgewick). // Take N=15, because that seems to work best after testing // N=5, 10, 15 and 20. if (nr <= 15) { return; } // According to Sedgewick it is best to use a random partition element // to avoid degenerated cases (if the data is already in order for example) // rand is not a particularly good random number generator, but good // enough for this purpose. // Put this element at the beginning of the array. T p = rand() % nr; swap (T(0), p, inx); // Now shift all elements < partition-element to the left. // If an element is equal, shift every other element to avoid // degeneration. This trick is described by Jon Bentley in // UNIX Review, October 1992. // We do not have equal elements anymore (because of the stability // property introduced on 13-Feb-1995). T j = 0; for (T i=1; i T Sort::heapSort (T nrrec, T* inx) const { // Use the heapsort algorithm described by Jon Bentley in // UNIX Review, August 1992. T j; inx--; for (j=nrrec/2; j>=1; j--) { siftDown (j, nrrec, inx); } for (j=nrrec; j>=2; j--) { swap (T(1), j, inx); siftDown (T(1), j-1, inx); } return nrrec; } template T Sort::heapSortNoDup (T nrrec, T* inx) const { heapSort (nrrec, inx); return insSortNoDup (nrrec, inx); } template void Sort::siftDown (T low, T up, T* inx) const { T sav = inx[low]; T c; T i; for (i=low; (c=2*i)<=up; i=c) { if (c < up && compare(inx[c+1], inx[c]) <= 0) { c++; } inx[i] = inx[c]; } inx[i] = sav; for ( ; (c=i/2)>=low; i=c) { if (compare (inx[i], inx[c]) > 0) { break; } swap (c, i, inx); } } // Note that the block of SortKeys is defined as void*, to achieve // that only 1 type of Block is needed. // Casting is perfectly save. // The comparison functions return: // -1 when obj1 < obj2 // 0 when obj1 = obj2 // 1 when obj1 > obj2 // compare returns: // 2 when data[i1],data[i2] is in correct order // (thus data[i1] < data[i2] for ascending sort) // 1 when data is equal and indices are in order // 0 when data is out of order // -1 when data is equal and indices are out of order template int Sort::compare (T i1, T i2) const { size_t idxComp; return compareChangeIdx(i1, i2, idxComp); } // This is a similar function to compare() but it also gives back which is the // first comparison function that doesn't match. // idxComp gives the comparison function index. In case the function returns // 1 or -1 idxComp is not modified. template int Sort::compareChangeIdx(T i1, T i2, size_t& idxComp) const { int seq; SortKey* skp; for (size_t i=0; icmpObj_p->comp ((char*)skp->data_p + i1*skp->incr_p, (char*)skp->data_p + i2*skp->incr_p); if (seq == skp->order_p) { idxComp = i; return 2; // in order } if (seq != 0) { idxComp = i; return 0; // out-of-order } } // Equal keys, so return i1