/////////////////////////////////////////////////////////////////////// // File: genericvector.h // Description: Generic vector class // Author: Daria Antonova // Created: Mon Jun 23 11:26:43 PDT 2008 // // (C) Copyright 2007, Google Inc. // 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. // /////////////////////////////////////////////////////////////////////// // #ifndef TESSERACT_CCUTIL_GENERICVECTOR_H_ #define TESSERACT_CCUTIL_GENERICVECTOR_H_ #include #include #include #include "tesscallback.h" #include "errcode.h" #include "helpers.h" #include "ndminx.h" #include "strngs.h" // Use PointerVector below in preference to GenericVector, as that // provides automatic deletion of pointers, [De]Serialize that works, and // sort that works. template class GenericVector { public: GenericVector() { init(kDefaultVectorSize); } GenericVector(int size, T init_val) { init(size); init_to_size(size, init_val); } // Copy GenericVector(const GenericVector& other) { this->init(other.size()); this->operator+=(other); } GenericVector &operator+=(const GenericVector& other); GenericVector &operator=(const GenericVector& other); ~GenericVector(); // Reserve some memory. void reserve(int size); // Double the size of the internal array. void double_the_size(); // Resizes to size and sets all values to t. void init_to_size(int size, T t); // Return the size used. int size() const { return size_used_; } int size_reserved() const { return size_reserved_; } int length() const { return size_used_; } // Return true if empty. bool empty() const { return size_used_ == 0; } // Return the object from an index. T &get(int index) const; T &back() const; T &operator[](int index) const; // Returns the last object and removes it. T pop_back(); // Return the index of the T object. // This method NEEDS a compare_callback to be passed to // set_compare_callback. int get_index(T object) const; // Return true if T is in the array bool contains(T object) const; // Return true if the index is valid T contains_index(int index) const; // Push an element in the end of the array int push_back(T object); void operator+=(T t); // Push an element in the end of the array if the same // element is not already contained in the array. int push_back_new(T object); // Push an element in the front of the array // Note: This function is O(n) int push_front(T object); // Set the value at the given index void set(T t, int index); // Insert t at the given index, push other elements to the right. void insert(T t, int index); // Removes an element at the given index and // shifts the remaining elements to the left. void remove(int index); // Truncates the array to the given size by removing the end. // If the current size is less, the array is not expanded. void truncate(int size) { if (size < size_used_) size_used_ = size; } // Add a callback to be called to delete the elements when the array took // their ownership. void set_clear_callback(TessCallback1* cb); // Add a callback to be called to compare the elements when needed (contains, // get_id, ...) void set_compare_callback(TessResultCallback2* cb); // Clear the array, calling the clear callback function if any. // All the owned callbacks are also deleted. // If you don't want the callbacks to be deleted, before calling clear, set // the callback to NULL. void clear(); // Delete objects pointed to by data_[i] void delete_data_pointers(); // This method clears the current object, then, does a shallow copy of // its argument, and finally invalidates its argument. // Callbacks are moved to the current object; void move(GenericVector* from); // Read/Write the array to a file. This does _NOT_ read/write the callbacks. // The callback given must be permanent since they will be called more than // once. The given callback will be deleted at the end. // If the callbacks are NULL, then the data is simply read/written using // fread (and swapping)/fwrite. // Returns false on error or if the callback returns false. // DEPRECATED. Use [De]Serialize[Classes] instead. bool write(FILE* f, TessResultCallback2* cb) const; bool read(FILE* f, TessResultCallback3* cb, bool swap); // Writes a vector of simple types to the given file. Assumes that bitwise // read/write of T will work. Returns false in case of error. bool Serialize(FILE* fp) const; // Reads a vector of simple types from the given file. Assumes that bitwise // read/write will work with ReverseN according to sizeof(T). // Returns false in case of error. // If swap is true, assumes a big/little-endian swap is needed. bool DeSerialize(bool swap, FILE* fp); // Writes a vector of classes to the given file. Assumes the existence of // bool T::Serialize(FILE* fp) const that returns false in case of error. // Returns false in case of error. bool SerializeClasses(FILE* fp) const; // Reads a vector of classes from the given file. Assumes the existence of // bool T::Deserialize(bool swap, FILE* fp) that returns false in case of // error. Also needs T::T() and T::T(constT&), as init_to_size is used in // this function. Returns false in case of error. // If swap is true, assumes a big/little-endian swap is needed. bool DeSerializeClasses(bool swap, FILE* fp); // Allocates a new array of double the current_size, copies over the // information from data to the new location, deletes data and returns // the pointed to the new larger array. // This function uses memcpy to copy the data, instead of invoking // operator=() for each element like double_the_size() does. static T *double_the_size_memcpy(int current_size, T *data) { T *data_new = new T[current_size * 2]; memcpy(data_new, data, sizeof(T) * current_size); delete[] data; return data_new; } // Sorts the members of this vector using the less than comparator (cmp_lt), // which compares the values. Useful for GenericVectors to primitive types. // Will not work so great for pointers (unless you just want to sort some // pointers). You need to provide a specialization to sort_cmp to use // your type. void sort(); // Sort the array into the order defined by the qsort function comparator. // The comparator function is as defined by qsort, ie. it receives pointers // to two Ts and returns negative if the first element is to appear earlier // in the result and positive if it is to appear later, with 0 for equal. void sort(int (*comparator)(const void*, const void*)) { qsort(data_, size_used_, sizeof(*data_), comparator); } // Searches the array (assuming sorted in ascending order, using sort()) for // an element equal to target and returns true if it is present. // Use binary_search to get the index of target, or its nearest candidate. bool bool_binary_search(const T& target) const { int index = binary_search(target); if (index >= size_used_) return false; return data_[index] == target; } // Searches the array (assuming sorted in ascending order, using sort()) for // an element equal to target and returns the index of the best candidate. // The return value is conceptually the largest index i such that // data_[i] <= target or 0 if target < the whole vector. // NOTE that this function uses operator> so really the return value is // the largest index i such that data_[i] > target is false. int binary_search(const T& target) const { int bottom = 0; int top = size_used_; do { int middle = (bottom + top) / 2; if (data_[middle] > target) top = middle; else bottom = middle; } while (top - bottom > 1); return bottom; } // Compact the vector by deleting elements using operator!= on basic types. // The vector must be sorted. void compact_sorted() { if (size_used_ == 0) return; // First element is in no matter what, hence the i = 1. int last_write = 0; for (int i = 1; i < size_used_; ++i) { // Finds next unique item and writes it. if (data_[last_write] != data_[i]) data_[++last_write] = data_[i]; } // last_write is the index of a valid data cell, so add 1. size_used_ = last_write + 1; } // Compact the vector by deleting elements for which delete_cb returns // true. delete_cb is a permanent callback and will be deleted. void compact(TessResultCallback1* delete_cb) { int new_size = 0; int old_index = 0; // Until the callback returns true, the elements stay the same. while (old_index < size_used_ && !delete_cb->Run(old_index++)) ++new_size; // Now just copy anything else that gets false from delete_cb. for (; old_index < size_used_; ++old_index) { if (!delete_cb->Run(old_index)) { data_[new_size++] = data_[old_index]; } } size_used_ = new_size; delete delete_cb; } T dot_product(const GenericVector& other) const { T result = static_cast(0); for (int i = MIN(size_used_, other.size_used_) - 1; i >= 0; --i) result += data_[i] * other.data_[i]; return result; } // Returns the index of what would be the target_index_th item in the array // if the members were sorted, without actually sorting. Members are // shuffled around, but it takes O(n) time. // NOTE: uses operator< and operator== on the members. int choose_nth_item(int target_index) { // Make sure target_index is legal. if (target_index < 0) target_index = 0; // ensure legal else if (target_index >= size_used_) target_index = size_used_ - 1; unsigned int seed = 1; return choose_nth_item(target_index, 0, size_used_, &seed); } // Swaps the elements with the given indices. void swap(int index1, int index2) { if (index1 != index2) { T tmp = data_[index1]; data_[index1] = data_[index2]; data_[index2] = tmp; } } protected: // Internal recursive version of choose_nth_item. int choose_nth_item(int target_index, int start, int end, unsigned int* seed); // Init the object, allocating size memory. void init(int size); // We are assuming that the object generally placed in thie // vector are small enough that for efficiency it makes sence // to start with a larger initial size. static const int kDefaultVectorSize = 4; inT32 size_used_; inT32 size_reserved_; T* data_; TessCallback1* clear_cb_; // Mutable because Run method is not const mutable TessResultCallback2* compare_cb_; }; namespace tesseract { // Function to read a GenericVector from a whole file. // Returns false on failure. typedef bool (*FileReader)(const STRING& filename, GenericVector* data); // Function to write a GenericVector to a whole file. // Returns false on failure. typedef bool (*FileWriter)(const GenericVector& data, const STRING& filename); // The default FileReader loads the whole file into the vector of char, // returning false on error. inline bool LoadDataFromFile(const STRING& filename, GenericVector* data) { FILE* fp = fopen(filename.string(), "rb"); if (fp == NULL) return false; fseek(fp, 0, SEEK_END); size_t size = ftell(fp); fseek(fp, 0, SEEK_SET); // Pad with a 0, just in case we treat the result as a string. data->init_to_size(size + 1, 0); bool result = fread(&(*data)[0], 1, size, fp) == size; fclose(fp); return result; } // The default FileWriter writes the vector of char to the filename file, // returning false on error. inline bool SaveDataToFile(const GenericVector& data, const STRING& filename) { FILE* fp = fopen(filename.string(), "wb"); if (fp == NULL) return false; bool result = fwrite(&data[0], 1, data.size(), fp) == data.size(); fclose(fp); return result; } template bool cmp_eq(T const & t1, T const & t2) { return t1 == t2; } // Used by sort() // return < 0 if t1 < t2 // return 0 if t1 == t2 // return > 0 if t1 > t2 template int sort_cmp(const void* t1, const void* t2) { const T* a = static_cast (t1); const T* b = static_cast (t2); if (*a < *b) { return -1; } else if (*b < *a) { return 1; } else { return 0; } } // Used by PointerVector::sort() // return < 0 if t1 < t2 // return 0 if t1 == t2 // return > 0 if t1 > t2 template int sort_ptr_cmp(const void* t1, const void* t2) { const T* a = *reinterpret_cast(t1); const T* b = *reinterpret_cast(t2); if (*a < *b) { return -1; } else if (*b < *a) { return 1; } else { return 0; } } // Subclass for a vector of pointers. Use in preference to GenericVector // as it provides automatic deletion and correct serialization, with the // corollary that all copy operations are deep copies of the pointed-to objects. template class PointerVector : public GenericVector { public: PointerVector() : GenericVector() { } explicit PointerVector(int size) : GenericVector(size) { } ~PointerVector() { // Clear must be called here, even though it is called again by the base, // as the base will call the wrong clear. clear(); } // Copy must be deep, as the pointers will be automatically deleted on // destruction. PointerVector(const PointerVector& other) { this->init(other.size()); this->operator+=(other); } PointerVector& operator+=(const PointerVector& other) { this->reserve(this->size_used_ + other.size_used_); for (int i = 0; i < other.size(); ++i) { this->push_back(new T(*other.data_[i])); } return *this; } PointerVector& operator=(const PointerVector& other) { this->truncate(0); this->operator+=(other); return *this; } // Removes an element at the given index and // shifts the remaining elements to the left. void remove(int index) { delete GenericVector::data_[index]; GenericVector::remove(index); } // Truncates the array to the given size by removing the end. // If the current size is less, the array is not expanded. void truncate(int size) { for (int i = size; i < GenericVector::size_used_; ++i) delete GenericVector::data_[i]; GenericVector::truncate(size); } // Compact the vector by deleting elements for which delete_cb returns // true. delete_cb is a permanent callback and will be deleted. void compact(TessResultCallback1* delete_cb) { int new_size = 0; int old_index = 0; // Until the callback returns true, the elements stay the same. while (old_index < GenericVector::size_used_ && !delete_cb->Run(GenericVector::data_[old_index++])) ++new_size; // Now just copy anything else that gets false from delete_cb. for (; old_index < GenericVector::size_used_; ++old_index) { if (!delete_cb->Run(GenericVector::data_[old_index])) { GenericVector::data_[new_size++] = GenericVector::data_[old_index]; } else { delete GenericVector::data_[old_index]; } } GenericVector::size_used_ = new_size; delete delete_cb; } // Clear the array, calling the clear callback function if any. // All the owned callbacks are also deleted. // If you don't want the callbacks to be deleted, before calling clear, set // the callback to NULL. void clear() { GenericVector::delete_data_pointers(); GenericVector::clear(); } // Writes a vector of simple types to the given file. Assumes that bitwise // read/write of T will work. Returns false in case of error. bool Serialize(FILE* fp) const { inT32 used = GenericVector::size_used_; if (fwrite(&used, sizeof(used), 1, fp) != 1) return false; for (int i = 0; i < used; ++i) { inT8 non_null = GenericVector::data_[i] != NULL; if (fwrite(&non_null, sizeof(non_null), 1, fp) != 1) return false; if (non_null && !GenericVector::data_[i]->Serialize(fp)) return false; } return true; } // Reads a vector of simple types from the given file. Assumes that bitwise // read/write will work with ReverseN according to sizeof(T). // Also needs T::T(), as new T is used in this function. // Returns false in case of error. // If swap is true, assumes a big/little-endian swap is needed. bool DeSerialize(bool swap, FILE* fp) { inT32 reserved; if (fread(&reserved, sizeof(reserved), 1, fp) != 1) return false; if (swap) Reverse32(&reserved); GenericVector::reserve(reserved); for (int i = 0; i < reserved; ++i) { inT8 non_null; if (fread(&non_null, sizeof(non_null), 1, fp) != 1) return false; T* item = NULL; if (non_null) { item = new T; if (!item->DeSerialize(swap, fp)) return false; } this->push_back(item); } return true; } // Sorts the items pointed to by the members of this vector using // t::operator<(). void sort() { sort(&sort_ptr_cmp); } }; } // namespace tesseract // A useful vector that uses operator== to do comparisons. template class GenericVectorEqEq : public GenericVector { public: GenericVectorEqEq() { GenericVector::set_compare_callback( NewPermanentTessCallback(tesseract::cmp_eq)); } GenericVectorEqEq(int size) : GenericVector(size) { GenericVector::set_compare_callback( NewPermanentTessCallback(tesseract::cmp_eq)); } }; template void GenericVector::init(int size) { size_used_ = 0; size_reserved_ = 0; data_ = 0; clear_cb_ = 0; compare_cb_ = 0; reserve(size); } template GenericVector::~GenericVector() { clear(); } // Reserve some memory. If the internal array contains elements, they are // copied. template void GenericVector::reserve(int size) { if (size_reserved_ >= size || size <= 0) return; T* new_array = new T[size]; for (int i = 0; i < size_used_; ++i) new_array[i] = data_[i]; if (data_ != NULL) delete[] data_; data_ = new_array; size_reserved_ = size; } template void GenericVector::double_the_size() { if (size_reserved_ == 0) { reserve(kDefaultVectorSize); } else { reserve(2 * size_reserved_); } } // Resizes to size and sets all values to t. template void GenericVector::init_to_size(int size, T t) { reserve(size); size_used_ = size; for (int i = 0; i < size; ++i) data_[i] = t; } // Return the object from an index. template T &GenericVector::get(int index) const { ASSERT_HOST(index >= 0 && index < size_used_); return data_[index]; } template T &GenericVector::operator[](int index) const { assert(index >= 0 && index < size_used_); return data_[index]; } template T &GenericVector::back() const { ASSERT_HOST(size_used_ > 0); return data_[size_used_ - 1]; } // Returns the last object and removes it. template T GenericVector::pop_back() { ASSERT_HOST(size_used_ > 0); return data_[--size_used_]; } // Return the object from an index. template void GenericVector::set(T t, int index) { ASSERT_HOST(index >= 0 && index < size_used_); data_[index] = t; } // Shifts the rest of the elements to the right to make // space for the new elements and inserts the given element // at the specified index. template void GenericVector::insert(T t, int index) { ASSERT_HOST(index >= 0 && index <= size_used_); if (size_reserved_ == size_used_) double_the_size(); for (int i = size_used_; i > index; --i) { data_[i] = data_[i-1]; } data_[index] = t; size_used_++; } // Removes an element at the given index and // shifts the remaining elements to the left. template void GenericVector::remove(int index) { ASSERT_HOST(index >= 0 && index < size_used_); for (int i = index; i < size_used_ - 1; ++i) { data_[i] = data_[i+1]; } size_used_--; } // Return true if the index is valindex template T GenericVector::contains_index(int index) const { return index >= 0 && index < size_used_; } // Return the index of the T object. template int GenericVector::get_index(T object) const { for (int i = 0; i < size_used_; ++i) { ASSERT_HOST(compare_cb_ != NULL); if (compare_cb_->Run(object, data_[i])) return i; } return -1; } // Return true if T is in the array template bool GenericVector::contains(T object) const { return get_index(object) != -1; } // Add an element in the array template int GenericVector::push_back(T object) { int index = 0; if (size_used_ == size_reserved_) double_the_size(); index = size_used_++; data_[index] = object; return index; } template int GenericVector::push_back_new(T object) { int index = get_index(object); if (index >= 0) return index; return push_back(object); } // Add an element in the array (front) template int GenericVector::push_front(T object) { if (size_used_ == size_reserved_) double_the_size(); for (int i = size_used_; i > 0; --i) data_[i] = data_[i-1]; data_[0] = object; ++size_used_; return 0; } template void GenericVector::operator+=(T t) { push_back(t); } template GenericVector &GenericVector::operator+=(const GenericVector& other) { this->reserve(size_used_ + other.size_used_); for (int i = 0; i < other.size(); ++i) { this->operator+=(other.data_[i]); } return *this; } template GenericVector &GenericVector::operator=(const GenericVector& other) { this->truncate(0); this->operator+=(other); return *this; } // Add a callback to be called to delete the elements when the array took // their ownership. template void GenericVector::set_clear_callback(TessCallback1* cb) { clear_cb_ = cb; } // Add a callback to be called to delete the elements when the array took // their ownership. template void GenericVector::set_compare_callback( TessResultCallback2* cb) { compare_cb_ = cb; } // Clear the array, calling the callback function if any. template void GenericVector::clear() { if (size_reserved_ > 0) { if (clear_cb_ != NULL) for (int i = 0; i < size_used_; ++i) clear_cb_->Run(data_[i]); delete[] data_; data_ = NULL; size_used_ = 0; size_reserved_ = 0; } if (clear_cb_ != NULL) { delete clear_cb_; clear_cb_ = NULL; } if (compare_cb_ != NULL) { delete compare_cb_; compare_cb_ = NULL; } } template void GenericVector::delete_data_pointers() { for (int i = 0; i < size_used_; ++i) if (data_[i]) { delete data_[i]; } } template bool GenericVector::write( FILE* f, TessResultCallback2* cb) const { if (fwrite(&size_reserved_, sizeof(size_reserved_), 1, f) != 1) return false; if (fwrite(&size_used_, sizeof(size_used_), 1, f) != 1) return false; if (cb != NULL) { for (int i = 0; i < size_used_; ++i) { if (!cb->Run(f, data_[i])) { delete cb; return false; } } delete cb; } else { if (fwrite(data_, sizeof(T), size_used_, f) != size_used_) return false; } return true; } template bool GenericVector::read(FILE* f, TessResultCallback3* cb, bool swap) { inT32 reserved; if (fread(&reserved, sizeof(reserved), 1, f) != 1) return false; if (swap) Reverse32(&reserved); reserve(reserved); if (fread(&size_used_, sizeof(size_used_), 1, f) != 1) return false; if (swap) Reverse32(&size_used_); if (cb != NULL) { for (int i = 0; i < size_used_; ++i) { if (!cb->Run(f, data_ + i, swap)) { delete cb; return false; } } delete cb; } else { if (fread(data_, sizeof(T), size_used_, f) != size_used_) return false; if (swap) { for (int i = 0; i < size_used_; ++i) ReverseN(&data_[i], sizeof(T)); } } return true; } // Writes a vector of simple types to the given file. Assumes that bitwise // read/write of T will work. Returns false in case of error. template bool GenericVector::Serialize(FILE* fp) const { if (fwrite(&size_used_, sizeof(size_used_), 1, fp) != 1) return false; if (fwrite(data_, sizeof(*data_), size_used_, fp) != size_used_) return false; return true; } // Reads a vector of simple types from the given file. Assumes that bitwise // read/write will work with ReverseN according to sizeof(T). // Returns false in case of error. // If swap is true, assumes a big/little-endian swap is needed. template bool GenericVector::DeSerialize(bool swap, FILE* fp) { inT32 reserved; if (fread(&reserved, sizeof(reserved), 1, fp) != 1) return false; if (swap) Reverse32(&reserved); reserve(reserved); size_used_ = reserved; if (fread(data_, sizeof(T), size_used_, fp) != size_used_) return false; if (swap) { for (int i = 0; i < size_used_; ++i) ReverseN(&data_[i], sizeof(data_[i])); } return true; } // Writes a vector of classes to the given file. Assumes the existence of // bool T::Serialize(FILE* fp) const that returns false in case of error. // Returns false in case of error. template bool GenericVector::SerializeClasses(FILE* fp) const { if (fwrite(&size_used_, sizeof(size_used_), 1, fp) != 1) return false; for (int i = 0; i < size_used_; ++i) { if (!data_[i].Serialize(fp)) return false; } return true; } // Reads a vector of classes from the given file. Assumes the existence of // bool T::Deserialize(bool swap, FILE* fp) that returns false in case of // error. Alse needs T::T() and T::T(constT&), as init_to_size is used in // this function. Returns false in case of error. // If swap is true, assumes a big/little-endian swap is needed. template bool GenericVector::DeSerializeClasses(bool swap, FILE* fp) { uinT32 reserved; if (fread(&reserved, sizeof(reserved), 1, fp) != 1) return false; if (swap) Reverse32(&reserved); T empty; init_to_size(reserved, empty); for (int i = 0; i < reserved; ++i) { if (!data_[i].DeSerialize(swap, fp)) return false; } return true; } // This method clear the current object, then, does a shallow copy of // its argument, and finally invalidates its argument. template void GenericVector::move(GenericVector* from) { this->clear(); this->data_ = from->data_; this->size_reserved_ = from->size_reserved_; this->size_used_ = from->size_used_; this->compare_cb_ = from->compare_cb_; this->clear_cb_ = from->clear_cb_; from->data_ = NULL; from->clear_cb_ = NULL; from->compare_cb_ = NULL; from->size_used_ = 0; from->size_reserved_ = 0; } template void GenericVector::sort() { sort(&tesseract::sort_cmp); } // Internal recursive version of choose_nth_item. // The algorithm used comes from "Algorithms" by Sedgewick: // http://books.google.com/books/about/Algorithms.html?id=idUdqdDXqnAC // The principle is to choose a random pivot, and move everything less than // the pivot to its left, and everything greater than the pivot to the end // of the array, then recurse on the part that contains the desired index, or // just return the answer if it is in the equal section in the middle. // The random pivot guarantees average linear time for the same reason that // n times vector::push_back takes linear time on average. // target_index, start and and end are all indices into the full array. // Seed is a seed for rand_r for thread safety purposes. Its value is // unimportant as the random numbers do not affect the result except // between equal answers. template int GenericVector::choose_nth_item(int target_index, int start, int end, unsigned int* seed) { // Number of elements to process. int num_elements = end - start; // Trivial cases. if (num_elements <= 1) return start; if (num_elements == 2) { if (data_[start] < data_[start + 1]) { return target_index > start ? start + 1 : start; } else { return target_index > start ? start : start + 1; } } // Place the pivot at start. #ifdef _MSC_VER // TODO(zdenop): check this srand(*seed); #define rand_r(seed) rand() #endif // _MSC_VER int pivot = rand_r(seed) % num_elements + start; swap(pivot, start); // The invariant condition here is that items [start, next_lesser) are less // than the pivot (which is at index next_lesser) and items // [prev_greater, end) are greater than the pivot, with items // [next_lesser, prev_greater) being equal to the pivot. int next_lesser = start; int prev_greater = end; for (int next_sample = start + 1; next_sample < prev_greater;) { if (data_[next_sample] < data_[next_lesser]) { swap(next_lesser++, next_sample++); } else if (data_[next_sample] == data_[next_lesser]) { ++next_sample; } else { swap(--prev_greater, next_sample); } } // Now the invariant is set up, we recurse on just the section that contains // the desired index. if (target_index < next_lesser) return choose_nth_item(target_index, start, next_lesser, seed); else if (target_index < prev_greater) return next_lesser; // In equal bracket. else return choose_nth_item(target_index, prev_greater, end, seed); } #endif // TESSERACT_CCUTIL_GENERICVECTOR_H_