AK: Add String factories for Roman numerals and bijective bases

These are copied and modified from ByteString, with the addition of a
Case parameter so that we can construct them in lowercase instead of
having to them make a copy.
This commit is contained in:
Sam Atkins 2025-02-10 11:47:51 +00:00 committed by Andreas Kling
parent 0a805fe7a5
commit 8b52a354fa
Notes: github-actions[bot] 2025-02-11 09:40:34 +00:00
2 changed files with 90 additions and 0 deletions

View file

@ -1,5 +1,6 @@
/*
* Copyright (c) 2018-2022, Andreas Kling <andreas@ladybird.org>
* Copyright (c) 2025, Sam Atkins <sam@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@ -459,4 +460,86 @@ ErrorOr<String> String::repeated(String const& input, size_t count)
return result;
}
String String::bijective_base_from(size_t value, Case target_case, unsigned base, StringView map)
{
value++;
if (map.is_null())
map = target_case == Case::Upper ? "ABCDEFGHIJKLMNOPQRSTUVWXYZ"sv : "abcdefghijklmnopqrstuvwxyz"sv;
VERIFY(base >= 2 && base <= map.length());
// The '8 bits per byte' assumption may need to go?
Array<char, round_up_to_power_of_two(sizeof(size_t) * 8 + 1, 2)> buffer;
size_t i = 0;
do {
auto remainder = value % base;
auto new_value = value / base;
if (remainder == 0) {
new_value--;
remainder = map.length();
}
buffer[i++] = map[remainder - 1];
value = new_value;
} while (value > 0);
for (size_t j = 0; j < i / 2; ++j)
swap(buffer[j], buffer[i - j - 1]);
return MUST(from_utf8(ReadonlyBytes(buffer.data(), i)));
}
String String::roman_number_from(size_t value, Case target_case)
{
if (value > 3999)
return number(value);
StringBuilder builder;
while (value > 0) {
if (value >= 1000) {
builder.append(target_case == Case::Upper ? 'M' : 'm');
value -= 1000;
} else if (value >= 900) {
builder.append(target_case == Case::Upper ? "CM"sv : "cm"sv);
value -= 900;
} else if (value >= 500) {
builder.append(target_case == Case::Upper ? 'D' : 'd');
value -= 500;
} else if (value >= 400) {
builder.append(target_case == Case::Upper ? "CD"sv : "cd"sv);
value -= 400;
} else if (value >= 100) {
builder.append(target_case == Case::Upper ? 'C' : 'c');
value -= 100;
} else if (value >= 90) {
builder.append(target_case == Case::Upper ? "XC"sv : "xc"sv);
value -= 90;
} else if (value >= 50) {
builder.append(target_case == Case::Upper ? 'L' : 'l');
value -= 50;
} else if (value >= 40) {
builder.append(target_case == Case::Upper ? "XL"sv : "xl"sv);
value -= 40;
} else if (value >= 10) {
builder.append(target_case == Case::Upper ? 'X' : 'x');
value -= 10;
} else if (value == 9) {
builder.append(target_case == Case::Upper ? "IX"sv : "ix"sv);
value -= 9;
} else if (value >= 5 && value <= 8) {
builder.append(target_case == Case::Upper ? 'V' : 'v');
value -= 5;
} else if (value == 4) {
builder.append(target_case == Case::Upper ? "IV"sv : "iv"sv);
value -= 4;
} else if (value <= 3) {
builder.append(target_case == Case::Upper ? 'I' : 'i');
value -= 1;
}
}
return builder.to_string_without_validation();
}
}

View file

@ -96,6 +96,13 @@ public:
// Creates a new String from another string, repeated N times.
static ErrorOr<String> repeated(String const&, size_t count);
enum class Case {
Upper,
Lower,
};
[[nodiscard]] static String bijective_base_from(size_t value, Case, unsigned base = 26, StringView map = {});
[[nodiscard]] static String roman_number_from(size_t value, Case);
// Creates a new String by case-transforming this String. Using these methods require linking LibUnicode into your application.
ErrorOr<String> to_lowercase(Optional<StringView> const& locale = {}) const;
ErrorOr<String> to_uppercase(Optional<StringView> const& locale = {}) const;