Everywhere: Use AK::mod() more, and add a test

The tests show that mod(a, b) takes on the sign of b while
a % b takes on the sign of a. As b is usually a positive constant,
mod() is useful when you want a guaranteed positive result.

mod() matches the semantics of % in Python.

Also, mod(a, b) == mod(a, abs(b)) modulo b, while
a % b != abs(a) % b modulo b. So mod() makes it easier to get a
guaranteed-positive result even if the sign of b is unknown.

No behavior change.
This commit is contained in:
Nico Weber
2025-10-14 08:10:33 -04:00
parent b616d04968
commit 0cd71f1d91
7 changed files with 29 additions and 20 deletions

View File

@@ -128,6 +128,30 @@ TEST_CASE(mix)
EXPECT_APPROXIMATE(mix(b, a, 1.0), 1.0);
}
TEST_CASE(mod)
{
EXPECT_EQ(mod(5, 3), 2);
EXPECT_EQ(mod(-5, 3), 1);
EXPECT_EQ(-5 % 3, -2);
EXPECT_EQ(mod(5, -3), -1);
EXPECT_EQ(5 % -3, 2);
EXPECT_EQ(mod(-5, -3), -2);
EXPECT_EQ(-5 % -3, -2);
EXPECT_EQ(mod(4, 2), 0);
EXPECT_EQ(mod(-4, 2), 0);
EXPECT_EQ(mod(4, -2), 0);
EXPECT_EQ(mod(-4, -2), 0);
EXPECT_EQ(mod(1, 1), 0);
EXPECT_EQ(mod(-1, 1), 0);
EXPECT_EQ(mod(1, -1), 0);
EXPECT_EQ(mod(-1, -1), 0);
EXPECT_EQ(mod(0, 5), 0);
EXPECT_EQ(mod(0, -5), 0);
}
TEST_CASE(swap)
{
int i = 4;

View File

@@ -119,11 +119,6 @@ static inline BigAllocator (&big_allocators())[1]
// chunk. It has no bearing on the rest of the allocator, especially for
// regular malloc.
static inline unsigned long modulo(long a, long b)
{
return (b + (a % b)) % b;
}
struct EuclideanResult {
long x;
long y;
@@ -160,12 +155,12 @@ static inline bool block_has_aligned_chunk(long align, long bytes_per_chunk, lon
// Solve the linear congruence n*bytes_per_chunk = -sizeof(ChunkedBlock) (mod align).
auto [x, y, gcd] = extended_euclid(bytes_per_chunk % align, align);
long constant = modulo(-sizeof(ChunkedBlock), align);
long constant = mod(-sizeof(ChunkedBlock), align);
if (constant % gcd != 0)
// No solution. Chunk size is probably a multiple of align.
return false;
long n = modulo(x * (constant / gcd), align);
long n = mod(x * (constant / gcd), align);
if (x < 0)
n = (n + align / gcd) % align;

View File

@@ -157,10 +157,6 @@ static struct tm* time_to_tm(struct tm* tm, time_t t, StringView time_zone)
tm->tm_wday = day_of_week(year, month, tm->tm_mday);
tm->tm_mon = month - 1;
auto mod = [](i64 a, i64 b) {
return (a % b + b) % b;
};
t = mod(t, __seconds_per_day);
VERIFY(t >= 0);

View File

@@ -108,8 +108,7 @@ public:
auto current_loc = loc + m_start_offset;
auto gradient_len = static_cast<i64>(m_gradient_line_colors.size());
if (m_repeat_mode == RepeatMode::Repeat) {
auto color_loc = current_loc % gradient_len;
return color_loc < 0 ? gradient_len + color_loc : color_loc;
return mod(current_loc, gradient_len);
} else if (m_repeat_mode == RepeatMode::Reflect) {
auto color_loc = AK::abs(current_loc % gradient_len);
auto repeats = current_loc / gradient_len;

View File

@@ -259,9 +259,6 @@ inline void _1D_EXTR(Transformation transformation, IDWTOutput& a, int start, in
// (F-4)
// PSE is short for "Period Symmetric Extension".
auto PSE = [](int i, int i0, int i1) {
auto mod = [](int a, int b) {
return (a % b + b) % b;
};
return i0 + min(mod(i - i0, 2 * (i1 - i0 - 1)), 2 * (i1 - i0 - 1) - mod(i - i0, 2 * (i1 - i0 - 1)));
};

View File

@@ -301,7 +301,7 @@ auto modulo(T x, U y)
auto r = fmod(x, y);
return r < 0 ? r + y : r;
} else {
return ((x % y) + y) % y;
return AK::mod(x, y);
}
}

View File

@@ -112,9 +112,7 @@ void WindowSwitcher::on_key_event(KeyEvent const& event)
if (!event.shift()) {
new_selected_index = (m_selected_index + 1) % static_cast<int>(m_windows.size());
} else {
new_selected_index = (m_selected_index - 1) % static_cast<int>(m_windows.size());
if (new_selected_index < 0)
new_selected_index = static_cast<int>(m_windows.size()) - 1;
new_selected_index = mod(m_selected_index - 1, static_cast<int>(m_windows.size()));
}
VERIFY(new_selected_index < static_cast<int>(m_windows.size()));