2025-07-24 14:51:10 -04:00
|
|
|
/*
|
|
|
|
|
* Copyright 2025 Valve Corporation
|
|
|
|
|
* SPDX-License-Identifier: MIT
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include "range_minimum_query.h"
|
|
|
|
|
|
|
|
|
|
#include "bitscan.h"
|
|
|
|
|
#include "macros.h"
|
|
|
|
|
#include "ralloc.h"
|
|
|
|
|
#include "u_math.h"
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
range_minimum_query_table_resize(struct range_minimum_query_table *table,
|
|
|
|
|
void *mem_ctx, uint32_t width)
|
|
|
|
|
{
|
|
|
|
|
const uint64_t height = util_logbase2_64(width) + 1;
|
|
|
|
|
const uint64_t size = width * height;
|
|
|
|
|
assert(size < UINT32_MAX);
|
|
|
|
|
|
|
|
|
|
table->table = reralloc_array_size(mem_ctx, table->table,
|
|
|
|
|
sizeof(uint32_t), size);
|
|
|
|
|
table->width = width;
|
|
|
|
|
table->height = height;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
elementwise_minimum(uint32_t *restrict out,
|
|
|
|
|
uint32_t *const a,
|
|
|
|
|
uint32_t *const b,
|
|
|
|
|
uint32_t count)
|
|
|
|
|
{
|
|
|
|
|
for (uint32_t i = 0; i < count; i++) {
|
|
|
|
|
out[i] = MIN2(a[i], b[i]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-05 12:47:36 -05:00
|
|
|
/**
|
|
|
|
|
* For a given level (row) of the table, how many input values is the
|
|
|
|
|
* minimum computed over?
|
|
|
|
|
*
|
|
|
|
|
* Each row of the table has (table->width - rmq_distance(level) + 1)
|
|
|
|
|
* valid elements.
|
|
|
|
|
*/
|
2025-07-24 14:51:10 -04:00
|
|
|
static uint32_t
|
|
|
|
|
rmq_distance(int32_t level)
|
|
|
|
|
{
|
|
|
|
|
return 1 << level;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
range_minimum_query_table_preprocess(struct range_minimum_query_table *table)
|
|
|
|
|
{
|
|
|
|
|
for (uint32_t i = 1; i < table->height; i++) {
|
|
|
|
|
uint32_t in_distance = rmq_distance(i - 1);
|
2026-01-05 12:47:36 -05:00
|
|
|
uint32_t out_distance = rmq_distance(i);
|
2025-07-24 14:51:10 -04:00
|
|
|
uint32_t *in_row = table->table + table->width * (i - 1);
|
|
|
|
|
uint32_t *out_row = table->table + table->width * i;
|
2026-01-05 12:47:36 -05:00
|
|
|
/*
|
|
|
|
|
* This reads elements [0, x) from in_row, where x is:
|
|
|
|
|
* in_distance + table->width - out_distance + 1
|
|
|
|
|
* in_distance + table->width - (2 * in_distance) + 1
|
|
|
|
|
* table->width - in_distance + 1
|
|
|
|
|
* which is the number of valid elements in in_row
|
|
|
|
|
*/
|
2025-07-24 14:51:10 -04:00
|
|
|
elementwise_minimum(out_row, in_row, in_row + in_distance,
|
2026-01-05 12:47:36 -05:00
|
|
|
table->width - out_distance + 1);
|
2025-07-24 14:51:10 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t
|
|
|
|
|
range_minimum_query(struct range_minimum_query_table *const table,
|
|
|
|
|
uint32_t left_idx, uint32_t right_idx)
|
|
|
|
|
{
|
|
|
|
|
assert(left_idx < right_idx);
|
2026-01-05 12:47:36 -05:00
|
|
|
assert(right_idx <= table->width);
|
2025-07-24 14:51:10 -04:00
|
|
|
const uint32_t distance = right_idx - left_idx;
|
|
|
|
|
|
|
|
|
|
uint32_t level = util_logbase2(distance);
|
|
|
|
|
assert(rmq_distance(level) <= distance);
|
|
|
|
|
assert(distance < 2 * rmq_distance(level));
|
|
|
|
|
assert(level < table->height);
|
|
|
|
|
|
2026-01-05 12:47:36 -05:00
|
|
|
/*
|
|
|
|
|
* Since right_idx <= table->width by precondition, we know
|
|
|
|
|
* right_idx - rmq_distance(level) <= table->width - rmq_distance(level)
|
|
|
|
|
* right_idx - rmq_distance(level) < table->width - rmq_distance(level) + 1
|
|
|
|
|
* which means that the read for `right` is in bounds.
|
|
|
|
|
*
|
|
|
|
|
* The read for `left` is then in bounds because
|
|
|
|
|
* left_idx == right_idx - width <= right_idx - rmq_distance(level)
|
|
|
|
|
*/
|
2025-07-24 14:51:10 -04:00
|
|
|
uint32_t *const row = table->table + table->width * level;
|
|
|
|
|
uint32_t left = row[left_idx];
|
|
|
|
|
uint32_t right = row[right_idx - rmq_distance(level)];
|
|
|
|
|
return MIN2(left, right);
|
|
|
|
|
}
|