Zrythm v2.0.0-DEV
a highly automated and intuitive digital audio workstation
Loading...
Searching...
No Matches
algorithms.h
1// SPDX-FileCopyrightText: © 2019, 2021, 2024-2025 Alexandros Theodotou <alex@zrythm.org>
2// SPDX-License-Identifier: LicenseRef-ZrythmLicense
3
4#pragma once
5
6#include <algorithm>
7#include <span>
8
9#include "utils/types.h"
10
11namespace zrythm::utils::algorithms
12{
13
14template <typename Range, typename T>
15auto
16find_closest (const Range &range, const T &target)
17{
18 return std::ranges::min_element (range, [&] (const auto &a, const auto &b) {
19 return std::abs (a - target) < std::abs (b - target);
20 });
21}
22
32template <std::totally_ordered T>
33std::optional<std::reference_wrapper<const T>>
34binary_search_nearby (
35 const T &key,
36 std::span<const T> elements,
37 bool return_prev = false,
38 bool include_equal = true)
39{
40 // Handle empty array case
41 if (elements.empty ())
42 return std::nullopt;
43
44 // Initialize binary search bounds
45 size_t first = 0;
46 size_t last = elements.size () - 1;
47
48 while (first <= last)
49 {
50 // Calculate middle point safely to avoid overflow
51 size_t middle = first + (last - first) / 2;
52 const T &pivot = elements[middle];
53
54 // Handle exact match case
55 if (pivot == key)
56 {
57 if (include_equal)
58 return std::make_optional (std::ref (pivot));
59 // Return previous/next element if exact matches not wanted
60 if (return_prev)
61 return middle > 0
62 ? std::make_optional (std::ref (elements[middle - 1]))
63 : std::nullopt;
64 return middle < elements.size () - 1
65 ? std::make_optional (std::ref (elements[middle + 1]))
66 : std::nullopt;
67 }
68
69 // Handle case where pivot is less than search key
70 if (pivot < key)
71 {
72 // Check if we're at the end
73 if (middle == elements.size () - 1)
74 return return_prev ? std::make_optional (std::ref (pivot)) : std::nullopt;
75 // Found value between pivot and next element
76 if (elements[middle + 1] > key)
77 return return_prev
78 ? std::make_optional (std::ref (pivot))
79 : std::make_optional (std::ref (elements[middle + 1]));
80 first = middle + 1;
81 }
82 else
83 {
84 // Handle case where pivot is greater than search key
85 if (middle == 0)
86 return return_prev
87 ? std::nullopt
88 : std::make_optional (std::ref (pivot));
89 last = middle - 1;
90 }
91 }
92
93 // Handle edge case when search terminates between elements
94 return return_prev && last < elements.size ()
95 ? std::make_optional (std::ref (elements[last]))
96 : std::nullopt;
97}
98
99}; // namespace zrythm::utils