1use crate::{GetResultExt, NaturalSegment, OrderedF64};
2use alloc::rc::Rc;
3use alloc::string::String;
4use alloc::vec;
5use alloc::vec::Vec;
6use core::sync::atomic::{AtomicU32, Ordering};
7use hashbrown::{HashMap, HashSet};
8
9static WIDGET_ID_COUNTER: AtomicU32 = AtomicU32::new(0);
10
11pub(crate) fn generate_unique_id() -> u32 {
12 WIDGET_ID_COUNTER.fetch_add(1, Ordering::Relaxed)
13}
14
15const ALPHABET: [char; 62] = [
16 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
17 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
18 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4',
19 '5', '6', '7', '8', '9',
20];
21
22pub(crate) fn remove_duplicates_outside_section(
23 items: Vec<Rc<str>>,
24 section_start: usize,
25 section_size: usize,
26) -> Vec<Rc<str>> {
27 if section_start == 0 && section_size >= items.len() {
28 return items;
29 }
30 let section_end = section_start + section_size;
31 let section_set: HashSet<Rc<str>> = items[section_start..section_end].iter().cloned().collect();
32 items
33 .into_iter()
34 .enumerate()
35 .filter(|(i, s)| {
36 *i >= section_start && *i < section_end || !section_set.contains(s.as_ref())
37 })
38 .map(|(_, s)| s)
39 .collect()
40}
41
42#[inline]
45pub(crate) fn bin_index_usize(sorted_seq: &[usize], find: usize) -> Option<usize> {
46 sorted_seq.binary_search(&find).ok()
47}
48
49#[inline]
50pub(crate) fn push_n(k: usize, sorted_seq: &[usize]) -> usize {
51 if sorted_seq.is_empty() || k < sorted_seq[0] {
52 k
53 } else {
54 let mut lo: usize = 0;
55 let mut hi = sorted_seq.len();
56 while lo < hi {
57 let mid = lo + (hi - lo) / 2;
58 if sorted_seq[mid] < k + mid + 1 {
59 lo = mid + 1;
60 } else {
61 hi = mid;
62 }
63 }
64 k + lo
65 }
66}
67
68#[inline]
69pub(crate) fn pull_n(k: usize, sorted_seq: &[usize]) -> usize {
70 k - sorted_seq.binary_search(&k).unwrap_or_else(|e| e)
71}
72
73#[inline]
74pub(crate) fn push_n_add<T>(k: usize, sorted_seq: &[(usize, T)]) -> usize {
75 if sorted_seq.is_empty() || k < sorted_seq[0].0 {
76 k
77 } else {
78 let mut lo: usize = 0;
79 let mut hi: usize = sorted_seq.len();
80 while lo < hi {
81 let mid = lo + (hi - lo) / 2;
82 if sorted_seq[mid].0 < k + mid + 1 {
83 lo = mid + 1;
84 } else {
85 hi = mid;
86 }
87 }
88 k + lo
89 }
90}
91
92pub(crate) fn offset_move_indices(
93 move_to: usize,
94 to_move: &[usize],
95 new_to_old: &mut HashMap<usize, usize>,
96) -> Vec<(usize, usize)> {
97 let offset = to_move.iter().filter(|i| *i < &move_to).count();
99
100 let correct_move_to = move_to.saturating_sub(offset);
102
103 let mut old_to_new = Vec::new();
105 new_to_old.clear();
106
107 for (i, &elem) in to_move.iter().enumerate() {
109 let value = correct_move_to + i;
110 old_to_new.push((elem, value));
111 new_to_old.insert(value, elem);
112 }
113
114 old_to_new
115}
116
117pub(crate) fn get_full_move_map(
124 len: usize,
125 old: &HashSet<usize>,
126 new_to_old: &HashMap<usize, usize>, ) -> (Vec<usize>, Vec<usize>) {
128 let mut full_old_to_new = vec![0usize; len]; let mut full_new_to_old = vec![0usize; len]; let mut remaining_old = (0..len).filter(|&i| !old.contains(&i));
133
134 for new_i in 0..len {
135 let old_i = if let Some(&oi) = new_to_old.get(&new_i) {
136 oi
137 } else {
138 remaining_old.next().unwrap_or_else(|| new_i) };
140
141 full_new_to_old[new_i] = old_i;
142 full_old_to_new[old_i] = new_i;
143 }
144
145 (full_old_to_new, full_new_to_old)
146}
147
148#[inline(always)]
153pub fn n_to_base62(n: usize) -> String {
154 let base: usize = 62;
155 let mut s = String::with_capacity(11);
156 let mut m = n + 1;
157 while m > 0 {
158 let rem = (m - 1) % base;
159 s.push(ALPHABET[rem as usize]);
160 m = (m - 1) / base;
161 }
162 s.chars().rev().collect::<String>()
163}
164
165pub fn binary_search_ins_pos(
169 v: &[Rc<str>],
170 iid_to_row: &HashMap<Rc<str>, usize>,
171 target_row: usize,
172) -> Result<usize, String> {
173 let mut lo = 0;
174 let mut hi = v.len();
175 while lo < hi {
176 let mid = lo + (hi - lo) / 2;
177 let row = *iid_to_row.get_res(&v[mid])?;
178 if row < target_row {
179 lo = mid + 1;
180 } else {
181 hi = mid;
182 }
183 }
184 Ok(lo)
185}
186
187pub fn binary_search_exact_pos(
191 v: &[Rc<str>],
192 iid_to_row: &HashMap<Rc<str>, usize>,
193 target_row: usize,
194) -> Result<Option<usize>, String> {
195 let mut lo = 0;
196 let mut hi = v.len();
197 while lo < hi {
198 let mid = lo + (hi - lo) / 2;
199 let row = *iid_to_row.get_res(&v[mid])?;
200 if row < target_row {
201 lo = mid + 1;
202 } else if row > target_row {
203 hi = mid;
204 } else {
205 return Ok(Some(mid));
206 }
207 }
208 Ok(None)
209}
210
211#[inline(always)]
212fn is_decimal_digit(c: char) -> bool {
213 c.to_digit(10).is_some()
214}
215
216#[inline(always)]
217pub fn natural_text_segments(s: &str) -> Vec<NaturalSegment> {
218 let mut segments = Vec::with_capacity(8);
219 let mut chars = s.chars().peekable();
220 let mut text_buf = String::with_capacity(32);
221
222 while let Some(c) = chars.next() {
223 if matches!(c, '-' | '+' | '\u{2212}')
225 && chars.peek().map_or(false, |&n| is_decimal_digit(n))
226 {
227 if !text_buf.is_empty() {
228 segments.push(NaturalSegment::Text(text_buf.to_lowercase()));
229 text_buf.clear();
230 }
231 let negative = c == '-' || c == '\u{2212}';
232 let mut value: i64 = 0;
233
234 if let Some(d) = chars.next().and_then(|ch| ch.to_digit(10)) {
236 value = d as i64;
237 }
238 while let Some(&next) = chars.peek() {
240 if let Some(nd) = next.to_digit(10) {
241 value = value.saturating_mul(10).saturating_add(nd as i64);
242 chars.next();
243 } else {
244 break;
245 }
246 }
247 let num = if negative { -value } else { value };
248 segments.push(NaturalSegment::Number(OrderedF64(num as f64)));
249 continue;
250 }
251
252 if let Some(d) = c.to_digit(10) {
254 if !text_buf.is_empty() {
255 segments.push(NaturalSegment::Text(text_buf.to_lowercase()));
256 text_buf.clear();
257 }
258 let mut value = d as i64;
259 while let Some(&next) = chars.peek() {
260 if let Some(nd) = next.to_digit(10) {
261 value = value.saturating_mul(10).saturating_add(nd as i64);
262 chars.next();
263 } else {
264 break;
265 }
266 }
267 segments.push(NaturalSegment::Number(OrderedF64(value as f64)));
268 continue;
269 }
270
271 text_buf.push(c);
273 }
274
275 if !text_buf.is_empty() {
276 segments.push(NaturalSegment::Text(text_buf.to_lowercase()));
277 }
278
279 segments
280}
281
282#[inline(always)]
283pub fn natural_text_segments_cs(s: &str) -> Vec<NaturalSegment> {
284 let mut segments = Vec::with_capacity(8);
285 let mut chars = s.chars().peekable();
286 let mut text_buf = String::with_capacity(32);
287
288 while let Some(c) = chars.next() {
289 if matches!(c, '-' | '+' | '\u{2212}')
291 && chars.peek().map_or(false, |&n| is_decimal_digit(n))
292 {
293 if !text_buf.is_empty() {
294 segments.push(NaturalSegment::Text(text_buf.clone()));
295 text_buf.clear();
296 }
297 let negative = c == '-' || c == '\u{2212}';
298 let mut value: i64 = 0;
299
300 if let Some(d) = chars.next().and_then(|ch| ch.to_digit(10)) {
302 value = d as i64;
303 }
304 while let Some(&next) = chars.peek() {
306 if let Some(nd) = next.to_digit(10) {
307 value = value.saturating_mul(10).saturating_add(nd as i64);
308 chars.next();
309 } else {
310 break;
311 }
312 }
313 let num = if negative { -value } else { value };
314 segments.push(NaturalSegment::Number(OrderedF64(num as f64)));
315 continue;
316 }
317
318 if let Some(d) = c.to_digit(10) {
320 if !text_buf.is_empty() {
321 segments.push(NaturalSegment::Text(text_buf.clone()));
322 text_buf.clear();
323 }
324 let mut value = d as i64;
325 while let Some(&next) = chars.peek() {
326 if let Some(nd) = next.to_digit(10) {
327 value = value.saturating_mul(10).saturating_add(nd as i64);
328 chars.next();
329 } else {
330 break;
331 }
332 }
333 segments.push(NaturalSegment::Number(OrderedF64(value as f64)));
334 continue;
335 }
336
337 text_buf.push(c);
339 }
340
341 if !text_buf.is_empty() {
342 segments.push(NaturalSegment::Text(text_buf));
343 }
344
345 segments
346}