/*============================================================================ Title: weld.h Author: Ignacio Castaņo Date: 07/10/2002 License: Public Domain ============================================================================*/ #ifndef _WELD_H_ #define _WELD_H_ #define WELD_MAIN 1 // true if you want to compile the 'main' usage demo. /*---------------------------------------------------------------------------- Doc: ----------------------------------------------------------------------------*/ /** @file weld.h * @brief Weld function to remove array duplicates in linear time. **/ /*---------------------------------------------------------------------------- Headers ----------------------------------------------------------------------------*/ #include // vector #include // equal_to /*---------------------------------------------------------------------------- Functions: ----------------------------------------------------------------------------*/ inline size_t NextPowerOfTwo(size_t x) { size_t p = 1; while( x > p ) { p += p; } return p; } // In case hash_map is not provided. namespace std { template struct hash {}; template <> struct hash { size_t operator()(int x) { return x; } }; template <> struct hash { size_t operator()(const char * str) { size_t h = 5381; while (*str) { h = ((h << 5) + h) ^ *str; str++; } return h; } }; } /** Generic welding routine. This function welds the elements of the vector p * and returns the cross references in the xrefs array. To compare the elements * it uses the standard hash and key_equal functors. * * This code is based on the ideas of Ville Miettinen and Pierre Terdiman. */ template size_t weld( std::vector & p, std::vector & xrefs, HashFunction hash, BinaryPredicate equal ) { size_t const NIL = size_t(~0); // linked list terminator symbol. size_t const N = p.size(); // # of input vertices. size_t outputCount = 0; // # of output vertices size_t hashSize = NextPowerOfTwo(N); // size of the hash table size_t * const hashTable = new size_t[hashSize + N]; // hash table + linked list size_t * const next = hashTable + hashSize; // use bottom part as linked list memset( hashTable, NIL, hashSize*sizeof(size_t) ); // init hash table (NIL = 0xFFFFFFFF so memset works) // xrefs and p have the same size. xrefs.resize(N); for (size_t i = 0; i < N; ++i) { const T & e = p[i]; size_t hashValue = hash(e) & (hashSize-1); size_t offset = hashTable[hashValue]; // traverse linked list while( offset != NIL && !equal(p[offset], e) ) { offset = next[offset]; } xrefs[i] = offset; // no match found - copy vertex & add to hash if( offset == NIL ) { // save xref xrefs[i] = outputCount; // copy vertex p[outputCount] = e; // link to hash table next[outputCount] = hashTable[hashValue]; // update hash heads and increase output counter hashTable[hashValue] = outputCount++; } } // cleanup delete [] hashTable; // drop duplicates. p.resize(outputCount); // number of output vertices return outputCount; } /** Reorder the given array accoding to the indices given in xrefs. * Use this after weld to reorder an array according to its result: * @code * size_t num = weld(points, xrefs); * reorder(texcoords, num, xrefs); * @endcode */ template void reorder(std::vector & array, const std::vector & xrefs, const size_t num) { std::vector new_array; new_array.resize(num); for(size_t i = 0; i < num; ++i) { new_array[i] = array[xrefs[i]]; } // replace old array by the new one. std::swap(array, new_array); } #endif // _PI_WELDING_H_