simstr/src/sstring.cpp

1472 lines
57 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* (c) Проект "SimStr", Александр Орефков orefkov@gmail.com
* ver. 1.0
* Реализация строковых функций
*/
#include "simstr/simple_unicode.h"
#include "simstr/sstring.h"
#include <simdutf.h>
namespace simstr {
template class sstring<u8s>;
template class sstring<uws>;
template class sstring<u16s>;
template class sstring<u32s>;
// from sqlite.c
/*
** Notes On Invalid UTF-8:
**
** * This routine never allows a 7-bit character (0x00 through 0x7f) to
** be encoded as a multi-byte character. Any multi-byte character that
** attempts to encode a value between 0x00 and 0x7f is rendered as 0xfffd.
**
** * This routine never allows a UTF16 surrogate value to be encoded.
** If a multi-byte character attempts to encode a value between
** 0xd800 and 0xe000 then it is rendered as 0xfffd.
**
** * Bytes in the range of 0x80 through 0xbf which occur as the first
** byte of a character are interpreted as single-byte characters
** and rendered as themselves even though they are technically
** invalid characters.
**
** * This routine accepts over-length UTF8 encodings
** for unicode values 0x80 and greater. It does not change over-length
** encodings to 0xfffd as some systems recommend.
*/
static const uu8s sqlite3Utf8Trans1[] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b,
0x0c, 0x0d, 0x0e, 0x0f, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x00, 0x00,
};
u32s readUtf8Symbol(const uu8s*& ptr, const uu8s* end) {
u32s us = static_cast<u32s>(*ptr++);
if (us >= 0xC0) {
us = sqlite3Utf8Trans1[us - 0xC0];
while (ptr < end && (*ptr & 0xC0) == 0x80)
us = (us << 6) + (0x3F & *(ptr++));
if (us < 0x80 || (us & 0xFFFFF800) == 0xD800 || (us & 0xFFFFFFFE) == 0xFFFE) {
us = 0xFFFD;
}
}
return us;
}
void writeUtf8Symbol(uu8s*& write, u32s us) {
using u8 = uu8s;
if (us <= 0x80)
*write++ = (u8)us;
else if (us < 0x00800) {
*write++ = 0xC0 + (u8)((us >> 6) & 0x1F);
*write++ = 0x80 + (u8)(us & 0x3F);
} else if (us < 0x10000) {
*write++ = 0xE0 + (u8)((us >> 12) & 0x0F);
*write++ = 0x80 + (u8)((us >> 6) & 0x3F);
*write++ = 0x80 + (u8)(us & 0x3F);
} else {
*write++ = 0xF0 + (u8)((us >> 18) & 0x07);
*write++ = 0x80 + (u8)((us >> 12) & 0x3F);
*write++ = 0x80 + (u8)((us >> 6) & 0x3F);
*write++ = 0x80 + (u8)(us & 0x3F);
}
}
u32s readUtf16Symbol(const u16s*& ptr, const u16s* end) {
u32s us = static_cast<u32s>(*ptr++);
if (us >= 0xD800) {
if (us < 0xDC00 && ptr < end) {
if (u32s s = static_cast<u32s>(*ptr++); s >= 0xDC00 && s < 0xE000) {
return ((us & 0x3ff) << 10) + (s & 0x3ff) + 0x10000;
}
}
return 0xFFFD;
}
return us;
}
void writeUtf16Symbol(u16s*& ptr, u32s s) {
if (s < 0x10000) {
*ptr++ = static_cast<u16s>(s);
} else {
s -= 0x10000;
*ptr++ = 0xD800 + ((s >> 10) & 0x3FF);
*ptr++ = 0xDC00 + (s & 0x3FF);
}
}
size_t utf8len(u32s us) {
if (us <= 0x80)
return 1;
else if (us < 0x00800)
return 2;
else if (us < 0x10000)
return 3;
else
return 4;
}
SIMSTR_API size_t utf_convert_selector<u8s, u16s>::need_len(const u8s*src, size_t srcLen) {
return simdutf::utf16_length_from_utf8(src, srcLen);
}
SIMSTR_API size_t utf_convert_selector<u8s, u16s>::convert(const u8s* src, size_t srcLen, u16s* dest) {
return simdutf::convert_utf8_to_utf16(src, srcLen, dest);
}
SIMSTR_API size_t utf_convert_selector<u8s, u32s>::need_len(const u8s*src, size_t srcLen) {
return simdutf::utf32_length_from_utf8(src, srcLen);
}
SIMSTR_API size_t utf_convert_selector<u8s, u32s>::convert(const u8s* src, size_t srcLen, u32s* dest) {
return simdutf::convert_utf8_to_utf32(src, srcLen, dest);
}
SIMSTR_API size_t utf_convert_selector<u16s, u8s>::need_len(const u16s*src, size_t srcLen) {
return simdutf::utf8_length_from_utf16(src, srcLen);
}
SIMSTR_API size_t utf_convert_selector<u16s, u8s>::convert(const u16s* src, size_t srcLen, u8s* dest) {
return simdutf::convert_utf16_to_utf8(src, srcLen, dest);
}
SIMSTR_API size_t utf_convert_selector<u16s, u32s>::need_len(const u16s*src, size_t srcLen) {
return simdutf::utf32_length_from_utf16(src, srcLen);
}
SIMSTR_API size_t utf_convert_selector<u16s, u32s>::convert(const u16s* src, size_t srcLen, u32s* dest) {
return simdutf::convert_utf16_to_utf32(src, srcLen, dest);
}
SIMSTR_API size_t utf_convert_selector<u32s, u8s>::need_len(const u32s*src, size_t srcLen) {
return simdutf::utf8_length_from_utf32(src, srcLen);
}
SIMSTR_API size_t utf_convert_selector<u32s, u8s>::convert(const u32s* src, size_t srcLen, u8s* dest) {
return simdutf::convert_utf32_to_utf8(src, srcLen, dest);
}
SIMSTR_API size_t utf_convert_selector<u32s, u16s>::need_len(const u32s*src, size_t srcLen) {
return simdutf::utf16_length_from_utf32(src, srcLen);
}
SIMSTR_API size_t utf_convert_selector<u32s, u16s>::convert(const u32s* src, size_t srcLen, u16s* dest) {
return simdutf::convert_utf32_to_utf16(src, srcLen, dest);
}
inline u32s makeLowerU(u32s s) {
if (isAsciiUpper(s))
return s | 0x20;
else if (s > 127 && s < 0x10000)
return simpleUnicodeLower(static_cast<uint16_t>(s));
else
return s;
}
inline u32s makeUpperU(u32s s) {
if (isAsciiLower(s))
return s & ~0x20;
else if (s > 127 && s < 0x10000)
return simpleUnicodeUpper(static_cast<uint16_t>(s));
else
return s;
}
inline u32s makeFoldU(u32s s) {
if (isAsciiUpper(s))
return s | 0x20;
else if (s > 127 && s < 0x10000)
return simpleUnicodeFold(static_cast<uint16_t>(s));
else
return s;
}
/*
* Поиск utf-8 symbols, которые меняют свою длину при upper/lower преобразовании
* вот они нашлись
* "İiıIſSȺⱥȾⱦɐⱯɑⱭɫⱢɱⱮɽⱤẞßΙΩωkÅåⱢɫⱤɽⱥȺⱦȾⱭɑⱮɱⱯɐ"
l2u ıIſSɐⱯɑⱭɫⱢɱⱮɽⱤΙⱥȺⱦȾ
u2l İiȺⱥȾⱦẞßΩωkÅåⱢɫⱤɽⱭɑⱮɱⱯɐ
void findStrange() {
u16s badsL2U[100], * pBadslu = badsL2U;
u16s badsU2L[100], * pBadsul = badsU2L;
uu8s buf[20];
size_t allToUpper = 0, allToLower = 0, lowerbadshorter = 0, upperbadshorter = 0, lowerbadlonger = 0, upperbadlonger = 0;
for (u32s s = 128; s <= 0xFFFF; s++) {
u16s upper = makeUpperU(s);
if (upper != s) {
allToUpper++;
uu8s* pTest = buf;
writeUtf8Symbol(pTest, s);
size_t len1 = pTest - buf;
pTest = buf;
writeUtf8Symbol(pTest, upper);
size_t len2 = pTest - buf;
if (len1 != len2) {
*pBadslu++ = s;
*pBadslu++ = upper;
if (len1 > len2) {
upperbadshorter++;
} else if (len1 < len2) {
upperbadlonger++;
}
}
}
u16s lower = makeLowerU(s);
if (lower != s) {
allToLower++;
uu8s* pTest = buf;
writeUtf8Symbol(pTest, s);
size_t len1 = pTest - buf;
pTest = buf;
writeUtf8Symbol(pTest, lower);
size_t len2 = pTest - buf;
if (len1 != len2) {
*pBadsul++ = s;
*pBadsul++ = lower;
if (len1 > len2) {
lowerbadshorter++;
} else if (len1 < len2) {
lowerbadlonger++;
}
}
}
}
*pBadsul = 0;
*pBadslu = 0;
return;
}
*/
template<auto Op>
size_t utf8_case_change(const u8s*& src, size_t len, u8s*& dest, size_t lenBuffer) {
// Допущение для оптимизации - считается, что выделенный буфер записи всегда не меньше длины буфера чтения
// и что буфер записи не начинается внутри буфера чтения
// то есть если символ считан, и длина записываемого символа не больше считанного, то он поместится в буфер записи
// и не перетрет символы, которые еще не прочитали
// По другому работать откажемся
if (lenBuffer < len || (dest > src && dest < src + len))
return len;
const uu8s *beginReadPos = reinterpret_cast<const uu8s*>(src), *readPos = beginReadPos, *endReadPos = beginReadPos + len,
*readFromPos = readPos;
uu8s *beginWritePos = reinterpret_cast<uu8s*>(dest), *writePos = beginWritePos, *endWritePos = writePos + lenBuffer, *tempWrite;
size_t state = 0, writedSymbolLen, needExtraLen = 0;
u32s readedSymbol, writedSymbol;
lstringa<4096> tempStore;
while (readPos < endReadPos) {
readedSymbol = readUtf8Symbol(readPos, endReadPos), writedSymbol = Op(readedSymbol);
writedSymbolLen = utf8len(writedSymbol);
switch (state) {
case 0: // начальное состояние
// Так как выделенный буфер записи всегда не меньше длины буфера чтения
// то если символ считан, и длина записываемого символа не больше считанного,
// то он поместится в буфер записи, можно не проверять
// оптимизация для inplace конвертации в идеальном случае
if (writePos == readFromPos && readedSymbol == writedSymbol) {
writePos = const_cast<uu8s*>(readPos);
readFromPos = readPos;
continue;
}
if (writePos + writedSymbolLen <= endWritePos && (writePos > readPos || writePos + writedSymbolLen <= readPos)) {
// всё отлично
writeUtf8Symbol(writePos, writedSymbol);
} else {
state = 1;
goto state1;
}
break;
case 1:
state1:
// До этого когда-то был считан символ, при записи ставший длиннее.
// Можно его писать, но надо проверять, что записываемый символ:
// - не перезатрёт читаемые
// - не выйдет за границы буфера записи
// - восстановит обычный режим, если записываемый символ короче прочитанного
if (writePos + writedSymbolLen > endWritePos) {
// Записываемый символ не поместится в буфер записи
// Запомним, где это произошло, подсчитаем символы далее
src = reinterpret_cast<const u8s*>(readFromPos);
dest = reinterpret_cast<u8s*>(writePos);
needExtraLen = 0;
state = 2;
goto state2;
} else if (writePos < readPos && writePos + writedSymbolLen > readPos) {
// Совсем плохой случай - записываемый символ перезатрет следующий читаемый
// Запомним, где это произошло, подсчитаем символы далее
src = reinterpret_cast<const u8s*>(readFromPos);
dest = reinterpret_cast<u8s*>(writePos);
needExtraLen = 0;
if (lenBuffer > len) {
// считаем, что это второй вызов, и места в буфере в итоге хватит
// поэтому будем сохранять считанные символы в свой буфер, а потом скопируем их в результат
tempWrite = reinterpret_cast<uu8s*>(tempStore.reserve(lenBuffer - static_cast<size_t>(writePos - beginWritePos)));
state = 3;
goto state3;
} else {
// Это первый вызов, будем просто считать нужное место
state = 2;
goto state2;
}
} else {
// Можем записать прочитанный символ
writeUtf8Symbol(writePos, writedSymbol);
// Возможно, мы выровнялись по прочитанным/записанным байтам, тогда можно снова писать в буфер
// в обычном состоянии
size_t allReaded = static_cast<size_t>(readPos - beginReadPos);
size_t allWrite = static_cast<size_t>(writePos - beginWritePos);
if (allReaded >= allWrite) {
// вернулись в обычное состояние
state = 0;
}
}
break;
case 2:
state2:
// Место в буфере кончилось, просто считаем нужное место
needExtraLen += writedSymbolLen;
break;
case 3:
state3:
// Был прочитан удлинившийся символ, который мог перезатереть читаемые.
// Конец буфера записи не достигнут, но писать туда нельзя.
// При этом известно, что место в буфере выделено
// с нужным запасом, и сейчас мы просто запоминаем считанные символы, чтобы
// потом вписать их когда позволит указатель чтения
if (writePos + writedSymbolLen <= readPos || readPos == endReadPos) {
// Опа, символ стал короче и снова влезает в буфер. Или все прочитали, можно записывать
std::char_traits<u8s>::copy(dest, tempStore.symbols(), needExtraLen);
writeUtf8Symbol(writePos, writedSymbol);
state = 0;
} else {
if (writePos + writedSymbolLen > endWritePos) {
// Таки всё-равно не влезем в буфер
state = 2;
goto state2;
}
writeUtf8Symbol(tempWrite, writedSymbol);
writePos += writedSymbolLen;
needExtraLen += writedSymbolLen;
}
break;
}
readFromPos = readPos;
}
if (state == 0 || state == 1) {
// все отлично, можно возвращать получившуюся длину
src = reinterpret_cast<const u8s*>(readFromPos);
dest = reinterpret_cast<u8s*>(writePos);
return static_cast<size_t>(writePos - beginWritePos);
} else {
size_t writedCount = static_cast<size_t>(dest - (u8s*)beginWritePos);
size_t needBuffer = writedCount + needExtraLen;
if (needBuffer <= lenBuffer) {
// значит попали во 2ое состояние и считали символы, так как перезатирали читаемые
// но видимо потом попались укоротившиеся и по итогу всё-равно все влезет, только
// нужно временно сохранять символы. Запустим еще раз
size_t readedCount = static_cast<size_t>((uu8s*)src - beginReadPos);
len -= readedCount;
lenBuffer -= writedCount; // по идее они должны быть одинаковые
utf8_case_change<Op>(src, len, dest, lenBuffer + 1);
}
// в dest и src уже сохранены последние позиции. Вернем нужную длину
return needBuffer;
}
}
SIMSTR_API size_t unicode_traits<u8s>::upper(const u8s*& src, size_t len, u8s*& dest, size_t lenBuffer) {
return utf8_case_change<makeUpperU>(src, len, dest, lenBuffer);
}
SIMSTR_API size_t unicode_traits<u8s>::lower(const u8s*& src, size_t len, u8s*& dest, size_t lenBuffer) {
return utf8_case_change<makeLowerU>(src, len, dest, lenBuffer);
}
template<auto Op>
void utf16_change_case(const u16s* src, size_t len, u16s* dest) {
if (dest != src) {
// не inplace - надо писать все символы
for (size_t l = 0; l < len; l++)
dest[l] = static_cast<u16s>(Op(src[l]));
} else {
// inplace - будем писать только измененные символы
for (size_t l = 0; l < len; l++) {
u16s s = src[l], s1 = static_cast<u16s>(Op(s));
if (s != s1)
dest[l] = s1;
}
}
}
void unicode_traits<u16s>::upper(const u16s* src, size_t len, u16s* dest) {
utf16_change_case<makeUpperU>(src, len, dest);
}
void unicode_traits<u16s>::lower(const u16s* src, size_t len, u16s* dest) {
utf16_change_case<makeLowerU>(src, len, dest);
}
template<auto Op>
void utf32_change_case(const u32s* src, size_t len, u32s* dest) {
if (dest != src) {
// не inplace - надо писать все символы
for (size_t l = 0; l < len; l++)
*dest++ = Op(*src++);
} else {
// inplace - будем писать только измененные символы
for (size_t l = 0; l < len; l++, src++, dest++) {
u32s s = *src, s1 = Op(s);
if (s != s1)
*dest = s1;
}
}
}
SIMSTR_API void unicode_traits<u32s>::upper(const u32s* src, size_t len, u32s* dest) {
utf32_change_case<makeUpperU>(src, len, dest);
}
SIMSTR_API void unicode_traits<u32s>::lower(const u32s* src, size_t len, u32s* dest) {
utf32_change_case<makeLowerU>(src, len, dest);
}
SIMSTR_API int unicode_traits<u8s>::compareiu(const u8s* text1, size_t len1, const u8s* text2, size_t len2) {
if (!len1) {
return len2 == 0 ? 0 : -1;
} else if (!len2)
return 1;
const uu8s *ptr1 = reinterpret_cast<const uu8s*>(text1), *ptr2 = reinterpret_cast<const uu8s*>(text2),
*ptr1End = ptr1 + len1, *ptr2End = ptr2 + len2;
for (;;) {
u32s s1 = makeFoldU(readUtf8Symbol(ptr1, ptr1End)), s2 = makeFoldU(readUtf8Symbol(ptr2, ptr2End));
if (s1 > s2)
return 1;
else if (s1 < s2)
return -1;
else if (ptr1 >= ptr1End)
return ptr2 >= ptr2End ? 0 : -1;
else if (ptr2 >= ptr2End)
return 1;
}
}
SIMSTR_API int unicode_traits<u16s>::compareiu(const u16s* text1, size_t len1, const u16s* text2, size_t len2) {
if (!len1) {
return len2 == 0 ? 0 : -1;
} else if (!len2)
return 1;
const u16s *ptr1End = text1 + len1, *ptr2End = text2 + len2;
for (;;) {
u32s s1 = makeFoldU(readUtf16Symbol(text1, ptr1End)), s2 = makeFoldU(readUtf16Symbol(text2, ptr2End));
if (s1 > s2)
return 1;
else if (s1 < s2)
return -1;
else if (text1 >= ptr1End)
return text2 >= ptr2End ? 0 : -1;
else if (text2 >= ptr2End)
return 1;
}
}
SIMSTR_API int unicode_traits<u32s>::compareiu(const u32s* text1, size_t len1, const u32s* text2, size_t len2) {
if (!len1) {
return len2 == 0 ? 0 : -1;
} else if (!len2)
return 1;
const u32s *ptr1End = text1 + len1, *ptr2End = text2 + len2;
for (;;) {
u32s s1 = makeFoldU(*text1++), s2 = makeFoldU(*text2++);
if (s1 > s2)
return 1;
else if (s1 < s2)
return -1;
else if (text1 > ptr1End)
return text2 >= ptr2End ? 0 : -1;
else if (text2 > ptr2End)
return 1;
}
}
SIMSTR_API size_t unicode_traits<u8s>::hashia(const u8s* src, size_t l) {
size_t h = fnv::basis;
for (size_t i = 0; i < l; i++) {
h = (h ^ (uu8s)makeAsciiLower(src[i])) * fnv::prime;
}
return h;
}
SIMSTR_API size_t unicode_traits<u8s>::hashiu(const u8s* src, size_t l) {
size_t h = fnv::basis;
const uu8s *ptr = (const uu8s*)src, *pEnd = ptr + l;
while (ptr < pEnd) {
h = (h ^ makeFoldU(readUtf8Symbol(ptr, pEnd))) * fnv::prime;
}
return h;
}
SIMSTR_API size_t unicode_traits<u16s>::hashia(const u16s* src, size_t l) {
size_t h = fnv::basis;
for (size_t i = 0; i < l; i++) {
h = (h ^ makeAsciiLower(src[i])) * fnv::prime;
}
return h;
}
SIMSTR_API size_t unicode_traits<u16s>::hashiu(const u16s* src, size_t l) {
size_t h = fnv::basis;
const u16s* pEnd = src + l;
while (src < pEnd) {
h = (h ^ makeFoldU(readUtf16Symbol(src, pEnd))) * fnv::prime;
}
return h;
}
SIMSTR_API size_t unicode_traits<u32s>::hashia(const u32s* src, size_t l) {
size_t h = fnv::basis;
for (size_t i = 0; i < l; i++) {
h = (h ^ makeAsciiLower(src[i])) * fnv::prime;
}
return h;
}
SIMSTR_API size_t unicode_traits<u32s>::hashiu(const u32s* src, size_t l) {
size_t h = fnv::basis;
for (size_t i = 0; i < l; i++) {
h = (h ^ makeFoldU(src[i])) * fnv::prime;
}
return h;
}
namespace {
// Based on simdjson sources https://github.com/simdjson/simdjson/blob/667d0ed3c77f55cbda2082b034168d69898d1f88/include/simdjson/compile_time_json-inl.h#L189
// Counts the number of leading zeros in a 64-bit integer.
int leading_zeroes(uint64_t input_num) {
#if defined __has_builtin
# if __has_builtin (__builtin_clzll)
# define HAS_BLT_CLZ
# endif
#endif
#ifdef HAS_BLT_CLZ
return __builtin_clzll(input_num);
#else
int last_bit = 0;
if (input_num & uint64_t(0xffffffff00000000)) {
input_num >>= 32;
last_bit |= 32;
}
if (input_num & uint64_t(0xffff0000)) {
input_num >>= 16;
last_bit |= 16;
}
if (input_num & uint64_t(0xff00)) {
input_num >>= 8;
last_bit |= 8;
}
if (input_num & uint64_t(0xf0)) {
input_num >>= 4;
last_bit |= 4;
}
if (input_num & uint64_t(0xc)) {
input_num >>= 2;
last_bit |= 2;
}
if (input_num & uint64_t(0x2)) { /* input_num >>= 1; */
last_bit |= 1;
}
return 63 - last_bit;
#endif
}
// Multiplies two 32-bit unsigned integers and returns a 64-bit result.
uint64_t emulu(uint32_t x, uint32_t y) {
return x * (uint64_t)y;
}
uint64_t umul128_generic(uint64_t ab, uint64_t cd, uint64_t* hi) {
uint64_t ad = emulu((uint32_t)(ab >> 32), (uint32_t)cd);
uint64_t bd = emulu((uint32_t)ab, (uint32_t)cd);
uint64_t adbc = ad + emulu((uint32_t)ab, (uint32_t)(cd >> 32));
uint64_t adbc_carry = (uint64_t)(adbc < ad);
uint64_t lo = bd + (adbc << 32);
*hi = emulu((uint32_t)(ab >> 32), (uint32_t)(cd >> 32)) + (adbc >> 32) + (adbc_carry << 32) + (uint64_t)(lo < bd);
return lo;
}
// Represents a 128-bit unsigned integer as two 64-bit parts.
// We have a value128 struct elsewhere in the simdjson, but we
// use a separate one here for clarity.
struct value128 {
uint64_t low;
uint64_t high;
constexpr value128(uint64_t _low, uint64_t _high) : low(_low), high(_high) {}
constexpr value128() : low(0), high(0) {}
};
// Multiplies two 64-bit integers and returns a 128-bit result as value128.
value128 full_multiplication(uint64_t a, uint64_t b) {
value128 answer;
answer.low = umul128_generic(a, b, &answer.high);
return answer;
}
// Converts mantissa and exponent to a double, considering the sign.
double to_double(uint64_t mantissa, int64_t exponent, bool negative) {
uint64_t sign_bit = negative ? (1ULL << 63) : 0;
uint64_t exponent_bits = (uint64_t(exponent) & 0x7FF) << 52;
uint64_t bits = sign_bit | exponent_bits | (mantissa & ((1ULL << 52) - 1));
return std::bit_cast<double>(bits);
}
constexpr int smallest_power = -342;
constexpr int largest_power = 308;
// Precomputed powers of ten from 10^0 to 10^22. These
// can be represented exactly using the double type.
const double power_of_ten[] = {
1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11,
1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22
};
/**
* When mapping numbers from decimal to binary,
* we go from w * 10^q to m * 2^p but we have
* 10^q = 5^q * 2^q, so effectively
* we are trying to match
* w * 2^q * 5^q to m * 2^p. Thus the powers of two
* are not a concern since they can be represented
* exactly using the binary notation, only the powers of five
* affect the binary significand.
*/
// The truncated powers of five from 5^-342 all the way to 5^308
// The mantissa is truncated to 128 bits, and
// never rounded up. Uses about 10KB.
const uint64_t power_of_five_128[]= {
0xeef453d6923bd65a,0x113faa2906a13b3f,
0x9558b4661b6565f8,0x4ac7ca59a424c507,
0xbaaee17fa23ebf76,0x5d79bcf00d2df649,
0xe95a99df8ace6f53,0xf4d82c2c107973dc,
0x91d8a02bb6c10594,0x79071b9b8a4be869,
0xb64ec836a47146f9,0x9748e2826cdee284,
0xe3e27a444d8d98b7,0xfd1b1b2308169b25,
0x8e6d8c6ab0787f72,0xfe30f0f5e50e20f7,
0xb208ef855c969f4f,0xbdbd2d335e51a935,
0xde8b2b66b3bc4723,0xad2c788035e61382,
0x8b16fb203055ac76,0x4c3bcb5021afcc31,
0xaddcb9e83c6b1793,0xdf4abe242a1bbf3d,
0xd953e8624b85dd78,0xd71d6dad34a2af0d,
0x87d4713d6f33aa6b,0x8672648c40e5ad68,
0xa9c98d8ccb009506,0x680efdaf511f18c2,
0xd43bf0effdc0ba48,0x212bd1b2566def2,
0x84a57695fe98746d,0x14bb630f7604b57,
0xa5ced43b7e3e9188,0x419ea3bd35385e2d,
0xcf42894a5dce35ea,0x52064cac828675b9,
0x818995ce7aa0e1b2,0x7343efebd1940993,
0xa1ebfb4219491a1f,0x1014ebe6c5f90bf8,
0xca66fa129f9b60a6,0xd41a26e077774ef6,
0xfd00b897478238d0,0x8920b098955522b4,
0x9e20735e8cb16382,0x55b46e5f5d5535b0,
0xc5a890362fddbc62,0xeb2189f734aa831d,
0xf712b443bbd52b7b,0xa5e9ec7501d523e4,
0x9a6bb0aa55653b2d,0x47b233c92125366e,
0xc1069cd4eabe89f8,0x999ec0bb696e840a,
0xf148440a256e2c76,0xc00670ea43ca250d,
0x96cd2a865764dbca,0x380406926a5e5728,
0xbc807527ed3e12bc,0xc605083704f5ecf2,
0xeba09271e88d976b,0xf7864a44c633682e,
0x93445b8731587ea3,0x7ab3ee6afbe0211d,
0xb8157268fdae9e4c,0x5960ea05bad82964,
0xe61acf033d1a45df,0x6fb92487298e33bd,
0x8fd0c16206306bab,0xa5d3b6d479f8e056,
0xb3c4f1ba87bc8696,0x8f48a4899877186c,
0xe0b62e2929aba83c,0x331acdabfe94de87,
0x8c71dcd9ba0b4925,0x9ff0c08b7f1d0b14,
0xaf8e5410288e1b6f,0x7ecf0ae5ee44dd9,
0xdb71e91432b1a24a,0xc9e82cd9f69d6150,
0x892731ac9faf056e,0xbe311c083a225cd2,
0xab70fe17c79ac6ca,0x6dbd630a48aaf406,
0xd64d3d9db981787d,0x92cbbccdad5b108,
0x85f0468293f0eb4e,0x25bbf56008c58ea5,
0xa76c582338ed2621,0xaf2af2b80af6f24e,
0xd1476e2c07286faa,0x1af5af660db4aee1,
0x82cca4db847945ca,0x50d98d9fc890ed4d,
0xa37fce126597973c,0xe50ff107bab528a0,
0xcc5fc196fefd7d0c,0x1e53ed49a96272c8,
0xff77b1fcbebcdc4f,0x25e8e89c13bb0f7a,
0x9faacf3df73609b1,0x77b191618c54e9ac,
0xc795830d75038c1d,0xd59df5b9ef6a2417,
0xf97ae3d0d2446f25,0x4b0573286b44ad1d,
0x9becce62836ac577,0x4ee367f9430aec32,
0xc2e801fb244576d5,0x229c41f793cda73f,
0xf3a20279ed56d48a,0x6b43527578c1110f,
0x9845418c345644d6,0x830a13896b78aaa9,
0xbe5691ef416bd60c,0x23cc986bc656d553,
0xedec366b11c6cb8f,0x2cbfbe86b7ec8aa8,
0x94b3a202eb1c3f39,0x7bf7d71432f3d6a9,
0xb9e08a83a5e34f07,0xdaf5ccd93fb0cc53,
0xe858ad248f5c22c9,0xd1b3400f8f9cff68,
0x91376c36d99995be,0x23100809b9c21fa1,
0xb58547448ffffb2d,0xabd40a0c2832a78a,
0xe2e69915b3fff9f9,0x16c90c8f323f516c,
0x8dd01fad907ffc3b,0xae3da7d97f6792e3,
0xb1442798f49ffb4a,0x99cd11cfdf41779c,
0xdd95317f31c7fa1d,0x40405643d711d583,
0x8a7d3eef7f1cfc52,0x482835ea666b2572,
0xad1c8eab5ee43b66,0xda3243650005eecf,
0xd863b256369d4a40,0x90bed43e40076a82,
0x873e4f75e2224e68,0x5a7744a6e804a291,
0xa90de3535aaae202,0x711515d0a205cb36,
0xd3515c2831559a83,0xd5a5b44ca873e03,
0x8412d9991ed58091,0xe858790afe9486c2,
0xa5178fff668ae0b6,0x626e974dbe39a872,
0xce5d73ff402d98e3,0xfb0a3d212dc8128f,
0x80fa687f881c7f8e,0x7ce66634bc9d0b99,
0xa139029f6a239f72,0x1c1fffc1ebc44e80,
0xc987434744ac874e,0xa327ffb266b56220,
0xfbe9141915d7a922,0x4bf1ff9f0062baa8,
0x9d71ac8fada6c9b5,0x6f773fc3603db4a9,
0xc4ce17b399107c22,0xcb550fb4384d21d3,
0xf6019da07f549b2b,0x7e2a53a146606a48,
0x99c102844f94e0fb,0x2eda7444cbfc426d,
0xc0314325637a1939,0xfa911155fefb5308,
0xf03d93eebc589f88,0x793555ab7eba27ca,
0x96267c7535b763b5,0x4bc1558b2f3458de,
0xbbb01b9283253ca2,0x9eb1aaedfb016f16,
0xea9c227723ee8bcb,0x465e15a979c1cadc,
0x92a1958a7675175f,0xbfacd89ec191ec9,
0xb749faed14125d36,0xcef980ec671f667b,
0xe51c79a85916f484,0x82b7e12780e7401a,
0x8f31cc0937ae58d2,0xd1b2ecb8b0908810,
0xb2fe3f0b8599ef07,0x861fa7e6dcb4aa15,
0xdfbdcece67006ac9,0x67a791e093e1d49a,
0x8bd6a141006042bd,0xe0c8bb2c5c6d24e0,
0xaecc49914078536d,0x58fae9f773886e18,
0xda7f5bf590966848,0xaf39a475506a899e,
0x888f99797a5e012d,0x6d8406c952429603,
0xaab37fd7d8f58178,0xc8e5087ba6d33b83,
0xd5605fcdcf32e1d6,0xfb1e4a9a90880a64,
0x855c3be0a17fcd26,0x5cf2eea09a55067f,
0xa6b34ad8c9dfc06f,0xf42faa48c0ea481e,
0xd0601d8efc57b08b,0xf13b94daf124da26,
0x823c12795db6ce57,0x76c53d08d6b70858,
0xa2cb1717b52481ed,0x54768c4b0c64ca6e,
0xcb7ddcdda26da268,0xa9942f5dcf7dfd09,
0xfe5d54150b090b02,0xd3f93b35435d7c4c,
0x9efa548d26e5a6e1,0xc47bc5014a1a6daf,
0xc6b8e9b0709f109a,0x359ab6419ca1091b,
0xf867241c8cc6d4c0,0xc30163d203c94b62,
0x9b407691d7fc44f8,0x79e0de63425dcf1d,
0xc21094364dfb5636,0x985915fc12f542e4,
0xf294b943e17a2bc4,0x3e6f5b7b17b2939d,
0x979cf3ca6cec5b5a,0xa705992ceecf9c42,
0xbd8430bd08277231,0x50c6ff782a838353,
0xece53cec4a314ebd,0xa4f8bf5635246428,
0x940f4613ae5ed136,0x871b7795e136be99,
0xb913179899f68584,0x28e2557b59846e3f,
0xe757dd7ec07426e5,0x331aeada2fe589cf,
0x9096ea6f3848984f,0x3ff0d2c85def7621,
0xb4bca50b065abe63,0xfed077a756b53a9,
0xe1ebce4dc7f16dfb,0xd3e8495912c62894,
0x8d3360f09cf6e4bd,0x64712dd7abbbd95c,
0xb080392cc4349dec,0xbd8d794d96aacfb3,
0xdca04777f541c567,0xecf0d7a0fc5583a0,
0x89e42caaf9491b60,0xf41686c49db57244,
0xac5d37d5b79b6239,0x311c2875c522ced5,
0xd77485cb25823ac7,0x7d633293366b828b,
0x86a8d39ef77164bc,0xae5dff9c02033197,
0xa8530886b54dbdeb,0xd9f57f830283fdfc,
0xd267caa862a12d66,0xd072df63c324fd7b,
0x8380dea93da4bc60,0x4247cb9e59f71e6d,
0xa46116538d0deb78,0x52d9be85f074e608,
0xcd795be870516656,0x67902e276c921f8b,
0x806bd9714632dff6,0xba1cd8a3db53b6,
0xa086cfcd97bf97f3,0x80e8a40eccd228a4,
0xc8a883c0fdaf7df0,0x6122cd128006b2cd,
0xfad2a4b13d1b5d6c,0x796b805720085f81,
0x9cc3a6eec6311a63,0xcbe3303674053bb0,
0xc3f490aa77bd60fc,0xbedbfc4411068a9c,
0xf4f1b4d515acb93b,0xee92fb5515482d44,
0x991711052d8bf3c5,0x751bdd152d4d1c4a,
0xbf5cd54678eef0b6,0xd262d45a78a0635d,
0xef340a98172aace4,0x86fb897116c87c34,
0x9580869f0e7aac0e,0xd45d35e6ae3d4da0,
0xbae0a846d2195712,0x8974836059cca109,
0xe998d258869facd7,0x2bd1a438703fc94b,
0x91ff83775423cc06,0x7b6306a34627ddcf,
0xb67f6455292cbf08,0x1a3bc84c17b1d542,
0xe41f3d6a7377eeca,0x20caba5f1d9e4a93,
0x8e938662882af53e,0x547eb47b7282ee9c,
0xb23867fb2a35b28d,0xe99e619a4f23aa43,
0xdec681f9f4c31f31,0x6405fa00e2ec94d4,
0x8b3c113c38f9f37e,0xde83bc408dd3dd04,
0xae0b158b4738705e,0x9624ab50b148d445,
0xd98ddaee19068c76,0x3badd624dd9b0957,
0x87f8a8d4cfa417c9,0xe54ca5d70a80e5d6,
0xa9f6d30a038d1dbc,0x5e9fcf4ccd211f4c,
0xd47487cc8470652b,0x7647c3200069671f,
0x84c8d4dfd2c63f3b,0x29ecd9f40041e073,
0xa5fb0a17c777cf09,0xf468107100525890,
0xcf79cc9db955c2cc,0x7182148d4066eeb4,
0x81ac1fe293d599bf,0xc6f14cd848405530,
0xa21727db38cb002f,0xb8ada00e5a506a7c,
0xca9cf1d206fdc03b,0xa6d90811f0e4851c,
0xfd442e4688bd304a,0x908f4a166d1da663,
0x9e4a9cec15763e2e,0x9a598e4e043287fe,
0xc5dd44271ad3cdba,0x40eff1e1853f29fd,
0xf7549530e188c128,0xd12bee59e68ef47c,
0x9a94dd3e8cf578b9,0x82bb74f8301958ce,
0xc13a148e3032d6e7,0xe36a52363c1faf01,
0xf18899b1bc3f8ca1,0xdc44e6c3cb279ac1,
0x96f5600f15a7b7e5,0x29ab103a5ef8c0b9,
0xbcb2b812db11a5de,0x7415d448f6b6f0e7,
0xebdf661791d60f56,0x111b495b3464ad21,
0x936b9fcebb25c995,0xcab10dd900beec34,
0xb84687c269ef3bfb,0x3d5d514f40eea742,
0xe65829b3046b0afa,0xcb4a5a3112a5112,
0x8ff71a0fe2c2e6dc,0x47f0e785eaba72ab,
0xb3f4e093db73a093,0x59ed216765690f56,
0xe0f218b8d25088b8,0x306869c13ec3532c,
0x8c974f7383725573,0x1e414218c73a13fb,
0xafbd2350644eeacf,0xe5d1929ef90898fa,
0xdbac6c247d62a583,0xdf45f746b74abf39,
0x894bc396ce5da772,0x6b8bba8c328eb783,
0xab9eb47c81f5114f,0x66ea92f3f326564,
0xd686619ba27255a2,0xc80a537b0efefebd,
0x8613fd0145877585,0xbd06742ce95f5f36,
0xa798fc4196e952e7,0x2c48113823b73704,
0xd17f3b51fca3a7a0,0xf75a15862ca504c5,
0x82ef85133de648c4,0x9a984d73dbe722fb,
0xa3ab66580d5fdaf5,0xc13e60d0d2e0ebba,
0xcc963fee10b7d1b3,0x318df905079926a8,
0xffbbcfe994e5c61f,0xfdf17746497f7052,
0x9fd561f1fd0f9bd3,0xfeb6ea8bedefa633,
0xc7caba6e7c5382c8,0xfe64a52ee96b8fc0,
0xf9bd690a1b68637b,0x3dfdce7aa3c673b0,
0x9c1661a651213e2d,0x6bea10ca65c084e,
0xc31bfa0fe5698db8,0x486e494fcff30a62,
0xf3e2f893dec3f126,0x5a89dba3c3efccfa,
0x986ddb5c6b3a76b7,0xf89629465a75e01c,
0xbe89523386091465,0xf6bbb397f1135823,
0xee2ba6c0678b597f,0x746aa07ded582e2c,
0x94db483840b717ef,0xa8c2a44eb4571cdc,
0xba121a4650e4ddeb,0x92f34d62616ce413,
0xe896a0d7e51e1566,0x77b020baf9c81d17,
0x915e2486ef32cd60,0xace1474dc1d122e,
0xb5b5ada8aaff80b8,0xd819992132456ba,
0xe3231912d5bf60e6,0x10e1fff697ed6c69,
0x8df5efabc5979c8f,0xca8d3ffa1ef463c1,
0xb1736b96b6fd83b3,0xbd308ff8a6b17cb2,
0xddd0467c64bce4a0,0xac7cb3f6d05ddbde,
0x8aa22c0dbef60ee4,0x6bcdf07a423aa96b,
0xad4ab7112eb3929d,0x86c16c98d2c953c6,
0xd89d64d57a607744,0xe871c7bf077ba8b7,
0x87625f056c7c4a8b,0x11471cd764ad4972,
0xa93af6c6c79b5d2d,0xd598e40d3dd89bcf,
0xd389b47879823479,0x4aff1d108d4ec2c3,
0x843610cb4bf160cb,0xcedf722a585139ba,
0xa54394fe1eedb8fe,0xc2974eb4ee658828,
0xce947a3da6a9273e,0x733d226229feea32,
0x811ccc668829b887,0x806357d5a3f525f,
0xa163ff802a3426a8,0xca07c2dcb0cf26f7,
0xc9bcff6034c13052,0xfc89b393dd02f0b5,
0xfc2c3f3841f17c67,0xbbac2078d443ace2,
0x9d9ba7832936edc0,0xd54b944b84aa4c0d,
0xc5029163f384a931,0xa9e795e65d4df11,
0xf64335bcf065d37d,0x4d4617b5ff4a16d5,
0x99ea0196163fa42e,0x504bced1bf8e4e45,
0xc06481fb9bcf8d39,0xe45ec2862f71e1d6,
0xf07da27a82c37088,0x5d767327bb4e5a4c,
0x964e858c91ba2655,0x3a6a07f8d510f86f,
0xbbe226efb628afea,0x890489f70a55368b,
0xeadab0aba3b2dbe5,0x2b45ac74ccea842e,
0x92c8ae6b464fc96f,0x3b0b8bc90012929d,
0xb77ada0617e3bbcb,0x9ce6ebb40173744,
0xe55990879ddcaabd,0xcc420a6a101d0515,
0x8f57fa54c2a9eab6,0x9fa946824a12232d,
0xb32df8e9f3546564,0x47939822dc96abf9,
0xdff9772470297ebd,0x59787e2b93bc56f7,
0x8bfbea76c619ef36,0x57eb4edb3c55b65a,
0xaefae51477a06b03,0xede622920b6b23f1,
0xdab99e59958885c4,0xe95fab368e45eced,
0x88b402f7fd75539b,0x11dbcb0218ebb414,
0xaae103b5fcd2a881,0xd652bdc29f26a119,
0xd59944a37c0752a2,0x4be76d3346f0495f,
0x857fcae62d8493a5,0x6f70a4400c562ddb,
0xa6dfbd9fb8e5b88e,0xcb4ccd500f6bb952,
0xd097ad07a71f26b2,0x7e2000a41346a7a7,
0x825ecc24c873782f,0x8ed400668c0c28c8,
0xa2f67f2dfa90563b,0x728900802f0f32fa,
0xcbb41ef979346bca,0x4f2b40a03ad2ffb9,
0xfea126b7d78186bc,0xe2f610c84987bfa8,
0x9f24b832e6b0f436,0xdd9ca7d2df4d7c9,
0xc6ede63fa05d3143,0x91503d1c79720dbb,
0xf8a95fcf88747d94,0x75a44c6397ce912a,
0x9b69dbe1b548ce7c,0xc986afbe3ee11aba,
0xc24452da229b021b,0xfbe85badce996168,
0xf2d56790ab41c2a2,0xfae27299423fb9c3,
0x97c560ba6b0919a5,0xdccd879fc967d41a,
0xbdb6b8e905cb600f,0x5400e987bbc1c920,
0xed246723473e3813,0x290123e9aab23b68,
0x9436c0760c86e30b,0xf9a0b6720aaf6521,
0xb94470938fa89bce,0xf808e40e8d5b3e69,
0xe7958cb87392c2c2,0xb60b1d1230b20e04,
0x90bd77f3483bb9b9,0xb1c6f22b5e6f48c2,
0xb4ecd5f01a4aa828,0x1e38aeb6360b1af3,
0xe2280b6c20dd5232,0x25c6da63c38de1b0,
0x8d590723948a535f,0x579c487e5a38ad0e,
0xb0af48ec79ace837,0x2d835a9df0c6d851,
0xdcdb1b2798182244,0xf8e431456cf88e65,
0x8a08f0f8bf0f156b,0x1b8e9ecb641b58ff,
0xac8b2d36eed2dac5,0xe272467e3d222f3f,
0xd7adf884aa879177,0x5b0ed81dcc6abb0f,
0x86ccbb52ea94baea,0x98e947129fc2b4e9,
0xa87fea27a539e9a5,0x3f2398d747b36224,
0xd29fe4b18e88640e,0x8eec7f0d19a03aad,
0x83a3eeeef9153e89,0x1953cf68300424ac,
0xa48ceaaab75a8e2b,0x5fa8c3423c052dd7,
0xcdb02555653131b6,0x3792f412cb06794d,
0x808e17555f3ebf11,0xe2bbd88bbee40bd0,
0xa0b19d2ab70e6ed6,0x5b6aceaeae9d0ec4,
0xc8de047564d20a8b,0xf245825a5a445275,
0xfb158592be068d2e,0xeed6e2f0f0d56712,
0x9ced737bb6c4183d,0x55464dd69685606b,
0xc428d05aa4751e4c,0xaa97e14c3c26b886,
0xf53304714d9265df,0xd53dd99f4b3066a8,
0x993fe2c6d07b7fab,0xe546a8038efe4029,
0xbf8fdb78849a5f96,0xde98520472bdd033,
0xef73d256a5c0f77c,0x963e66858f6d4440,
0x95a8637627989aad,0xdde7001379a44aa8,
0xbb127c53b17ec159,0x5560c018580d5d52,
0xe9d71b689dde71af,0xaab8f01e6e10b4a6,
0x9226712162ab070d,0xcab3961304ca70e8,
0xb6b00d69bb55c8d1,0x3d607b97c5fd0d22,
0xe45c10c42a2b3b05,0x8cb89a7db77c506a,
0x8eb98a7a9a5b04e3,0x77f3608e92adb242,
0xb267ed1940f1c61c,0x55f038b237591ed3,
0xdf01e85f912e37a3,0x6b6c46dec52f6688,
0x8b61313bbabce2c6,0x2323ac4b3b3da015,
0xae397d8aa96c1b77,0xabec975e0a0d081a,
0xd9c7dced53c72255,0x96e7bd358c904a21,
0x881cea14545c7575,0x7e50d64177da2e54,
0xaa242499697392d2,0xdde50bd1d5d0b9e9,
0xd4ad2dbfc3d07787,0x955e4ec64b44e864,
0x84ec3c97da624ab4,0xbd5af13bef0b113e,
0xa6274bbdd0fadd61,0xecb1ad8aeacdd58e,
0xcfb11ead453994ba,0x67de18eda5814af2,
0x81ceb32c4b43fcf4,0x80eacf948770ced7,
0xa2425ff75e14fc31,0xa1258379a94d028d,
0xcad2f7f5359a3b3e,0x96ee45813a04330,
0xfd87b5f28300ca0d,0x8bca9d6e188853fc,
0x9e74d1b791e07e48,0x775ea264cf55347e,
0xc612062576589dda,0x95364afe032a81a0,
0xf79687aed3eec551,0x3a83ddbd83f52210,
0x9abe14cd44753b52,0xc4926a9672793580,
0xc16d9a0095928a27,0x75b7053c0f178400,
0xf1c90080baf72cb1,0x5324c68b12dd6800,
0x971da05074da7bee,0xd3f6fc16ebca8000,
0xbce5086492111aea,0x88f4bb1ca6bd0000,
0xec1e4a7db69561a5,0x2b31e9e3d0700000,
0x9392ee8e921d5d07,0x3aff322e62600000,
0xb877aa3236a4b449,0x9befeb9fad487c3,
0xe69594bec44de15b,0x4c2ebe687989a9b4,
0x901d7cf73ab0acd9,0xf9d37014bf60a11,
0xb424dc35095cd80f,0x538484c19ef38c95,
0xe12e13424bb40e13,0x2865a5f206b06fba,
0x8cbccc096f5088cb,0xf93f87b7442e45d4,
0xafebff0bcb24aafe,0xf78f69a51539d749,
0xdbe6fecebdedd5be,0xb573440e5a884d1c,
0x89705f4136b4a597,0x31680a88f8953031,
0xabcc77118461cefc,0xfdc20d2b36ba7c3e,
0xd6bf94d5e57a42bc,0x3d32907604691b4d,
0x8637bd05af6c69b5,0xa63f9a49c2c1b110,
0xa7c5ac471b478423,0xfcf80dc33721d54,
0xd1b71758e219652b,0xd3c36113404ea4a9,
0x83126e978d4fdf3b,0x645a1cac083126ea,
0xa3d70a3d70a3d70a,0x3d70a3d70a3d70a4,
0xcccccccccccccccc,0xcccccccccccccccd,
0x8000000000000000,0x0,
0xa000000000000000,0x0,
0xc800000000000000,0x0,
0xfa00000000000000,0x0,
0x9c40000000000000,0x0,
0xc350000000000000,0x0,
0xf424000000000000,0x0,
0x9896800000000000,0x0,
0xbebc200000000000,0x0,
0xee6b280000000000,0x0,
0x9502f90000000000,0x0,
0xba43b74000000000,0x0,
0xe8d4a51000000000,0x0,
0x9184e72a00000000,0x0,
0xb5e620f480000000,0x0,
0xe35fa931a0000000,0x0,
0x8e1bc9bf04000000,0x0,
0xb1a2bc2ec5000000,0x0,
0xde0b6b3a76400000,0x0,
0x8ac7230489e80000,0x0,
0xad78ebc5ac620000,0x0,
0xd8d726b7177a8000,0x0,
0x878678326eac9000,0x0,
0xa968163f0a57b400,0x0,
0xd3c21bcecceda100,0x0,
0x84595161401484a0,0x0,
0xa56fa5b99019a5c8,0x0,
0xcecb8f27f4200f3a,0x0,
0x813f3978f8940984,0x4000000000000000,
0xa18f07d736b90be5,0x5000000000000000,
0xc9f2c9cd04674ede,0xa400000000000000,
0xfc6f7c4045812296,0x4d00000000000000,
0x9dc5ada82b70b59d,0xf020000000000000,
0xc5371912364ce305,0x6c28000000000000,
0xf684df56c3e01bc6,0xc732000000000000,
0x9a130b963a6c115c,0x3c7f400000000000,
0xc097ce7bc90715b3,0x4b9f100000000000,
0xf0bdc21abb48db20,0x1e86d40000000000,
0x96769950b50d88f4,0x1314448000000000,
0xbc143fa4e250eb31,0x17d955a000000000,
0xeb194f8e1ae525fd,0x5dcfab0800000000,
0x92efd1b8d0cf37be,0x5aa1cae500000000,
0xb7abc627050305ad,0xf14a3d9e40000000,
0xe596b7b0c643c719,0x6d9ccd05d0000000,
0x8f7e32ce7bea5c6f,0xe4820023a2000000,
0xb35dbf821ae4f38b,0xdda2802c8a800000,
0xe0352f62a19e306e,0xd50b2037ad200000,
0x8c213d9da502de45,0x4526f422cc340000,
0xaf298d050e4395d6,0x9670b12b7f410000,
0xdaf3f04651d47b4c,0x3c0cdd765f114000,
0x88d8762bf324cd0f,0xa5880a69fb6ac800,
0xab0e93b6efee0053,0x8eea0d047a457a00,
0xd5d238a4abe98068,0x72a4904598d6d880,
0x85a36366eb71f041,0x47a6da2b7f864750,
0xa70c3c40a64e6c51,0x999090b65f67d924,
0xd0cf4b50cfe20765,0xfff4b4e3f741cf6d,
0x82818f1281ed449f,0xbff8f10e7a8921a4,
0xa321f2d7226895c7,0xaff72d52192b6a0d,
0xcbea6f8ceb02bb39,0x9bf4f8a69f764490,
0xfee50b7025c36a08,0x2f236d04753d5b4,
0x9f4f2726179a2245,0x1d762422c946590,
0xc722f0ef9d80aad6,0x424d3ad2b7b97ef5,
0xf8ebad2b84e0d58b,0xd2e0898765a7deb2,
0x9b934c3b330c8577,0x63cc55f49f88eb2f,
0xc2781f49ffcfa6d5,0x3cbf6b71c76b25fb,
0xf316271c7fc3908a,0x8bef464e3945ef7a,
0x97edd871cfda3a56,0x97758bf0e3cbb5ac,
0xbde94e8e43d0c8ec,0x3d52eeed1cbea317,
0xed63a231d4c4fb27,0x4ca7aaa863ee4bdd,
0x945e455f24fb1cf8,0x8fe8caa93e74ef6a,
0xb975d6b6ee39e436,0xb3e2fd538e122b44,
0xe7d34c64a9c85d44,0x60dbbca87196b616,
0x90e40fbeea1d3a4a,0xbc8955e946fe31cd,
0xb51d13aea4a488dd,0x6babab6398bdbe41,
0xe264589a4dcdab14,0xc696963c7eed2dd1,
0x8d7eb76070a08aec,0xfc1e1de5cf543ca2,
0xb0de65388cc8ada8,0x3b25a55f43294bcb,
0xdd15fe86affad912,0x49ef0eb713f39ebe,
0x8a2dbf142dfcc7ab,0x6e3569326c784337,
0xacb92ed9397bf996,0x49c2c37f07965404,
0xd7e77a8f87daf7fb,0xdc33745ec97be906,
0x86f0ac99b4e8dafd,0x69a028bb3ded71a3,
0xa8acd7c0222311bc,0xc40832ea0d68ce0c,
0xd2d80db02aabd62b,0xf50a3fa490c30190,
0x83c7088e1aab65db,0x792667c6da79e0fa,
0xa4b8cab1a1563f52,0x577001b891185938,
0xcde6fd5e09abcf26,0xed4c0226b55e6f86,
0x80b05e5ac60b6178,0x544f8158315b05b4,
0xa0dc75f1778e39d6,0x696361ae3db1c721,
0xc913936dd571c84c,0x3bc3a19cd1e38e9,
0xfb5878494ace3a5f,0x4ab48a04065c723,
0x9d174b2dcec0e47b,0x62eb0d64283f9c76,
0xc45d1df942711d9a,0x3ba5d0bd324f8394,
0xf5746577930d6500,0xca8f44ec7ee36479,
0x9968bf6abbe85f20,0x7e998b13cf4e1ecb,
0xbfc2ef456ae276e8,0x9e3fedd8c321a67e,
0xefb3ab16c59b14a2,0xc5cfe94ef3ea101e,
0x95d04aee3b80ece5,0xbba1f1d158724a12,
0xbb445da9ca61281f,0x2a8a6e45ae8edc97,
0xea1575143cf97226,0xf52d09d71a3293bd,
0x924d692ca61be758,0x593c2626705f9c56,
0xb6e0c377cfa2e12e,0x6f8b2fb00c77836c,
0xe498f455c38b997a,0xb6dfb9c0f956447,
0x8edf98b59a373fec,0x4724bd4189bd5eac,
0xb2977ee300c50fe7,0x58edec91ec2cb657,
0xdf3d5e9bc0f653e1,0x2f2967b66737e3ed,
0x8b865b215899f46c,0xbd79e0d20082ee74,
0xae67f1e9aec07187,0xecd8590680a3aa11,
0xda01ee641a708de9,0xe80e6f4820cc9495,
0x884134fe908658b2,0x3109058d147fdcdd,
0xaa51823e34a7eede,0xbd4b46f0599fd415,
0xd4e5e2cdc1d1ea96,0x6c9e18ac7007c91a,
0x850fadc09923329e,0x3e2cf6bc604ddb0,
0xa6539930bf6bff45,0x84db8346b786151c,
0xcfe87f7cef46ff16,0xe612641865679a63,
0x81f14fae158c5f6e,0x4fcb7e8f3f60c07e,
0xa26da3999aef7749,0xe3be5e330f38f09d,
0xcb090c8001ab551c,0x5cadf5bfd3072cc5,
0xfdcb4fa002162a63,0x73d9732fc7c8f7f6,
0x9e9f11c4014dda7e,0x2867e7fddcdd9afa,
0xc646d63501a1511d,0xb281e1fd541501b8,
0xf7d88bc24209a565,0x1f225a7ca91a4226,
0x9ae757596946075f,0x3375788de9b06958,
0xc1a12d2fc3978937,0x52d6b1641c83ae,
0xf209787bb47d6b84,0xc0678c5dbd23a49a,
0x9745eb4d50ce6332,0xf840b7ba963646e0,
0xbd176620a501fbff,0xb650e5a93bc3d898,
0xec5d3fa8ce427aff,0xa3e51f138ab4cebe,
0x93ba47c980e98cdf,0xc66f336c36b10137,
0xb8a8d9bbe123f017,0xb80b0047445d4184,
0xe6d3102ad96cec1d,0xa60dc059157491e5,
0x9043ea1ac7e41392,0x87c89837ad68db2f,
0xb454e4a179dd1877,0x29babe4598c311fb,
0xe16a1dc9d8545e94,0xf4296dd6fef3d67a,
0x8ce2529e2734bb1d,0x1899e4a65f58660c,
0xb01ae745b101e9e4,0x5ec05dcff72e7f8f,
0xdc21a1171d42645d,0x76707543f4fa1f73,
0x899504ae72497eba,0x6a06494a791c53a8,
0xabfa45da0edbde69,0x487db9d17636892,
0xd6f8d7509292d603,0x45a9d2845d3c42b6,
0x865b86925b9bc5c2,0xb8a2392ba45a9b2,
0xa7f26836f282b732,0x8e6cac7768d7141e,
0xd1ef0244af2364ff,0x3207d795430cd926,
0x8335616aed761f1f,0x7f44e6bd49e807b8,
0xa402b9c5a8d3a6e7,0x5f16206c9c6209a6,
0xcd036837130890a1,0x36dba887c37a8c0f,
0x802221226be55a64,0xc2494954da2c9789,
0xa02aa96b06deb0fd,0xf2db9baa10b7bd6c,
0xc83553c5c8965d3d,0x6f92829494e5acc7,
0xfa42a8b73abbf48c,0xcb772339ba1f17f9,
0x9c69a97284b578d7,0xff2a760414536efb,
0xc38413cf25e2d70d,0xfef5138519684aba,
0xf46518c2ef5b8cd1,0x7eb258665fc25d69,
0x98bf2f79d5993802,0xef2f773ffbd97a61,
0xbeeefb584aff8603,0xaafb550ffacfd8fa,
0xeeaaba2e5dbf6784,0x95ba2a53f983cf38,
0x952ab45cfa97a0b2,0xdd945a747bf26183,
0xba756174393d88df,0x94f971119aeef9e4,
0xe912b9d1478ceb17,0x7a37cd5601aab85d,
0x91abb422ccb812ee,0xac62e055c10ab33a,
0xb616a12b7fe617aa,0x577b986b314d6009,
0xe39c49765fdf9d94,0xed5a7e85fda0b80b,
0x8e41ade9fbebc27d,0x14588f13be847307,
0xb1d219647ae6b31c,0x596eb2d8ae258fc8,
0xde469fbd99a05fe3,0x6fca5f8ed9aef3bb,
0x8aec23d680043bee,0x25de7bb9480d5854,
0xada72ccc20054ae9,0xaf561aa79a10ae6a,
0xd910f7ff28069da4,0x1b2ba1518094da04,
0x87aa9aff79042286,0x90fb44d2f05d0842,
0xa99541bf57452b28,0x353a1607ac744a53,
0xd3fa922f2d1675f2,0x42889b8997915ce8,
0x847c9b5d7c2e09b7,0x69956135febada11,
0xa59bc234db398c25,0x43fab9837e699095,
0xcf02b2c21207ef2e,0x94f967e45e03f4bb,
0x8161afb94b44f57d,0x1d1be0eebac278f5,
0xa1ba1ba79e1632dc,0x6462d92a69731732,
0xca28a291859bbf93,0x7d7b8f7503cfdcfe,
0xfcb2cb35e702af78,0x5cda735244c3d43e,
0x9defbf01b061adab,0x3a0888136afa64a7,
0xc56baec21c7a1916,0x88aaa1845b8fdd0,
0xf6c69a72a3989f5b,0x8aad549e57273d45,
0x9a3c2087a63f6399,0x36ac54e2f678864b,
0xc0cb28a98fcf3c7f,0x84576a1bb416a7dd,
0xf0fdf2d3f3c30b9f,0x656d44a2a11c51d5,
0x969eb7c47859e743,0x9f644ae5a4b1b325,
0xbc4665b596706114,0x873d5d9f0dde1fee,
0xeb57ff22fc0c7959,0xa90cb506d155a7ea,
0x9316ff75dd87cbd8,0x9a7f12442d588f2,
0xb7dcbf5354e9bece,0xc11ed6d538aeb2f,
0xe5d3ef282a242e81,0x8f1668c8a86da5fa,
0x8fa475791a569d10,0xf96e017d694487bc,
0xb38d92d760ec4455,0x37c981dcc395a9ac,
0xe070f78d3927556a,0x85bbe253f47b1417,
0x8c469ab843b89562,0x93956d7478ccec8e,
0xaf58416654a6babb,0x387ac8d1970027b2,
0xdb2e51bfe9d0696a,0x6997b05fcc0319e,
0x88fcf317f22241e2,0x441fece3bdf81f03,
0xab3c2fddeeaad25a,0xd527e81cad7626c3,
0xd60b3bd56a5586f1,0x8a71e223d8d3b074,
0x85c7056562757456,0xf6872d5667844e49,
0xa738c6bebb12d16c,0xb428f8ac016561db,
0xd106f86e69d785c7,0xe13336d701beba52,
0x82a45b450226b39c,0xecc0024661173473,
0xa34d721642b06084,0x27f002d7f95d0190,
0xcc20ce9bd35c78a5,0x31ec038df7b441f4,
0xff290242c83396ce,0x7e67047175a15271,
0x9f79a169bd203e41,0xf0062c6e984d386,
0xc75809c42c684dd1,0x52c07b78a3e60868,
0xf92e0c3537826145,0xa7709a56ccdf8a82,
0x9bbcc7a142b17ccb,0x88a66076400bb691,
0xc2abf989935ddbfe,0x6acff893d00ea435,
0xf356f7ebf83552fe,0x583f6b8c4124d43,
0x98165af37b2153de,0xc3727a337a8b704a,
0xbe1bf1b059e9a8d6,0x744f18c0592e4c5c,
0xeda2ee1c7064130c,0x1162def06f79df73,
0x9485d4d1c63e8be7,0x8addcb5645ac2ba8,
0xb9a74a0637ce2ee1,0x6d953e2bd7173692,
0xe8111c87c5c1ba99,0xc8fa8db6ccdd0437,
0x910ab1d4db9914a0,0x1d9c9892400a22a2,
0xb54d5e4a127f59c8,0x2503beb6d00cab4b,
0xe2a0b5dc971f303a,0x2e44ae64840fd61d,
0x8da471a9de737e24,0x5ceaecfed289e5d2,
0xb10d8e1456105dad,0x7425a83e872c5f47,
0xdd50f1996b947518,0xd12f124e28f77719,
0x8a5296ffe33cc92f,0x82bd6b70d99aaa6f,
0xace73cbfdc0bfb7b,0x636cc64d1001550b,
0xd8210befd30efa5a,0x3c47f7e05401aa4e,
0x8714a775e3e95c78,0x65acfaec34810a71,
0xa8d9d1535ce3b396,0x7f1839a741a14d0d,
0xd31045a8341ca07c,0x1ede48111209a050,
0x83ea2b892091e44d,0x934aed0aab460432,
0xa4e4b66b68b65d60,0xf81da84d5617853f,
0xce1de40642e3f4b9,0x36251260ab9d668e,
0x80d2ae83e9ce78f3,0xc1d72b7c6b426019,
0xa1075a24e4421730,0xb24cf65b8612f81f,
0xc94930ae1d529cfc,0xdee033f26797b627,
0xfb9b7cd9a4a7443c,0x169840ef017da3b1,
0x9d412e0806e88aa5,0x8e1f289560ee864e,
0xc491798a08a2ad4e,0xf1a6f2bab92a27e2,
0xf5b5d7ec8acb58a2,0xae10af696774b1db,
0x9991a6f3d6bf1765,0xacca6da1e0a8ef29,
0xbff610b0cc6edd3f,0x17fd090a58d32af3,
0xeff394dcff8a948e,0xddfc4b4cef07f5b0,
0x95f83d0a1fb69cd9,0x4abdaf101564f98e,
0xbb764c4ca7a4440f,0x9d6d1ad41abe37f1,
0xea53df5fd18d5513,0x84c86189216dc5ed,
0x92746b9be2f8552c,0x32fd3cf5b4e49bb4,
0xb7118682dbb66a77,0x3fbc8c33221dc2a1,
0xe4d5e82392a40515,0xfabaf3feaa5334a,
0x8f05b1163ba6832d,0x29cb4d87f2a7400e,
0xb2c71d5bca9023f8,0x743e20e9ef511012,
0xdf78e4b2bd342cf6,0x914da9246b255416,
0x8bab8eefb6409c1a,0x1ad089b6c2f7548e,
0xae9672aba3d0c320,0xa184ac2473b529b1,
0xda3c0f568cc4f3e8,0xc9e5d72d90a2741e,
0x8865899617fb1871,0x7e2fa67c7a658892,
0xaa7eebfb9df9de8d,0xddbb901b98feeab7,
0xd51ea6fa85785631,0x552a74227f3ea565,
0x8533285c936b35de,0xd53a88958f87275f,
0xa67ff273b8460356,0x8a892abaf368f137,
0xd01fef10a657842c,0x2d2b7569b0432d85,
0x8213f56a67f6b29b,0x9c3b29620e29fc73,
0xa298f2c501f45f42,0x8349f3ba91b47b8f,
0xcb3f2f7642717713,0x241c70a936219a73,
0xfe0efb53d30dd4d7,0xed238cd383aa0110,
0x9ec95d1463e8a506,0xf4363804324a40aa,
0xc67bb4597ce2ce48,0xb143c6053edcd0d5,
0xf81aa16fdc1b81da,0xdd94b7868e94050a,
0x9b10a4e5e9913128,0xca7cf2b4191c8326,
0xc1d4ce1f63f57d72,0xfd1c2f611f63a3f0,
0xf24a01a73cf2dccf,0xbc633b39673c8cec,
0x976e41088617ca01,0xd5be0503e085d813,
0xbd49d14aa79dbc82,0x4b2d8644d8a74e18,
0xec9c459d51852ba2,0xddf8e7d60ed1219e,
0x93e1ab8252f33b45,0xcabb90e5c942b503,
0xb8da1662e7b00a17,0x3d6a751f3b936243,
0xe7109bfba19c0c9d,0xcc512670a783ad4,
0x906a617d450187e2,0x27fb2b80668b24c5,
0xb484f9dc9641e9da,0xb1f9f660802dedf6,
0xe1a63853bbd26451,0x5e7873f8a0396973,
0x8d07e33455637eb2,0xdb0b487b6423e1e8,
0xb049dc016abc5e5f,0x91ce1a9a3d2cda62,
0xdc5c5301c56b75f7,0x7641a140cc7810fb,
0x89b9b3e11b6329ba,0xa9e904c87fcb0a9d,
0xac2820d9623bf429,0x546345fa9fbdcd44,
0xd732290fbacaf133,0xa97c177947ad4095,
0x867f59a9d4bed6c0,0x49ed8eabcccc485d,
0xa81f301449ee8c70,0x5c68f256bfff5a74,
0xd226fc195c6a2f8c,0x73832eec6fff3111,
0x83585d8fd9c25db7,0xc831fd53c5ff7eab,
0xa42e74f3d032f525,0xba3e7ca8b77f5e55,
0xcd3a1230c43fb26f,0x28ce1bd2e55f35eb,
0x80444b5e7aa7cf85,0x7980d163cf5b81b3,
0xa0555e361951c366,0xd7e105bcc332621f,
0xc86ab5c39fa63440,0x8dd9472bf3fefaa7,
0xfa856334878fc150,0xb14f98f6f0feb951,
0x9c935e00d4b9d8d2,0x6ed1bf9a569f33d3,
0xc3b8358109e84f07,0xa862f80ec4700c8,
0xf4a642e14c6262c8,0xcd27bb612758c0fa,
0x98e7e9cccfbd7dbd,0x8038d51cb897789c,
0xbf21e44003acdd2c,0xe0470a63e6bd56c3,
0xeeea5d5004981478,0x1858ccfce06cac74,
0x95527a5202df0ccb,0xf37801e0c43ebc8,
0xbaa718e68396cffd,0xd30560258f54e6ba,
0xe950df20247c83fd,0x47c6b82ef32a2069,
0x91d28b7416cdd27e,0x4cdc331d57fa5441,
0xb6472e511c81471d,0xe0133fe4adf8e952,
0xe3d8f9e563a198e5,0x58180fddd97723a6,
0x8e679c2f5e44ff8f,0x570f09eaa7ea7648,
};
// Attempts to compute i * 10^(power) exactly; and if "negative" is
// true, negate the result.
// Returns true on success, false on failure.
// Failure suggests and invalid input or out-of-range result.
bool compute_float_64(int64_t power, uint64_t i, bool negative, double& d) {
if (i == 0) {
d = negative ? -0.0 : 0.0;
return true;
}
int64_t exponent = (((152170 + 65536) * power) >> 16) + 1024 + 63;
int lz = leading_zeroes(i);
i <<= lz;
const uint32_t index = 2 * uint32_t(power - smallest_power);
value128 firstproduct = full_multiplication(i, power_of_five_128[index]);
if ((firstproduct.high & 0x1FF) == 0x1FF) {
value128 secondproduct = full_multiplication(i, power_of_five_128[index + 1]);
firstproduct.low += secondproduct.high;
if (secondproduct.high > firstproduct.low) {
firstproduct.high++;
}
}
uint64_t lower = firstproduct.low;
uint64_t upper = firstproduct.high;
uint64_t upperbit = upper >> 63;
uint64_t mantissa = upper >> (upperbit + 9);
lz += int(1 ^ upperbit);
int64_t real_exponent = exponent - lz;
if (real_exponent <= 0) {
if (-real_exponent + 1 >= 64) {
d = negative ? -0.0 : 0.0;
return true;
}
mantissa >>= -real_exponent + 1;
mantissa += (mantissa & 1);
mantissa >>= 1;
real_exponent = (mantissa < (uint64_t(1) << 52)) ? 0 : 1;
d = to_double(mantissa, real_exponent, negative);
return true;
}
if ((lower <= 1) && (power >= -4) && (power <= 23) && ((mantissa & 3) == 1)) {
if ((mantissa << (upperbit + 64 - 53 - 2)) == upper) {
mantissa &= ~1;
}
}
mantissa += mantissa & 1;
mantissa >>= 1;
if (mantissa >= (1ULL << 53)) {
mantissa = (1ULL << 52);
real_exponent++;
}
mantissa &= ~(1ULL << 52);
if (real_exponent > 2046) {
return false;
}
d = to_double(mantissa, real_exponent, negative);
return true;
}
// Parses a single digit character and updates the integer value.
template<typename K>
bool parse_digit(const K c, uint64_t& i) {
auto digit = int_convert::toDigit<K, 10>(c);
if (digit > 9) {
return false;
}
i = 10 * i + digit;
return true;
}
} // namespace
template<typename K>
SIMSTR_API std::optional<double> impl_to_double(const K* start, const K* end) {
auto get_value = [&](const K* pointer) -> K {
if (pointer == end) {
return '\0';
}
return *pointer;
};
const K* srcinit = start;
bool negative = (get_value(start) == '-');
start += (uint8_t)negative;
if (end - start == 3) {
if (!negative && (start[0] | 0x20) == K('n')) {
if ((start[1] | 0x20) == K('a') && (start[2] | 0x20) == K('n')) {
return std::nan("0");
}
} else if ((start[0] | 0x20) == K('i') && (start[1] | 0x20) == K('n') && (start[2] | 0x20) == K('f')) {
return negative ? -std::numeric_limits<double>::infinity() : std::numeric_limits<double>::infinity();
}
}
uint64_t digits = 0;
const K* p = start;
p += parse_digit(get_value(p), digits);
bool leading_zero = (digits == 0);
while (parse_digit(get_value(p), digits)) {
p++;
}
if (p == start) {
return {};
}
if ((leading_zero && p != start + 1)) {
return {};
}
int64_t exponent = 0;
int max_digits = 19;
bool overflow;
if (get_value(p) == '.') {
p++;
const K* start_decimal_digits = p;
if (!parse_digit(get_value(p), digits)) {
return {};
} // no decimal digits
p++;
while (parse_digit(get_value(p), digits)) {
p++;
}
exponent = -(p - start_decimal_digits);
overflow = p - start - 1 > max_digits;
if (overflow && leading_zero) {
const K* start_digits = start + 2;
while (get_value(start_digits) == '0') {
start_digits++;
}
overflow = p - start_digits > max_digits;
}
} else {
overflow = p - start > max_digits;
}
if (overflow) {
return {};
}
if ((get_value(p) | 0x20) == K('e')) {
p++;
bool exp_neg = get_value(p) == '-';
p += exp_neg || get_value(p) == '+';
uint64_t exp = 0;
const K* start_exp_digits = p;
while (parse_digit(get_value(p), exp)) {
p++;
}
if (p - start_exp_digits == 0 || p - start_exp_digits > max_digits) {
return {};
}
exponent += exp_neg ? 0 - exp : exp;
}
if (exponent < smallest_power || exponent > largest_power || p != end) {
return {};
}
double d;
if (!compute_float_64(exponent, digits, negative, d)) {
return {};
}
return d;
}
template SIMSTR_API std::optional<double> impl_to_double<u8s>(const u8s* start, const u8s* end);
template SIMSTR_API std::optional<double> impl_to_double<u16s>(const u16s* start, const u16s* end);
template SIMSTR_API std::optional<double> impl_to_double<u32s>(const u32s* start, const u32s* end);
template SIMSTR_API std::optional<double> impl_to_double<uws>(const uws* start, const uws* end);
} // namespace simstr