Base: UniqueNameManager support for very long numbers in name (#19943)
* Add unit tests for large digit count in unique names * Updated to use arbitrary-precision unsigneds Passes the new unit tests, all diagnostics, and resolves Issue 19881 * Place UnlimitedUnsigned at top level and add unit tests * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
@@ -30,99 +30,7 @@
|
||||
#endif
|
||||
#include "UniqueNameManager.h"
|
||||
|
||||
void Base::UniqueNameManager::PiecewiseSparseIntegerSet::add(unsigned int value)
|
||||
{
|
||||
SpanType newSpan(value, 1);
|
||||
// Look for the smallest entry not less than newSpan.
|
||||
// Bear in mind that overlapping spans are neither less than nor greater than ech other.
|
||||
auto above = spans.lower_bound(newSpan);
|
||||
if (above != spans.end() && above->first <= value) {
|
||||
// A span was found that includes value so there is nothing to do as it is already in the
|
||||
// set.
|
||||
return;
|
||||
}
|
||||
|
||||
// Set below to the next span below 'after', if any, otherwise to spans.end().
|
||||
// Logically, we want spans.begin()-1 so 'below' is always the entry before 'after',
|
||||
// but that is not an allowed reference so spans.end() is used
|
||||
std::set<SpanType, Comparer>::iterator below;
|
||||
if (above == spans.begin()) {
|
||||
// No spans are less than newSpan
|
||||
// (possibly spans is empty, in which case above == spans.end() too)
|
||||
below = spans.end();
|
||||
}
|
||||
else {
|
||||
// At least one span is less than newSpan,
|
||||
// and 'above' is the next span above that
|
||||
// (or above == spans.end() if all spans are below newSpan)
|
||||
below = above;
|
||||
--below;
|
||||
}
|
||||
|
||||
// Determine whether the span above (if any) and/or the span below (if any)
|
||||
// are adjacent to newSpan and if so, merge them appropriately and remove the
|
||||
// original span(s) that was/were merged, updating newSpan to be the new merged
|
||||
// span.
|
||||
if (above != spans.end() && below != spans.end()
|
||||
&& above->first - below->first + 1 == below->second) {
|
||||
// below and above have a gap of exactly one between them, and this must be value
|
||||
// so we coalesce the two spans (and the gap) into one.
|
||||
newSpan = SpanType(below->first, below->second + above->second + 1);
|
||||
spans.erase(above);
|
||||
above = spans.erase(below);
|
||||
}
|
||||
else if (below != spans.end() && value - below->first == below->second) {
|
||||
// value is adjacent to the end of below, so just expand below by one
|
||||
newSpan = SpanType(below->first, below->second + 1);
|
||||
above = spans.erase(below);
|
||||
}
|
||||
else if (above != spans.end() && above->first - value == 1) {
|
||||
// value is adjacent to the start of above, so just expand above down by one
|
||||
newSpan = SpanType(above->first - 1, above->second + 1);
|
||||
above = spans.erase(above);
|
||||
}
|
||||
// else value is not adjacent to any existing span, so just make anew span for it
|
||||
spans.insert(above, newSpan);
|
||||
}
|
||||
void Base::UniqueNameManager::PiecewiseSparseIntegerSet::remove(unsigned int value)
|
||||
{
|
||||
SpanType newSpan(value, 1);
|
||||
auto at = spans.lower_bound(newSpan);
|
||||
if (at == spans.end() || at->first > value) {
|
||||
// The found span does not include value so there is nothing to do, as it is already not in
|
||||
// the set.
|
||||
return;
|
||||
}
|
||||
if (at->second == 1) {
|
||||
// value is the only in this span, just remove the span
|
||||
spans.erase(at);
|
||||
}
|
||||
else if (at->first == value) {
|
||||
// value is the first in this span, trim the lower end
|
||||
SpanType replacement(at->first + 1, at->second - 1);
|
||||
spans.insert(spans.erase(at), replacement);
|
||||
}
|
||||
else if (value - at->first == at->second - 1) {
|
||||
// value is the last in this span, trim the upper end
|
||||
SpanType replacement(at->first, at->second - 1);
|
||||
spans.insert(spans.erase(at), replacement);
|
||||
}
|
||||
else {
|
||||
// value is in the moddle of the span, so we must split it.
|
||||
SpanType firstReplacement(at->first, value - at->first);
|
||||
SpanType secondReplacement(value + 1, at->second - ((value + 1) - at->first));
|
||||
// Because erase returns the iterator after the erased element, and insert returns the
|
||||
// iterator for the inserted item, we want to insert secondReplacement first.
|
||||
spans.insert(spans.insert(spans.erase(at), secondReplacement), firstReplacement);
|
||||
}
|
||||
}
|
||||
bool Base::UniqueNameManager::PiecewiseSparseIntegerSet::contains(unsigned int value) const
|
||||
{
|
||||
auto at = spans.lower_bound(SpanType(value, 1));
|
||||
return at != spans.end() && at->first <= value;
|
||||
}
|
||||
|
||||
std::tuple<std::string, std::string, unsigned int, unsigned int>
|
||||
std::tuple<std::string, std::string, unsigned int, Base::UnlimitedUnsigned>
|
||||
Base::UniqueNameManager::decomposeName(const std::string& name) const
|
||||
{
|
||||
auto suffixStart = getNameSuffixStartPosition(name);
|
||||
@@ -130,11 +38,11 @@ Base::UniqueNameManager::decomposeName(const std::string& name) const
|
||||
return std::isdigit(c);
|
||||
});
|
||||
unsigned int digitCount = digitsStart - suffixStart;
|
||||
return std::tuple<std::string, std::string, unsigned int, unsigned int> {
|
||||
return std::tuple<std::string, std::string, unsigned int, UnlimitedUnsigned> {
|
||||
name.substr(0, name.crend() - digitsStart),
|
||||
name.substr(name.crend() - suffixStart),
|
||||
digitCount,
|
||||
digitCount == 0 ? 0U : std::stoul(name.substr(name.crend() - digitsStart, digitCount))};
|
||||
UnlimitedUnsigned::fromString(name.substr(name.crend() - digitsStart, digitCount))};
|
||||
}
|
||||
bool Base::UniqueNameManager::haveSameBaseName(const std::string& first,
|
||||
const std::string& second) const
|
||||
@@ -162,13 +70,16 @@ void Base::UniqueNameManager::addExactName(const std::string& name)
|
||||
if (baseNameEntry == uniqueSeeds.end()) {
|
||||
// First use of baseName
|
||||
baseNameEntry =
|
||||
uniqueSeeds.emplace(baseName, std::vector<PiecewiseSparseIntegerSet>()).first;
|
||||
uniqueSeeds
|
||||
.emplace(baseName, std::vector<PiecewiseSparseIntegerSet<UnlimitedUnsigned>>())
|
||||
.first;
|
||||
}
|
||||
if (digitCount >= baseNameEntry->second.size()) {
|
||||
// First use of this digitCount
|
||||
baseNameEntry->second.resize(digitCount + 1);
|
||||
}
|
||||
PiecewiseSparseIntegerSet& baseNameAndDigitCountEntry = baseNameEntry->second[digitCount];
|
||||
PiecewiseSparseIntegerSet<UnlimitedUnsigned>& baseNameAndDigitCountEntry =
|
||||
baseNameEntry->second[digitCount];
|
||||
|
||||
if (baseNameAndDigitCountEntry.contains(digitsValue)) {
|
||||
// We already have at least one instance of the name.
|
||||
@@ -198,12 +109,12 @@ std::string Base::UniqueNameManager::makeUniqueName(const std::string& modelName
|
||||
// We start the longer digit string at 000...0001 even though we might have shorter strings
|
||||
// with larger numeric values.
|
||||
digitCount = minDigits;
|
||||
digitsValue = 1;
|
||||
digitsValue = UnlimitedUnsigned(1);
|
||||
}
|
||||
else {
|
||||
digitsValue = baseNameEntry->second[digitCount].next();
|
||||
}
|
||||
std::string digits = std::to_string(digitsValue);
|
||||
std::string digits = digitsValue.toString();
|
||||
if (digitCount > digits.size()) {
|
||||
namePrefix += std::string(digitCount - digits.size(), '0');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user