Coverage Report

Created: 2026-03-10 07:34

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/rav1e-0.8.1/src/util/kmeans.rs
Line
Count
Source
1
// Copyright (c) 2022-2023, The rav1e contributors. All rights reserved
2
//
3
// This source code is subject to the terms of the BSD 2 Clause License and
4
// the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
5
// was not distributed with this source code in the LICENSE file, you can
6
// obtain it at www.aomedia.org/license/software. If the Alliance for Open
7
// Media Patent License 1.0 was not distributed with this source code in the
8
// PATENTS file, you can obtain it at www.aomedia.org/license/patent.
9
10
/// Find k-means for a sorted slice of integers that can be summed in `i64`.
11
0
pub fn kmeans<T, const K: usize>(data: &[T]) -> [T; K]
12
0
where
13
0
  T: Copy,
14
0
  T: Into<i64>,
15
0
  T: PartialEq,
16
0
  T: PartialOrd,
17
0
  i64: TryInto<T>,
18
0
  <i64 as std::convert::TryInto<T>>::Error: std::fmt::Debug,
19
{
20
0
  let mut low = [0; K];
21
0
  for (i, val) in low.iter_mut().enumerate() {
22
0
    *val = (i * (data.len() - 1)) / (K - 1);
23
0
  }
24
0
  let mut means = low.map(|i| unsafe { *data.get_unchecked(i) });
Unexecuted instantiation: rav1e::util::kmeans::kmeans::<i16, 3>::{closure#0}
Unexecuted instantiation: rav1e::util::kmeans::kmeans::<i16, 4>::{closure#0}
Unexecuted instantiation: rav1e::util::kmeans::kmeans::<i16, 5>::{closure#0}
Unexecuted instantiation: rav1e::util::kmeans::kmeans::<i16, 6>::{closure#0}
Unexecuted instantiation: rav1e::util::kmeans::kmeans::<i16, 7>::{closure#0}
Unexecuted instantiation: rav1e::util::kmeans::kmeans::<i16, 8>::{closure#0}
25
0
  let mut high = low;
26
0
  let mut sum = [0i64; K];
27
0
  high[K - 1] = data.len();
28
0
  sum[K - 1] = means[K - 1].into();
29
30
  // Constrain complexity to O(n log n)
31
0
  let limit = 2 * (usize::BITS - data.len().leading_zeros());
32
0
  for _ in 0..limit {
33
0
    for (i, (threshold, (low, high))) in (means.iter().skip(1).zip(&means))
34
0
      .map(|(&c1, &c2)| unsafe {
35
0
        ((c1.into() + c2.into() + 1) >> 1).try_into().unwrap_unchecked()
36
0
      })
Unexecuted instantiation: rav1e::util::kmeans::kmeans::<i16, 3>::{closure#1}
Unexecuted instantiation: rav1e::util::kmeans::kmeans::<i16, 4>::{closure#1}
Unexecuted instantiation: rav1e::util::kmeans::kmeans::<i16, 5>::{closure#1}
Unexecuted instantiation: rav1e::util::kmeans::kmeans::<i16, 6>::{closure#1}
Unexecuted instantiation: rav1e::util::kmeans::kmeans::<i16, 7>::{closure#1}
Unexecuted instantiation: rav1e::util::kmeans::kmeans::<i16, 8>::{closure#1}
37
0
      .zip(low.iter_mut().skip(1).zip(&mut high))
38
0
      .enumerate()
39
    {
40
0
      unsafe {
41
0
        scan(high, low, sum.get_unchecked_mut(i..=i + 1), data, threshold);
42
0
      }
43
    }
44
0
    let mut changed = false;
45
0
    for (((m, sum), high), low) in
46
0
      means.iter_mut().zip(&sum).zip(&high).zip(&low)
47
    {
48
0
      let count = (high - low) as i64;
49
0
      if count == 0 {
50
0
        continue;
51
0
      }
52
0
      let new_mean = unsafe {
53
0
        ((sum + (count >> 1)).saturating_div(count))
54
0
          .try_into()
55
0
          .unwrap_unchecked()
56
      };
57
0
      changed |= *m != new_mean;
58
0
      *m = new_mean;
59
    }
60
0
    if !changed {
61
0
      break;
62
0
    }
63
  }
64
65
0
  means
66
0
}
Unexecuted instantiation: rav1e::util::kmeans::kmeans::<i16, 3>
Unexecuted instantiation: rav1e::util::kmeans::kmeans::<i16, 4>
Unexecuted instantiation: rav1e::util::kmeans::kmeans::<i16, 5>
Unexecuted instantiation: rav1e::util::kmeans::kmeans::<i16, 6>
Unexecuted instantiation: rav1e::util::kmeans::kmeans::<i16, 7>
Unexecuted instantiation: rav1e::util::kmeans::kmeans::<i16, 8>
67
68
#[inline(never)]
69
0
unsafe fn scan<T>(
70
0
  high: &mut usize, low: &mut usize, sum: &mut [i64], data: &[T], t: T,
71
0
) where
72
0
  T: Copy,
73
0
  T: Into<i64>,
74
0
  T: PartialEq,
75
0
  T: PartialOrd,
76
{
77
0
  let mut n = *high;
78
0
  let mut s = *sum.get_unchecked(0);
79
0
  for &d in data.get_unchecked(..n).iter().rev().take_while(|&d| *d > t) {
80
0
    s -= d.into();
81
0
    n -= 1;
82
0
  }
83
0
  for &d in data.get_unchecked(n..).iter().take_while(|&d| *d <= t) {
84
0
    s += d.into();
85
0
    n += 1;
86
0
  }
87
0
  *high = n;
88
0
  *sum.get_unchecked_mut(0) = s;
89
90
0
  let mut n = *low;
91
0
  let mut s = *sum.get_unchecked(1);
92
0
  for &d in data.get_unchecked(n..).iter().take_while(|&d| *d < t) {
93
0
    s -= d.into();
94
0
    n += 1;
95
0
  }
96
0
  for &d in data.get_unchecked(..n).iter().rev().take_while(|&d| *d >= t) {
97
0
    s += d.into();
98
0
    n -= 1;
99
0
  }
100
0
  *low = n;
101
0
  *sum.get_unchecked_mut(1) = s;
102
0
}
103
104
#[cfg(test)]
105
mod test {
106
  use super::*;
107
108
  #[test]
109
  fn three_means() {
110
    let mut data = [1, 2, 3, 10, 11, 12, 20, 21, 22];
111
    data.sort_unstable();
112
    let centroids = kmeans(&data);
113
    assert_eq!(centroids, [2, 11, 21]);
114
  }
115
116
  #[test]
117
  fn four_means() {
118
    let mut data = [30, 31, 32, 1, 2, 3, 10, 11, 12, 20, 21, 22];
119
    data.sort_unstable();
120
    let centroids = kmeans(&data);
121
    assert_eq!(centroids, [2, 11, 21, 31]);
122
  }
123
}