Mantid
Loading...
Searching...
No Matches
MultiFileNameParser.cpp
Go to the documentation of this file.
1// Mantid Repository : https://github.com/mantidproject/mantid
2//
3// Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI,
4// NScD Oak Ridge National Laboratory, European Spallation Source,
5// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
6// SPDX - License - Identifier: GPL - 3.0 +
7//----------------------------------------------------------------------
8// Includes
9//----------------------------------------------------------------------
11
15
16#include <numeric>
17#include <sstream>
18
19#include <boost/algorithm/string.hpp>
20#include <boost/regex.hpp>
21#include <utility>
22
25// Static constants.
27
28namespace Regexs {
29const std::string INST = "([A-Za-z]+|PG3|pg3)";
30
31const std::string UNDERSCORE = "(_{0,1})";
32const std::string SPACE = "(\\s*)";
33
34const std::string COMMA = "(" + SPACE + "," + SPACE + ")";
35const std::string PLUS = "(" + SPACE + "\\+" + SPACE + ")";
36const std::string MINUS = "(" + SPACE + "\\-" + SPACE + ")";
37const std::string COLON = "(" + SPACE + ":" + SPACE + ")";
38
39const std::string SINGLE = "(" + INST + "*[0-9]+)";
40const std::string RANGE = "(" + SINGLE + COLON + SINGLE + ")";
41const std::string STEP_RANGE = "(" + SINGLE + COLON + SINGLE + COLON + SINGLE + ")";
42const std::string ADD_RANGE = "(" + SINGLE + MINUS + SINGLE + ")";
43const std::string ADD_STEP_RANGE = "(" + SINGLE + MINUS + SINGLE + COLON + SINGLE + ")";
44const std::string SINGLE_OR_STEP_OR_ADD_RANGE = "(" + ADD_STEP_RANGE + "|" + ADD_RANGE + "|" + SINGLE + ")";
45const std::string ADD_LIST = "(" + SINGLE_OR_STEP_OR_ADD_RANGE + "(" + PLUS + SINGLE_OR_STEP_OR_ADD_RANGE + ")+" + ")";
46
47const std::string ANY =
48 "(" + ADD_STEP_RANGE + "|" + ADD_LIST + "|" + ADD_RANGE + "|" + STEP_RANGE + "|" + RANGE + "|" + SINGLE + ")";
49const std::string LIST = "(" + ANY + "(" + COMMA + ANY + ")*" + ")";
50} // namespace Regexs
51
53// Forward declarations.
55
56namespace {
57// Anonymous helper functions.
58void parseToken(std::vector<std::vector<unsigned int>> &parsedRuns, const std::string &token);
59std::vector<std::vector<unsigned int>> generateRange(const unsigned int from, const unsigned int to,
60 const unsigned int stepSize, const bool addRuns);
61void validateToken(const std::string &token);
62bool matchesFully(const std::string &stringToMatch, const std::string &regexString, const bool caseless = false);
63std::string getMatchingString(const std::string &regexString, const std::string &toParse, const bool caseless = false);
64std::string pad(const unsigned int run, const std::string &instString);
65
66std::set<std::pair<unsigned int, unsigned int>>
67mergeAdjacentRanges(std::set<std::pair<unsigned int, unsigned int>> ranges,
68 const std::pair<unsigned int, unsigned int> &range);
69
70// Helper functor.
71struct RangeContainsRun {
72 bool operator()(const std::pair<unsigned int, unsigned int> &range, const unsigned int run);
73 bool operator()(const unsigned int run, const std::pair<unsigned int, unsigned int> &range);
74};
75
76std::string toString(const RunRangeList &runRangeList);
77std::string accumulateString(std::string output, std::pair<unsigned int, unsigned int> runRange);
78} // namespace
79
81// Scoped, global functions.
83
96std::string suggestWorkspaceName(const std::vector<std::string> &fileNames) {
97 Parser parser;
98 RunRangeList runs;
99
100 // For each file name, parse the run number out of it, and add it to a
101 // RunRangeList.
102 for (const auto &fileName : fileNames) {
103 parser.parse(fileName);
104 runs.addRun(parser.runs()[0][0]);
105 }
106
107 // Return the suggested ws name.
108 return parser.instString() + parser.underscoreString() + toString(runs);
109}
110
112// Comparator class.
114
121bool ReverseCaselessCompare::operator()(const std::string &a, const std::string &b) const {
122 std::string lowerA;
123 lowerA.resize(a.size());
124 std::string lowerB;
125 lowerB.resize(b.size());
126
127 std::transform(a.cbegin(), a.cend(), lowerA.begin(), tolower);
128 std::transform(b.cbegin(), b.cend(), lowerB.begin(), tolower);
129
130 return lowerA > lowerB;
131}
132
134// Public member functions of Parser class.
136
139 : m_runs(), m_fileNames(), m_multiFileName(), m_dirString(), m_instString(), m_underscoreString(), m_runString(),
140 m_extString(), m_validInstNames(), m_trimWhiteSpaces(true) {
141 const ConfigServiceImpl &config = ConfigService::Instance();
142
143 const auto facilities = config.getFacilities();
144 for (const auto facility : facilities) {
145 const std::vector<InstrumentInfo> instruments = facility->instruments();
146
147 for (const auto &instrument : instruments) {
148 m_validInstNames.insert(instrument.name());
149 m_validInstNames.insert(instrument.shortName());
150 }
151 }
152}
153
161void Parser::parse(const std::string &multiFileName) {
162 // Clear any contents of the member variables.
163 clear();
164
165 // Set the string to parse.
166 m_multiFileName = multiFileName;
167
168 // Split the string to be parsed into sections, and do some validation.
169 split();
170
171 // Parse the run section into unsigned ints we can use.
173
174 // Set up helper functor.
176
177 // Generate complete file names for each run using helper functor.
178 std::transform(m_runs.begin(), m_runs.end(), std::back_inserter(m_fileNames), generateFileName);
179}
180
192std::vector<std::vector<unsigned int>> Parser::parseMultiRunString(std::string runString) {
193 // If the run string is empty, return no runs.
194 if (runString.empty())
195 return std::vector<std::vector<unsigned int>>();
196
197 // Remove whitespaces if requested.
198 if (trimWhiteSpaces()) {
199 runString.erase(std::remove_if( // ("Erase-remove" idiom.)
200 runString.begin(), runString.end(), isspace),
201 runString.end());
202 }
203 // Only numeric characters, or occurances of plus, minus, comma and colon are
204 // allowed.
205 if (!matchesFully(runString, "([0-9]|\\+|\\-|,|:)+")) {
206 throw std::runtime_error("Non-numeric or otherwise unaccetable character(s) detected.");
207 }
208
209 // Tokenize on commas.
210 std::vector<std::string> tokens;
211 tokens = boost::split(tokens, runString, boost::is_any_of(","));
212
213 // Validate each token.
214 std::for_each(tokens.begin(), tokens.end(), validateToken);
215
216 // Parse each token, accumulate the results, and return them.
217 std::vector<std::vector<unsigned int>> runGroups;
218 for (auto const &token : tokens) {
219 parseToken(runGroups, token);
220 }
221 return runGroups;
222}
223
230
237void Parser::setTrimWhiteSpaces(const bool &setting) { m_trimWhiteSpaces = setting; }
238
240// Private member functions of Parser class.
242
247 m_runs.clear();
248 m_fileNames.clear();
249 m_multiFileName.clear();
250 m_dirString.clear();
251 m_instString.clear();
252 m_underscoreString.clear();
253 m_runString.clear();
254 m_extString.clear();
255}
256
266 if (m_multiFileName.empty())
267 throw std::runtime_error("No file name to parse.");
268
269 // Clear whitespace before getting extentions and directories, if requested.
270 if (trimWhiteSpaces()) {
271 m_multiFileName.erase(std::remove_if( // ("Erase-remove" idiom.)
272 m_multiFileName.begin(), m_multiFileName.end(), isspace),
273 m_multiFileName.end());
274 }
275 // Get the extension, if there is one.
276 const size_t lastDot = m_multiFileName.find_last_of('.');
277 if (lastDot != std::string::npos)
278 m_extString = m_multiFileName.substr(lastDot);
279
280 // Get the directory, if there is one.
281 const size_t lastSeparator = m_multiFileName.find_last_of("/\\");
282 if (lastSeparator != std::string::npos)
283 m_dirString = m_multiFileName.substr(0, lastSeparator + 1);
284
285 // If the directory contains an instance of a comma, then the string is
286 // a comma separated list of single *full* file names to load.
287 if (std::string::npos != m_dirString.find(','))
288 throw std::runtime_error("Unable to parse.");
289
290 // Slice off the directory and extension.
291 std::string base =
292 m_multiFileName.substr(m_dirString.size(), m_multiFileName.size() - (m_dirString.size() + m_extString.size()));
293
294 if (base.empty())
295 throw std::runtime_error("There does not appear to be any runs present.");
296
297 auto instrumentNameIt = std::find_if(m_validInstNames.cbegin(), m_validInstNames.cend(), [&base](const auto &name) {
298 return matchesFully(base, name + ".*", true);
299 }); // USE CASELESS MATCHES HERE.
300
301 // See if the user has typed in one of the available instrument names.
302 if (instrumentNameIt != m_validInstNames.cend()) {
303 m_instString = getMatchingString("^" + *instrumentNameIt, base, true);
304 }
305
306 // If not, use the default, or throw if we encounter an unrecognisable
307 // non-numeric string.
308 if (m_instString.empty()) {
309 if (base.empty())
310 throw std::runtime_error("There does not appear to be any runs present.");
311
312 if (isdigit(base[0]))
313 m_instString = ConfigService::Instance().getString("default.instrument");
314 else
315 throw std::runtime_error("There does not appear to be a valid instrument name present.");
316 } else {
317 // Chop off instrument name.
318 base = base.substr(m_instString.size(), base.size());
319 }
320
321 if (base.empty())
322 throw std::runtime_error("There does not appear to be any runs present.");
323
324 const auto &instInfo = ConfigService::Instance().getInstrument(m_instString);
325 // why?
326 // m_instString = instInfo.shortName(); // Make sure we're using the shortened
327 // form of the isntrument name.
328
329 if (base.starts_with(instInfo.delimiter())) {
330 // Store the instrument delimiter, and strip it off the start of the string.
331 m_underscoreString = instInfo.delimiter();
332 base = base.substr(m_underscoreString.size(), base.size());
333 }
334
335 m_runString = getMatchingString("^" + Regexs::LIST, base);
336
337 if (m_runString.size() != base.size()) {
338 throw std::runtime_error("There is an unparsable token present.");
339 }
340}
341
343// Helper functor.
345
353GenerateFileName::GenerateFileName(std::string prefix, std::string suffix, std::string instString)
354 : m_prefix(std::move(prefix)), m_suffix(std::move(suffix)), m_instString(std::move(instString)) {}
355
364std::vector<std::string> GenerateFileName::operator()(const std::vector<unsigned int> &runs) {
365 std::vector<std::string> fileNames;
366
367 std::transform(runs.begin(), runs.end(), std::back_inserter(fileNames),
368 (*this) // Call other overloaded function operator.
369 );
370
371 return fileNames;
372}
373
381std::string GenerateFileName::operator()(const unsigned int run) {
382 std::stringstream fileName;
383
384 fileName << m_prefix << pad(run, m_instString) << m_suffix;
385
386 return fileName.str();
387}
388
390// Public member functions of RunRangeList class.
392
396RunRangeList::RunRangeList() : m_rangeList() {}
397
403void RunRangeList::addRun(const unsigned int run) {
404 // If the run is inside one of the ranges, do nothing.
405 if (std::binary_search(m_rangeList.begin(), m_rangeList.end(), run, RangeContainsRun()))
406 return;
407
408 // Else create a new range, containing a single run, and add it to the list.
409 m_rangeList.emplace(run, run);
410
411 // Now merge any ranges that are adjacent.
412 m_rangeList = std::accumulate(m_rangeList.begin(), m_rangeList.end(),
413 std::set<std::pair<unsigned int, unsigned int>>(), mergeAdjacentRanges);
414}
415
422void RunRangeList::addRunRange(unsigned int from, unsigned int to) {
423 for (; from <= to; ++from)
424 addRun(from);
425}
426
432void RunRangeList::addRunRange(const std::pair<unsigned int, unsigned int> &range) {
433 addRunRange(range.first, range.second);
434}
435
437// Anonymous helper functions.
439
440namespace // anonymous
441{
449void parseToken(std::vector<std::vector<unsigned int>> &parsedRuns, const std::string &token) {
450 std::vector<std::vector<unsigned int>> runs;
451 // Tokenise further on plus.
452 std::vector<std::string> subTokens;
453 boost::split(subTokens, token, boost::is_any_of("+"));
454 std::vector<unsigned int> runsToAdd;
455 for (auto const &subToken : subTokens) {
456 // E.g. "2012".
457 if (matchesFully(subToken, Regexs::SINGLE)) {
458 runsToAdd.emplace_back(std::stoi(subToken));
459 }
460 // E.g. "2012:2020".
461 else if (matchesFully(subToken, Regexs::RANGE)) {
462 // Fill in runs directly.
463 constexpr bool addRuns{false};
464 std::vector<std::string> rangeDetails;
465 rangeDetails.reserve(2);
466 boost::split(rangeDetails, subToken, boost::is_any_of(":"));
467 runs = generateRange(std::stoi(rangeDetails.front()), std::stoi(rangeDetails.back()), 1, addRuns);
468 }
469 // E.g. "2012:2020:4".
470 else if (matchesFully(subToken, Regexs::STEP_RANGE)) {
471 // Fill in runs directly.
472 constexpr bool addRuns{false};
473 std::vector<std::string> rangeDetails;
474 rangeDetails.reserve(3);
475 boost::split(rangeDetails, subToken, boost::is_any_of(":"));
476 runs = generateRange(std::stoi(rangeDetails[0]), std::stoi(rangeDetails[1]), std::stoi(rangeDetails[2]), addRuns);
477 }
478 // E.g. "2012-2020".
479 else if (matchesFully(subToken, Regexs::ADD_RANGE)) {
480 constexpr bool addRuns{true};
481 std::vector<std::string> rangeDetails;
482 rangeDetails.reserve(2);
483 boost::split(rangeDetails, subToken, boost::is_any_of("-"));
484 const auto generated = generateRange(std::stoi(rangeDetails.front()), std::stoi(rangeDetails.back()), 1, addRuns);
485 std::copy(generated.front().cbegin(), generated.front().cend(), back_inserter(runsToAdd));
486 }
487 // E.g. "2012-2020:4".
488 else if (matchesFully(subToken, Regexs::ADD_STEP_RANGE)) {
489 constexpr bool addRuns{true};
490 std::vector<std::string> rangeDetails;
491 rangeDetails.reserve(3);
492 boost::split(rangeDetails, subToken, boost::is_any_of("-:"));
493 const auto generated =
494 generateRange(std::stoi(rangeDetails[0]), std::stoi(rangeDetails[1]), std::stoi(rangeDetails[2]), addRuns);
495 std::copy(generated.front().cbegin(), generated.front().cend(), back_inserter(runsToAdd));
496 } else {
497 // We should never reach here - the validation done on the token
498 // previously should prevent any other possible scenario.
499 assert(false);
500 }
501 }
502 if (!runsToAdd.empty()) {
503 if (!runs.empty()) {
504 // We have either add ranges or step ranges. Never both.
505 throw std::runtime_error("Unable to handle a mixture of add ranges and step ranges");
506 }
507 runs.emplace_back(runsToAdd);
508 }
509 // Add the runs on to the end of parsedRuns, and return it.
510 std::copy(runs.begin(), runs.end(), std::back_inserter(parsedRuns));
511}
512
528std::vector<std::vector<unsigned int>> generateRange(unsigned int const from, unsigned int const to,
529 unsigned int const stepSize, bool const addRuns) {
530 if (stepSize == 0)
531 throw std::runtime_error("Unable to generate a range with a step size of zero.");
532
533 size_t limit;
534 auto limitStr = ConfigService::Instance().getValue<std::string>("loading.multifilelimit");
535 if (!limitStr.has_value() || !Strings::convert(limitStr.value(), limit)) {
536 limit = ConfigService::Instance().getFacility().multiFileLimit();
537 }
538
539 unsigned int const orderedTo = from > to ? from : to;
540 unsigned int const orderedFrom = from > to ? to : from;
541 unsigned int const numberOfFiles = (orderedTo - orderedFrom) / stepSize;
542 if (numberOfFiles > limit) {
543 std::stringstream sstream;
544 sstream << "The range from " << orderedFrom << " to " << orderedTo << " with step " << stepSize
545 << " would generate " << numberOfFiles << " files. "
546 << "This is greater than the current limit of " << limit << ". "
547 << "This limit can be configured in the Mantid.user.properties "
548 "file using the key loading.multifilelimit=200.";
549 throw std::range_error(sstream.str());
550 }
551
552 unsigned int currentRun = from;
553 std::vector<std::vector<unsigned int>> runs;
554
555 // If ascending range
556 if (from <= to) {
557 while (currentRun <= to) {
558 if (addRuns) {
559 if (runs.empty())
560 runs.emplace_back(1, currentRun);
561 else
562 runs.front().emplace_back(currentRun);
563 } else {
564 runs.emplace_back(1, currentRun);
565 }
566
567 currentRun += stepSize;
568 }
569 }
570 // Else descending range
571 else {
572 while (currentRun >= to) {
573 if (addRuns) {
574 if (runs.empty())
575 runs.emplace_back(1, currentRun);
576 else
577 runs.front().emplace_back(currentRun);
578 } else {
579 runs.emplace_back(1, currentRun);
580 }
581
582 // Guard against case where stepSize would take us into negative
583 // numbers (not supported by unsigned ints ...).
584 if (static_cast<int>(currentRun) - static_cast<int>(stepSize) < 0)
585 break;
586
587 currentRun -= stepSize;
588 }
589 }
590
591 return runs;
592}
593
601void validateToken(const std::string &token) {
602 // Each token must be non-empty.
603 if (token.empty())
604 throw std::runtime_error("A comma-separated token is empty.");
605
606 // Each token must begin and end with a numeric character.
607 if (!matchesFully(token, "[0-9].+[0-9]|[0-9]"))
608 throw std::runtime_error("The token \"" + token +
609 "\" is of an incorrect form. Does it begin or "
610 "end with a plus, minus or colon?");
611
612 // Each token must be one of the acceptable forms, i.e. a single run, an added
613 // range of runs, etc.
614 if (!matchesFully(token, Regexs::ANY))
615 throw std::runtime_error("The token \"" + token + "\" is of an incorrect form.");
616}
617
627bool matchesFully(const std::string &stringToMatch, const std::string &regexString, const bool caseless) {
628 boost::regex regex;
629
630 if (caseless)
631 regex = boost::regex("^(" + regexString + "$)", boost::regex::icase);
632 else
633 regex = boost::regex("^(" + regexString + "$)");
634
635 return boost::regex_match(stringToMatch, regex);
636}
637
647std::string getMatchingString(const std::string &regexString, const std::string &toParse, const bool caseless) {
648 boost::regex regex;
649 if (caseless) {
650 regex = boost::regex(regexString, boost::regex::icase);
651 } else {
652 regex = boost::regex(regexString);
653 }
654
655 boost::sregex_iterator it(toParse.begin(), toParse.end(), regex);
656
657 if (it == boost::sregex_iterator())
658 return "";
659
660 return it->str();
661}
662
672std::string pad(const unsigned int run, const std::string &instString) {
673 InstrumentInfo const instInfo = ConfigService::Instance().getInstrument(instString);
674 std::string prefix;
675 if (!instInfo.facility().noFilePrefix())
676 prefix = instInfo.filePrefix(run) + instInfo.delimiter();
677 unsigned int padLength = instInfo.zeroPadding(run);
678 std::string runStr = std::to_string(run);
679 if (runStr.size() < padLength)
680 runStr.insert(0, padLength - runStr.size(), '0');
681 else if (padLength > 0 && runStr.size() > padLength)
682 throw std::runtime_error("Could not parse run number \"" + runStr +
683 "\" since the instrument run number length required is " + std::to_string(padLength));
684 runStr.insert(0, prefix);
685 return runStr;
686}
687
695bool RangeContainsRun::operator()(const std::pair<unsigned int, unsigned int> &range, const unsigned int run) {
696 return range.second < run;
697}
698bool RangeContainsRun::operator()(const unsigned int run, const std::pair<unsigned int, unsigned int> &range) {
699 return run < range.first;
700}
701
711std::set<std::pair<unsigned int, unsigned int>>
712mergeAdjacentRanges(std::set<std::pair<unsigned int, unsigned int>> ranges,
713 const std::pair<unsigned int, unsigned int> &range) {
714 // If ranges is empty, just insert the new range.
715 if (ranges.empty()) {
716 ranges.insert(range);
717 }
718 // Else there are already some ranges present ...
719 else {
720 // ... if the last one is adjacent to the new range, merge the two.
721 if (ranges.rbegin()->second + 1 == range.first) {
722 unsigned int from = ranges.rbegin()->first;
723 unsigned int to = range.second;
724 std::pair<unsigned int, unsigned int> temp(from, to);
725
726 ranges.erase(--ranges.end(), ranges.end());
727 ranges.insert(temp);
728 }
729 // ... else just insert it.
730 else {
731 ranges.insert(range);
732 }
733 }
734 return ranges;
735}
736
747std::string accumulateString(std::string output, std::pair<unsigned int, unsigned int> runRange) {
748 if (!output.empty())
749 output += "_and_";
750
751 if (runRange.first == runRange.second)
752 output += std::to_string(runRange.first);
753 else
754 output += std::to_string(runRange.first) + "_to_" + std::to_string(runRange.second);
755
756 return output;
757}
758
766std::string toString(const RunRangeList &runRangeList) {
767 std::set<std::pair<unsigned int, unsigned int>> runRanges = runRangeList.rangeList();
768
769 // For each run range (pair of unsigned ints), call accumulateString and
770 // return the accumulated result.
771 return std::accumulate(runRanges.begin(), runRanges.end(), std::string(), accumulateString);
772}
773
774} // anonymous namespace
775
776} // namespace Mantid::Kernel::MultiFileNameParsing
std::string name
Definition Run.cpp:60
The ConfigService class provides a simple facade to access the Configuration functionality of the Man...
const std::vector< FacilityInfo * > getFacilities() const
Get the list of facilities.
A functor that generates a vector of file names from the given vector of runs, and other state passed...
std::string m_instString
String that identifies the instrument.
std::string m_prefix
String that prefixes any generated file names.
GenerateFileName(std::string prefix, std::string suffix, std::string instString)
Constructor.
std::vector< std::string > operator()(const std::vector< unsigned int > &runs)
Overloaded function operator that generates a vector of file names from a vector of runs.
std::string m_suffix
String that suffixes any generated file names.
This class takes a string representing multiple files and parses it into a vector of vectors of file ...
const std::string & runString() const
Return the parsed run string.
std::string m_multiFileName
The given string to parse.
std::vector< std::vector< std::string > > m_fileNames
A vector of vectors of the parsed file names.
std::vector< std::vector< unsigned int > > parseMultiRunString(std::string runString)
Parses a string consisting of only run number info, into a vector of vector of run numbers.
std::vector< std::vector< unsigned int > > m_runs
A vector of vectors of the parsed runs.
void split()
Split the string to parse into its component parts.
std::set< std::string, ReverseCaselessCompare > m_validInstNames
All the valid instrument names.
const std::string & underscoreString() const
Return the parsed underscore string.
const std::vector< std::vector< unsigned int > > & runs() const
Return the vector of vectors of parsed file names.
bool trimWhiteSpaces() const
Return the setting for trimming whitespaces in run string.
bool m_trimWhiteSpaces
Flag to determine if string input should be trimmed of whitespace.
void setTrimWhiteSpaces(const bool &setting)
Set the flag for trimming whitespaces in run string.
std::string m_dirString
The various sections of the given string to parse.
const std::string & instString() const
Return the parsed instrument string.
void parse(const std::string &multiFileName)
Parse the given multiFileNameString.
bool operator()(const std::string &a, const std::string &b) const
Comparator for the set that holds instrument names in Parser.
A class that holds a list of ranges of runs.
std::set< std::pair< unsigned int, unsigned int > > m_rangeList
A set of pairs of unsigned ints, where each pair represents a range of runs.
void addRunRange(const unsigned int from, const unsigned int to)
Add a range of runs.
void addRun(const unsigned int run)
Add a run to the list of run ranges.
MANTID_KERNEL_DLL std::string suggestWorkspaceName(const std::vector< std::string > &fileNames)
Suggests a workspace name, given a vector of file names.
int convert(const std::string &A, T &out)
Convert a string into a number.
Definition Strings.cpp:696
std::string toString(const T &value)
Convert values to strings.
MortonT pad(IntT)
Pad an integer with a given number of padding bits.
STL namespace.
std::string to_string(const wide_integer< Bits, Signed > &n)