diff --git a/applications/test/sort/Test-sortList.C b/applications/test/sort/Test-sortList.C index 1fbfcfcf39cd6b14e0f3b8e805bc0ce734e002ce..8b208f4f50fce9f0dfd38470b89e4229e535974b 100644 --- a/applications/test/sort/Test-sortList.C +++ b/applications/test/sort/Test-sortList.C @@ -3,7 +3,7 @@ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | \\ / A nd | Copyright (C) 2011 OpenFOAM Foundation - \\/ M anipulation | + \\/ M anipulation | Copyright (C) 2017 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -27,6 +27,8 @@ Description #include "SortableList.H" #include "ListOps.H" +#include "HashSet.H" +#include "stringOps.H" using namespace Foam; @@ -175,6 +177,142 @@ int main(int argc, char *argv[]) Info<< "flat = " << values << endl; } + // Sort strings + { + HashSet<string> hashed + { + "2.txt", + "05.txt", + "15.txt", + "other.bak04", + "other.bak1", + "file1.txt", + "file10.txt", + "file2.txt", + "file100.txt", + "file.txt", + "file011.txt", + "file15.txt", + "file0009.txt", + "abcd.txt", + + // Some regular processor directories + "processor0", + "processor1", + "processor9", + "processor10", + "processor11", + "processor20", + "processor21", + "processor35", + "processors", + + // Aggregate processor directories + "processor0-31", + "processor32-63", + "processor64-95", + "processor96-127", + "processor128-159", + "processor160-191", + "processor192-223", + "processor224-255", + }; + + Info<< nl << "Test string sorting" << nl << endl; + + // Using hash toc + if (true) + { + Info<< "Unsorted" << hashed.toc() << endl; + Info<< "sortedToc" << hashed.sortedToc() << endl; + Info<< "natural" + << hashed.sortedToc(stringOps::natural_sort()) << endl; + + Info<< "reverse natural" + << hashed.sortedToc(stringOps::natural_sort::reverse()) + << endl; + } + + // Normal list + if (true) + { + labelList order; + + List<string> strings(hashed.toc()); + Info<< nl << "stringList:" << strings << endl; + + sort(strings); + Info<< "normal sort:" << strings << endl; + + shuffle(strings); + sort(strings, stringOps::natural_sort()); + Info<< "natural sort:" << strings << endl; + + shuffle(strings); + sort(strings, stringOps::natural_sort::reverse()); + Info<< "reverse natural:" << strings << endl; + + strings = hashed.toc(); + + Info<< nl << "test sorted order" << endl; + Info<< nl << "list:" << strings << endl; + + sortedOrder(strings, order); + Info<< "sortedOrder:" << flatOutput(order) << endl; + + shuffle(strings); + sort(strings, stringOps::natural_sort()); + Info<< "reverse natural:" << strings << endl; + + shuffle(strings); + sort(strings, stringOps::natural_sort::reverse()); + Info<< "reverse natural:" << strings << endl; + + sortedOrder + ( + strings, + order, + stringOps::natural_sort::less<string>(strings) + ); + Info<< "natural sortedOrder: " << flatOutput(order) << endl; + } + + // SortableList + if (false) + { + SortableList<string> sortable; + Info<< nl << "Testing sortable list"; + + // Assign to ensure list is initially unsorted + sortable = hashed.toc(); + Info<< nl << "input:" << sortable << endl; + + sortable.sort(); + Info<< nl << "normal:" << sortable << endl; + + // This is still a bother (looks fairly ugly) + // so not implemented for now + + /// // Assign to ensure list is initially unsorted + /// sortable = hashed.toc(); + /// sortable.sort + /// ( + /// stringOps::natural_sort::less<string>(sortable) + /// ); + /// Info<< nl << "natural:" << sortable << endl; + + /// // Assign to ensure list is initially unsorted + /// sortable = hashed.toc(); + /// sortable.sort + /// ( + /// stringOps::natural_sort::greater<string>(sortable) + /// ); + /// Info<< nl << "natural:" << sortable << endl; + } + + } + + Info<< "\nEnd\n" << endl; return 0; diff --git a/src/OpenFOAM/Make/files b/src/OpenFOAM/Make/files index acf60a63d05b16b7feb2f6c9bb93b92d988b53d4..4a6adb98a168b649b741bef57c8856c2fc0b3c4e 100644 --- a/src/OpenFOAM/Make/files +++ b/src/OpenFOAM/Make/files @@ -114,6 +114,7 @@ $(strings)/wordRe/wordRe.C $(strings)/wordRes/wordRes.C $(strings)/lists/hashedWordList.C $(strings)/stringOps/stringOps.C +$(strings)/stringOps/stringOpsSort.C $(strings)/parsing/parsing.C ops = primitives/ops diff --git a/src/OpenFOAM/containers/Dictionaries/DictionaryBase/DictionaryBase.C b/src/OpenFOAM/containers/Dictionaries/DictionaryBase/DictionaryBase.C index 60b390223c2651a0d3e4d126582939ea16d382a5..2f95d40f713993a5b5ba4888845696ab0b68e738 100644 --- a/src/OpenFOAM/containers/Dictionaries/DictionaryBase/DictionaryBase.C +++ b/src/OpenFOAM/containers/Dictionaries/DictionaryBase/DictionaryBase.C @@ -189,6 +189,17 @@ Foam::wordList Foam::DictionaryBase<IDLListType, T>::sortedToc() const } +template<class IDLListType, class T> +template<class Compare> +Foam::wordList Foam::DictionaryBase<IDLListType, T>::sortedToc +( + const Compare& comp +) const +{ + return hashedTs_.sortedToc(comp); +} + + template<class IDLListType, class T> void Foam::DictionaryBase<IDLListType, T>::insert(const word& keyword, T* tPtr) { diff --git a/src/OpenFOAM/containers/Dictionaries/DictionaryBase/DictionaryBase.H b/src/OpenFOAM/containers/Dictionaries/DictionaryBase/DictionaryBase.H index f4d864d82c746d51807a8ed2f82e7027f8940890..8c48737427298aadfd2339622de74f2f1d790ef3 100644 --- a/src/OpenFOAM/containers/Dictionaries/DictionaryBase/DictionaryBase.H +++ b/src/OpenFOAM/containers/Dictionaries/DictionaryBase/DictionaryBase.H @@ -131,6 +131,10 @@ public: //- Return the table of contents as a sorted list wordList sortedToc() const; + //- Return table of contents sorted using the specified comparator + template<class Compare> + wordList sortedToc(const Compare& comp) const; + // Editing diff --git a/src/OpenFOAM/containers/HashTables/HashTable/HashTable.C b/src/OpenFOAM/containers/HashTables/HashTable/HashTable.C index 1f209ef350ec3c075ed0ed2ae7dc7a36dab472ab..1c1d7b43606376853bf77e96d9c5ec3da072e15d 100644 --- a/src/OpenFOAM/containers/HashTables/HashTable/HashTable.C +++ b/src/OpenFOAM/containers/HashTables/HashTable/HashTable.C @@ -268,13 +268,27 @@ Foam::List<Key> Foam::HashTable<T, Key, Hash>::toc() const template<class T, class Key, class Hash> Foam::List<Key> Foam::HashTable<T, Key, Hash>::sortedToc() const { - List<Key> keyLst = this->toc(); + List<Key> keyLst(this->toc()); Foam::sort(keyLst); return keyLst; } +template<class T, class Key, class Hash> +template<class Compare> +Foam::List<Key> Foam::HashTable<T, Key, Hash>::sortedToc +( + const Compare& comp +) const +{ + List<Key> keyLst(this->toc()); + Foam::sort(keyLst, comp); + + return keyLst; +} + + template<class T, class Key, class Hash> template<class UnaryPredicate> Foam::List<Key> Foam::HashTable<T, Key, Hash>::tocKeys diff --git a/src/OpenFOAM/containers/HashTables/HashTable/HashTable.H b/src/OpenFOAM/containers/HashTables/HashTable/HashTable.H index aa848e475c4dc7d2dbc7fc942065bf066345edad..f9dda76f9c81515f10ae7727702fecfde9e2fef8 100644 --- a/src/OpenFOAM/containers/HashTables/HashTable/HashTable.H +++ b/src/OpenFOAM/containers/HashTables/HashTable/HashTable.H @@ -347,6 +347,10 @@ public: //- Return the table of contents as a sorted list List<Key> sortedToc() const; + //- Return table of contents sorted using the specified comparator + template<class Compare> + List<Key> sortedToc(const Compare& comp) const; + //- Return the sorted table of contents with keys that satisfy // the unary predicate, optionally with inverted logic. template<class UnaryPredicate> diff --git a/src/OpenFOAM/containers/Lists/ListOps/ListOps.H b/src/OpenFOAM/containers/Lists/ListOps/ListOps.H index d7c93bef0235e0cb1198e65bdb689f908617e015..c3a6406da9e6f17cd9f0fc7c2a33f301727177e8 100644 --- a/src/OpenFOAM/containers/Lists/ListOps/ListOps.H +++ b/src/OpenFOAM/containers/Lists/ListOps/ListOps.H @@ -110,24 +110,44 @@ void inplaceMapKey(const labelUList& oldToNew, Container& lst); template<class T> void sortedOrder(const UList<T>& lst, labelList& order); -template<class T, class Cmp> -void sortedOrder(const UList<T>& lst, labelList& order, const Cmp& cmp); +//- Sort using specified list compare predicate +template<class T, class ListComparePredicate> +void sortedOrder +( + const UList<T>& lst, + labelList& order, + const ListComparePredicate& comp +); //- Generate (sorted) indices corresponding to duplicate list values template<class T> void duplicateOrder(const UList<T>& lst, labelList& order); -template<class T, class Cmp> -void duplicateOrder(const UList<T>& lst, labelList& order, const Cmp& cmp); +//- Generate (sorted) indices corresponding to duplicate list values +// sort using specified list compare predicate +template<class T, class ListComparePredicate> +void duplicateOrder +( + const UList<T>& lst, + labelList& order, + const ListComparePredicate& comp +); //- Generate (sorted) indices corresponding to unique list values template<class T> void uniqueOrder(const UList<T>& lst, labelList& order); -template<class T, class Cmp> -void uniqueOrder(const UList<T>& lst, labelList& order, const Cmp& cmp); +//- Generate (sorted) indices corresponding to unique list values +// sort using specified list compare predicate +template<class T, class ListComparePredicate> +void uniqueOrder +( + const UList<T>& lst, + labelList& order, + const ListComparePredicate& comp +); //- Inplace sorting and removal of duplicates. @@ -137,8 +157,12 @@ void inplaceUniqueSort(ListType& lst); //- Inplace sorting and removal of duplicates. // Do not use FixedList for the input list, since it doesn't resize. -template<class ListType, class Cmp> -void inplaceUniqueSort(ListType& lst, const Cmp& cmp); +template<class ListType, class ListComparePredicate> +void inplaceUniqueSort +( + ListType& lst, + const ListComparePredicate& comp +); //- Extract elements of List when select is a certain value. diff --git a/src/OpenFOAM/containers/Lists/ListOps/ListOpsTemplates.C b/src/OpenFOAM/containers/Lists/ListOps/ListOpsTemplates.C index 641aa4ade0eeb4d8e811bfcaeb5b0d604452ce10..a55b8c714cdd46d1c944cba14f20c6e08054ce29 100644 --- a/src/OpenFOAM/containers/Lists/ListOps/ListOpsTemplates.C +++ b/src/OpenFOAM/containers/Lists/ListOps/ListOpsTemplates.C @@ -211,27 +211,30 @@ void Foam::sortedOrder } -template<class T, class Cmp> +template<class T, class ListComparePredicate> void Foam::sortedOrder ( const UList<T>& lst, labelList& order, - const Cmp& cmp + const ListComparePredicate& comp ) { + const label len = lst.size(); + // list lengths must be identical - if (order.size() != lst.size()) + if (order.size() != len) { - // avoid copying any elements, they are overwritten anyhow + // Avoid copying any elements, they are overwritten anyhow order.clear(); - order.setSize(lst.size()); + order.setSize(len); } - forAll(order, elemI) + for (label i=0; i<len; ++i) { - order[elemI] = elemI; + order[i] = i; // identity } - Foam::stableSort(order, cmp); + + Foam::stableSort(order, comp); } @@ -246,12 +249,12 @@ void Foam::duplicateOrder } -template<class T, class Cmp> +template<class T, class ListComparePredicate> void Foam::duplicateOrder ( const UList<T>& lst, labelList& order, - const Cmp& cmp + const ListComparePredicate& comp ) { if (lst.size() < 2) @@ -260,7 +263,7 @@ void Foam::duplicateOrder return; } - sortedOrder(lst, order, cmp); + sortedOrder(lst, order, comp); const label last = (order.size()-1); label n = 0; @@ -286,15 +289,15 @@ void Foam::uniqueOrder } -template<class T, class Cmp> +template<class T, class ListComparePredicate> void Foam::uniqueOrder ( const UList<T>& lst, labelList& order, - const Cmp& cmp + const ListComparePredicate& comp ) { - sortedOrder(lst, order, cmp); + sortedOrder(lst, order, comp); if (order.size() > 1) { @@ -324,18 +327,24 @@ void Foam::inplaceUniqueSort(ListType& lst) } -template<class ListType, class Cmp> -void Foam::inplaceUniqueSort(ListType& lst, const Cmp& cmp) +template<class ListType, class ListComparePredicate> +void Foam::inplaceUniqueSort +( + ListType& lst, + const ListComparePredicate& comp +) { labelList order; - uniqueOrder(lst, order, cmp); + uniqueOrder(lst, order, comp); + + const label len = order.size(); - ListType newLst(order.size()); - newLst.setSize(order.size()); // Consistent sizing (eg, DynamicList) + ListType newLst(len); + newLst.setSize(len); // Consistent sizing (eg, DynamicList) - forAll(order, elemI) + for (label i=0; i<len; ++i) { - newLst[elemI] = lst[order[elemI]]; + newLst[i] = lst[order[i]]; } lst.transfer(newLst); diff --git a/src/OpenFOAM/containers/Lists/SortableList/SortableList.C b/src/OpenFOAM/containers/Lists/SortableList/SortableList.C index fac433d7d75795cf82415fabfd5d43a780b8b017..6d2d2698dca17ff64e380e4915f472deec870808 100644 --- a/src/OpenFOAM/containers/Lists/SortableList/SortableList.C +++ b/src/OpenFOAM/containers/Lists/SortableList/SortableList.C @@ -133,7 +133,7 @@ Foam::List<T>& Foam::SortableList<T>::shrink() template<class T> void Foam::SortableList<T>::sort() { - sortedOrder(*this, indices_); + Foam::sortedOrder(*this, indices_); List<T> lst(*this, indices_); // Copy with indices for mapping List<T>::transfer(lst); @@ -143,7 +143,7 @@ void Foam::SortableList<T>::sort() template<class T> void Foam::SortableList<T>::reverseSort() { - sortedOrder(*this, indices_, typename UList<T>::greater(*this)); + Foam::sortedOrder(*this, indices_, typename UList<T>::greater(*this)); List<T> lst(*this, indices_); // Copy with indices for mapping List<T>::transfer(lst); @@ -157,6 +157,7 @@ void Foam::SortableList<T>::swap(SortableList<T>& lst) indices_.swap(lst.indices_); } + template<class T> Foam::Xfer<Foam::List<T>> Foam::SortableList<T>::xfer() { diff --git a/src/OpenFOAM/containers/Lists/SortableList/SortableList.H b/src/OpenFOAM/containers/Lists/SortableList/SortableList.H index 8ef6aefa19796ec98a2eb0c1da0ba3eefa565345..083e69386f01603229095378eaf777f65426d589 100644 --- a/src/OpenFOAM/containers/Lists/SortableList/SortableList.H +++ b/src/OpenFOAM/containers/Lists/SortableList/SortableList.H @@ -119,8 +119,8 @@ public: //- Clear the indices and return a reference to the underlying List List<T>& shrink(); - //- (stable) sort the list (if changed after construction time) - // also resizes the indices as required + //- (stable) sort the list (if changed after construction time). + // Resizes the indices as required void sort(); //- Reverse (stable) sort the list diff --git a/src/OpenFOAM/containers/Lists/UList/UList.C b/src/OpenFOAM/containers/Lists/UList/UList.C index b87e524b5938e22181bf980178b744f9b13a17fe..9258fbf637a8d60ecb25a22757b41640bf79208c 100644 --- a/src/OpenFOAM/containers/Lists/UList/UList.C +++ b/src/OpenFOAM/containers/Lists/UList/UList.C @@ -293,10 +293,10 @@ void Foam::sort(UList<T>& a) } -template<class T, class Cmp> -void Foam::sort(UList<T>& a, const Cmp& cmp) +template<class T, class Compare> +void Foam::sort(UList<T>& a, const Compare& comp) { - std::sort(a.begin(), a.end(), cmp); + std::sort(a.begin(), a.end(), comp); } @@ -307,10 +307,10 @@ void Foam::stableSort(UList<T>& a) } -template<class T, class Cmp> -void Foam::stableSort(UList<T>& a, const Cmp& cmp) +template<class T, class Compare> +void Foam::stableSort(UList<T>& a, const Compare& comp) { - std::stable_sort(a.begin(), a.end(), cmp); + std::stable_sort(a.begin(), a.end(), comp); } diff --git a/src/OpenFOAM/containers/Lists/UList/UList.H b/src/OpenFOAM/containers/Lists/UList/UList.H index 5c7999e2294ab6131eb8f6af73bbe47c81cdec35..fcdbd7c0da0c9a15b3896a481db3b99dbe2e8d75 100644 --- a/src/OpenFOAM/containers/Lists/UList/UList.H +++ b/src/OpenFOAM/containers/Lists/UList/UList.H @@ -139,39 +139,35 @@ public: // Public classes - //- Less function class that can be used for sorting - class less + //- A list compare binary predicate for normal sort + struct less { - const UList<T>& values_; + const UList<T>& values; - public: - - less(const UList<T>& values) + less(const UList<T>& list) : - values_(values) + values(list) {} - bool operator()(const label a, const label b) + bool operator()(const label a, const label b) const { - return values_[a] < values_[b]; + return values[a] < values[b]; } }; - //- Greater function class that can be used for sorting - class greater + //- A list compare binary predicate for reverse sort + struct greater { - const UList<T>& values_; - - public: + const UList<T>& values; - greater(const UList<T>& values) + greater(const UList<T>& list) : - values_(values) + values(list) {} - bool operator()(const label a, const label b) + bool operator()(const label a, const label b) const { - return values_[a] > values_[b]; + return values[a] > values[b]; } }; @@ -487,14 +483,14 @@ public: template<class T> void sort(UList<T>& a); -template<class T, class Cmp> -void sort(UList<T>& a, const Cmp& cmp); +template<class T, class Compare> +void sort(UList<T>& a, const Compare& comp); template<class T> void stableSort(UList<T>& a); -template<class T, class Cmp> -void stableSort(UList<T>& a, const Cmp& cmp); +template<class T, class Compare> +void stableSort(UList<T>& a, const Compare& comp); template<class T> void shuffle(UList<T>& a); diff --git a/src/OpenFOAM/db/dictionary/dictionary.H b/src/OpenFOAM/db/dictionary/dictionary.H index fc34a32d991c09df2f8dfa6696761b61c9676172..f0c66b97a856272fdf046408ba576cc83b87f2fb 100644 --- a/src/OpenFOAM/db/dictionary/dictionary.H +++ b/src/OpenFOAM/db/dictionary/dictionary.H @@ -452,6 +452,10 @@ public: //- Return the sorted table of contents wordList sortedToc() const; + //- Return table of contents sorted using the specified comparator + template<class Compare> + wordList sortedToc(const Compare& comp) const; + //- Return the list of available keys or patterns List<keyType> keys(bool patterns = false) const; diff --git a/src/OpenFOAM/db/dictionary/dictionaryTemplates.C b/src/OpenFOAM/db/dictionary/dictionaryTemplates.C index b247dfe8122647d5ea367c06b24a51d99628705e..1e46e5da33bbcd9f195951425ddb5eede5f54e6a 100644 --- a/src/OpenFOAM/db/dictionary/dictionaryTemplates.C +++ b/src/OpenFOAM/db/dictionary/dictionaryTemplates.C @@ -28,6 +28,13 @@ License // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // +template<class Compare> +Foam::wordList Foam::dictionary::sortedToc(const Compare& comp) const +{ + return hashedEntries_.sortedToc(comp); +} + + template<class T> T Foam::dictionary::lookupType ( diff --git a/src/OpenFOAM/primitives/predicates/predicates.H b/src/OpenFOAM/primitives/predicates/predicates.H index 0d7d8539fa24394ddb2e743888df4ace222ad59c..a44b4eec7e2749ab83e1b396e0489aa967977b5c 100644 --- a/src/OpenFOAM/primitives/predicates/predicates.H +++ b/src/OpenFOAM/primitives/predicates/predicates.H @@ -49,37 +49,40 @@ namespace predicates Class always Declaration \*---------------------------------------------------------------------------*/ -//- Unary and binary predicates returning true, useful for templating. -class always +//- Unary and binary predicates that always return true, useful for templating. +struct always { -public: typedef always value_type; - //- Construct null + //- Null constructible inline always() {} - //- Evaluated as a bool - return true + //- Evaluated as a bool + // \return true inline operator bool() const { return true; } - //- Unary predicate returning true + //- Unary predicate + // \return true template<class T> inline bool operator()(const T&) const { return true; } - //- Binary predicate returning true + //- Binary predicate + // \return true template<class T1, class T2> inline bool operator()(const T1&, const T2&) const { return true; } - //- String match returning true + //- String match + // \return true inline bool match(const std::string&, bool literal=false) const { return true; @@ -91,37 +94,40 @@ public: Class never Declaration \*---------------------------------------------------------------------------*/ -//- Unary and binary predicates returning false, useful for templating. -class never +//- Unary and binary predicates that never return true, useful for templating. +struct never { -public: typedef never value_type; - //- Construct null + //- Null constructible inline never() {} - //- Evaluated as a bool - return false + //- Evaluated as a bool + // \return false inline operator bool() const { return false; } - //- Unary predicate returning false + //- Unary predicate + // \return false template<class T> inline bool operator()(const T&) const { return false; } - //- Binary predicate returning false + //- Binary predicate + // \return false template<class T1, class T2> inline bool operator()(const T1&, const T2&) const { return false; } - //- String match returning false + //- String match + // \return false inline bool match(const std::string&, bool literal=false) const { return false; diff --git a/src/OpenFOAM/primitives/strings/stringOps/stringOps.H b/src/OpenFOAM/primitives/strings/stringOps/stringOps.H index 92092f9d5207ddf0370d1b499bb1600892248b6c..0d84856f52db85ee9df5e972ee6b0e760a763c58 100644 --- a/src/OpenFOAM/primitives/strings/stringOps/stringOps.H +++ b/src/OpenFOAM/primitives/strings/stringOps/stringOps.H @@ -41,6 +41,7 @@ SourceFiles #include "word.H" #include "dictionary.H" #include "HashTable.H" +#include "stringOpsSort.H" // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // diff --git a/src/OpenFOAM/primitives/strings/stringOps/stringOpsSort.C b/src/OpenFOAM/primitives/strings/stringOps/stringOpsSort.C new file mode 100644 index 0000000000000000000000000000000000000000..aeeb29fa14b247400a24a8b566d5f01adae3746d --- /dev/null +++ b/src/OpenFOAM/primitives/strings/stringOps/stringOpsSort.C @@ -0,0 +1,325 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | Copyright (C) 2017 OpenCFD Ltd. + \\/ M anipulation | +------------------------------------------------------------------------------- +Changes for OpenFOAM + + Code cleanup, reduction, elimination of redundant code. +\*---------------------------------------------------------------------------*/ + +//======================================================================== +// Copyright (c) 1998-2010,2011 Free Software Foundation, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, distribute with modifications, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR +// THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// Except as contained in this notice, the name(s) of the above copyright +// holders shall not be used in advertising or otherwise to promote the +// sale, use or other dealings in this Software without prior written +// authorization. +//======================================================================== + +//======================================================================== +// Original Author: Jan-Marten Spit <jmspit@euronet.nl> +//======================================================================== + +#include "stringOpsSort.H" +#include <cctype> +#include <cstring> + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +// Local tweaks/preferences: +// +// [DIGITS_ALWAYS_FIRST] : as per original code +// This results in "file123.txt" sorting before "file.txt", which is +// inconsistent with what 'ls -v' produces +// - normally do not want this (Mark Olesen: Oct-2017) +#undef DIGITS_ALWAYS_FIRST + +// [IGNORE_LEADING_ZEROS] : as per original code +// This results in "file0005.txt" sorting before "file06.txt" +// -> normally want this (Mark Olesen: Oct-2017) +#define IGNORE_LEADING_ZEROS + +// [MANUAL_NUMCOMPARE] : handwritten code instead of strncmp +// The orignal code has a mix of strncmp for equality but handwritten code +// for greater-than/less-than. +// +// -> this does to be unneeded, rely on strncmp() return values +// (Mark Olesen: Oct-2017) +#undef MANUAL_NUMCOMPARE + +// [DEBUG_NATSTRCMP] : debug info to std::cerr (for development purposes only) +#undef DEBUG_NATSTRCMP + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +#ifdef DEBUG_NATSTRCMP +#include <iostream> + +template<class T> +static inline void debugPrint(const char* text, T item1, T item2) +{ + std::cerr << text << ": <" << item1 << "> <" << item2 << ">\n"; +} + +// Character sequences, end pointer is _inclusive_. +static inline void debugPrint +( + const char* b1, const char* e1, + const char* b2, const char* e2 +) +{ + std::cerr << "<"; + while (b1 < e1) { std::cerr << *b1++; } + std::cerr << *e1 << "> "; + + std::cerr << "<"; + while (b2 < e2) { std::cerr << *b2++; } + std::cerr << *e2 << ">\n"; +} +#endif + +#ifdef MANUAL_NUMCOMPARE +// Manual comparison of identical length (digit) sequences +static inline int manual_numcompare(const char* s1, const char* s2) +{ + while (*s1 && *s2) + { + const int cmp = (*s1 - *s2); + if (!cmp) return cmp; + + ++s1; ++s2; + } + return 0; // Length check done before in caller. +} +#endif + + +// ------------------------------------------------------------------------- // + +int Foam::stringOps::natstrcmp(const char* s1, const char* s2) +{ + #ifdef DEBUG_NATSTRCMP + debugPrint("natstrcmp", s1, s2); + #endif + + // States for state engine + enum stateType { SCAN, ALPHA, NUMERIC }; + + // Number of leading zeroes + unsigned zeros1 = 0; + unsigned zeros2 = 0; + + // Pointers to begin/end of integer sequences (without leading zeros) + const char* numbeg1 = nullptr; + const char* numbeg2 = nullptr; + const char* numend1 = nullptr; + const char* numend2 = nullptr; + + stateType state = SCAN; + + const char* p1 = s1; + const char* p2 = s2; + + while (*p1 && *p2) + { + // Bitmask for digits vs alpha + // - 0: neither are digits + // - 1: p1 is the only digit + // - 2: p2 is the only digit + // - 3: both p1 and p2 are digits + + const unsigned digitMask = + ((isdigit(*p2) ? 2:0) | (isdigit(*p1) ? 1:0)); + + switch (state) + { + case SCAN: + { + #ifdef DEBUG_NATSTRCMP + debugPrint("SCAN", *p1, *p2); + #endif + + switch (digitMask) + { + case 0: // (alpha,alpha) + { + state = ALPHA; + + if (*p1 == *p2) + { + ++p1; ++p2; + } + else + { + // Lexical compare + return (*p1 - *p2); + } + break; + } + + #ifdef DIGITS_ALWAYS_FIRST + case 0x1: // (digit,alpha) : digit < alpha + { + return -1; + break; + } + case 0x2: // (alpha,digit) : alpha > digit + { + return 1; + break; + } + #else /* DIGITS_ALWAYS_FIRST */ + case 0x1: // (digit,alpha) + case 0x2: // (alpha,digit) + { + // Lexical compare for digits/alpha + return (*p1 - *p2); + break; + } + #endif /* DIGITS_ALWAYS_FIRST */ + + default: // (digit,digit) + { + state = NUMERIC; + + #ifdef IGNORE_LEADING_ZEROS + if (!zeros1) // Start skip of leading zeros + { + while (*p1 == '0') { ++p1; ++zeros1; } + } + else + #endif + { + while (*p1 == '0') { ++p1; } + } + #ifdef IGNORE_LEADING_ZEROS + if (!zeros2) // Start skip of leading zeros + { + while (*p2 == '0') { ++p2; ++zeros2; } + } + else + #endif + { + while (*p2 == '0') { ++p2; } + } + + if (zeros1 == zeros2) + { + // Same number of zeros - so irrelevant + zeros1 = zeros2 = 0; + } + + if (!isdigit(*p1)) --p1; + if (!isdigit(*p2)) --p2; + + numbeg1 = numend1 = p1; + numbeg2 = numend2 = p2; + + break; + } + } + break; + } + + case ALPHA: + { + #ifdef DEBUG_NATSTRCMP + debugPrint("ALPHA", *p1, *p2); + #endif + + if (digitMask) + { + state = SCAN; + } + else // (alpha,alpha) + { + if (*p1 == *p2) + { + ++p1; ++p2; + } + else + { + // Lexical compare + return (*p1 - *p2); + } + } + break; + } + + case NUMERIC: + { + while (isdigit(*p1)) numend1 = p1++; + while (isdigit(*p2)) numend2 = p2++; + + #ifdef DEBUG_NATSTRCMP + debugPrint("NUMERIC", *p1, *p2); + debugPrint(numbeg1,numend1, numbeg2,numend2); + #endif + + // Length (minus 1) of each sequence + const size_t len1 = (numend1 - numbeg1); + const size_t len2 = (numend2 - numbeg2); + + if (len1 < len2) + { + return -1; + } + else if (len1 > len2) + { + return 1; + } + + // Same number of digits, leading zeros have been skipped. + // - so lexical and numerical compares are equivalent. + + const int cmp = strncmp(numbeg1, numbeg2, len1+1); + if (!cmp) + { + // Identical (digit) sequence - continue + state = SCAN; + } + else + { + #ifdef MANUAL_STRNCMP + return manual_numcompare(numbeg1, numbeg2); + #else + return cmp; + #endif + } + break; + } + } + } + + if (zeros1 < zeros2) return -1; + if (zeros1 > zeros2) return 1; + if (!*p1 && *p2) return -1; // s1 shorter than s2 + if (*p1 && !*p2) return 1; // s1 longer than s2 + + return 0; +} + + +// ************************************************************************* // diff --git a/src/OpenFOAM/primitives/strings/stringOps/stringOpsSort.H b/src/OpenFOAM/primitives/strings/stringOps/stringOpsSort.H new file mode 100644 index 0000000000000000000000000000000000000000..8e6344f27a81bfeb6f9662953204d677af75b384 --- /dev/null +++ b/src/OpenFOAM/primitives/strings/stringOps/stringOpsSort.H @@ -0,0 +1,133 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | Copyright (C) 2017 OpenCFD Ltd. + \\/ M anipulation | +------------------------------------------------------------------------------- +License + This file is part of OpenFOAM. + + OpenFOAM is free software: you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OpenFOAM 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 General Public License + for more details. + + You should have received a copy of the GNU General Public License + along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>. + +InNamespace + Foam::stringOps + +Description + Specialized string sorting. + +SourceFiles + stringOpsSort.C + +\*---------------------------------------------------------------------------*/ + +#ifndef stringOpsSort_H +#define stringOpsSort_H + +#include "stringOps.H" + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +namespace Foam +{ +namespace stringOps +{ + + //- 'Natural' compare for C-strings + // Uses algorithm and code from Jan-Marten Spit <jmspit@euronet.nl> + // + // In the 'natural' comparison, strings are compared alphabetically + // and numerically. Thus 'file010.txt' sorts after 'file2.txt' + // + // \param s1 left string + // \param s2 right string + // \return -1 when s1 < s2, 0 when s1 == s2, 1 when s1 > s2 + int natstrcmp(const char* s1, const char* s2); + + + //- Encapsulation of natural order sorting for algorithms + struct natural_sort + { + //- Natural compare for std::string + // \return -1 when s1 < s2, 0 when s1 == s2, 1 when s1 > s2 + static inline int compare + ( + const std::string& s1, + const std::string& s2 + ) + { + return natstrcmp(s1.data(), s2.data()); + } + + //- Default (forward) natural sorting + bool operator()(const std::string& s1, const std::string& s2) const + { + return natural_sort::compare(s1, s2) < 0; + } + + //- Reverse natural sorting + struct reverse + { + //- Reverse natural sorting + bool operator()(const std::string& s1, const std::string& s2) const + { + return natural_sort::compare(s1, s2) > 0; + } + }; + + + //- A list compare binary predicate for natural sort + template<class T> + struct less + { + const UList<T>& values; + + less(const UList<T>& list) + : + values(list) + {} + + bool operator()(const label a, const label b) const + { + return natural_sort::compare(values[a], values[b]) < 0; + } + }; + + + //- A list compare binary predicate for reverse natural sort + template<class T> + struct greater + { + const UList<T>& values; + + greater(const UList<T>& list) + : + values(list) + {} + + bool operator()(const label a, const label b) const + { + return natural_sort::compare(values[a], values[b]) > 0; + } + }; + }; + +} // End namespace stringOps +} // End namespace Foam + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +#endif + +// ************************************************************************* //