Coverage Report

Created: 2025-10-12 08:06

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/rav1e-0.7.1/src/util/logexp.rs
Line
Count
Source
1
// Copyright (c) 2019-2022, 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
/// Convert an integer into a Q57 fixed-point fraction.
11
0
pub const fn q57(v: i32) -> i64 {
12
0
  debug_assert!(v >= -64 && v <= 63);
13
0
  (v as i64) << 57
14
0
}
15
16
#[rustfmt::skip]
17
const ATANH_LOG2: &[i64; 32] = &[
18
  0x32B803473F7AD0F4, 0x2F2A71BD4E25E916, 0x2E68B244BB93BA06,
19
  0x2E39FB9198CE62E4, 0x2E2E683F68565C8F, 0x2E2B850BE2077FC1,
20
  0x2E2ACC58FE7B78DB, 0x2E2A9E2DE52FD5F2, 0x2E2A92A338D53EEC,
21
  0x2E2A8FC08F5E19B6, 0x2E2A8F07E51A485E, 0x2E2A8ED9BA8AF388,
22
  0x2E2A8ECE2FE7384A, 0x2E2A8ECB4D3E4B1A, 0x2E2A8ECA94940FE8,
23
  0x2E2A8ECA6669811D, 0x2E2A8ECA5ADEDD6A, 0x2E2A8ECA57FC347E,
24
  0x2E2A8ECA57438A43, 0x2E2A8ECA57155FB4, 0x2E2A8ECA5709D510,
25
  0x2E2A8ECA5706F267, 0x2E2A8ECA570639BD, 0x2E2A8ECA57060B92,
26
  0x2E2A8ECA57060008, 0x2E2A8ECA5705FD25, 0x2E2A8ECA5705FC6C,
27
  0x2E2A8ECA5705FC3E, 0x2E2A8ECA5705FC33, 0x2E2A8ECA5705FC30,
28
  0x2E2A8ECA5705FC2F, 0x2E2A8ECA5705FC2F
29
];
30
31
/// Computes the binary exponential of `logq57`.
32
/// `logq57`: a log base 2 in Q57 format.
33
/// Returns a 64 bit integer in Q0 (no fraction).
34
0
pub const fn bexp64(logq57: i64) -> i64 {
35
0
  let ipart = (logq57 >> 57) as i32;
36
0
  if ipart < 0 {
37
0
    return 0;
38
0
  }
39
0
  if ipart >= 63 {
40
0
    return 0x7FFFFFFFFFFFFFFF;
41
0
  }
42
  // z is the fractional part of the log in Q62 format.
43
  // We need 1 bit of headroom since the magnitude can get larger than 1
44
  //  during the iteration, and a sign bit.
45
0
  let mut z = logq57 - q57(ipart);
46
  let mut w: i64;
47
0
  if z != 0 {
48
    // Rust has 128 bit multiplies, so it should be possible to do this
49
    //  faster without losing accuracy.
50
0
    z <<= 5;
51
    // w is the exponential in Q61 format (since it also needs headroom and can
52
    //  get as large as 2.0); we could get another bit if we dropped the sign,
53
    //  but we'll recover that bit later anyway.
54
    // Ideally this should start out as
55
    //   \lim_{n->\infty} 2^{61}/\product_{i=1}^n \sqrt{1-2^{-2i}}
56
    //  but in order to guarantee convergence we have to repeat iterations 4,
57
    //  13 (=3*4+1), and 40 (=3*13+1, etc.), so it winds up somewhat larger.
58
0
    w = 0x26A3D0E401DD846D;
59
0
    let mut i: i64 = 0;
60
    loop {
61
0
      let mask = -((z < 0) as i64);
62
0
      w += ((w >> (i + 1)) + mask) ^ mask;
63
0
      z -= (ATANH_LOG2[i as usize] + mask) ^ mask;
64
      // Repeat iteration 4.
65
0
      if i >= 3 {
66
0
        break;
67
0
      }
68
0
      z *= 2;
69
0
      i += 1;
70
    }
71
    loop {
72
0
      let mask = -((z < 0) as i64);
73
0
      w += ((w >> (i + 1)) + mask) ^ mask;
74
0
      z -= (ATANH_LOG2[i as usize] + mask) ^ mask;
75
      // Repeat iteration 13.
76
0
      if i >= 12 {
77
0
        break;
78
0
      }
79
0
      z *= 2;
80
0
      i += 1;
81
    }
82
0
    while i < 32 {
83
0
      let mask = -((z < 0) as i64);
84
0
      w += ((w >> (i + 1)) + mask) ^ mask;
85
0
      z = (z - ((ATANH_LOG2[i as usize] + mask) ^ mask)) * 2;
86
0
      i += 1;
87
0
    }
88
    // Skip the remaining iterations unless we really require that much
89
    //  precision.
90
    // We could have bailed out earlier for smaller iparts, but that would
91
    //  require initializing w from a table, as the limit doesn't converge to
92
    //  61-bit precision until n=30.
93
0
    let mut wlo: i32 = 0;
94
0
    if ipart > 30 {
95
      // For these iterations, we just update the low bits, as the high bits
96
      //  can't possibly be affected.
97
      // OD_ATANH_LOG2 has also converged (it actually did so one iteration
98
      //  earlier, but that's no reason for an extra special case).
99
      loop {
100
0
        let mask = -((z < 0) as i64);
101
0
        wlo += (((w >> i) + mask) ^ mask) as i32;
102
0
        z -= (ATANH_LOG2[31] + mask) ^ mask;
103
        // Repeat iteration 40.
104
0
        if i >= 39 {
105
0
          break;
106
0
        }
107
0
        z *= 2;
108
0
        i += 1;
109
      }
110
0
      while i < 61 {
111
0
        let mask = -((z < 0) as i64);
112
0
        wlo += (((w >> i) + mask) ^ mask) as i32;
113
0
        z = (z - ((ATANH_LOG2[31] + mask) ^ mask)) * 2;
114
0
        i += 1;
115
0
      }
116
0
    }
117
0
    w = (w << 1) + (wlo as i64);
118
0
  } else {
119
0
    w = 1i64 << 62;
120
0
  }
121
0
  if ipart < 62 {
122
0
    w = ((w >> (61 - ipart)) + 1) >> 1;
123
0
  }
124
0
  w
125
0
}
126
127
/// Computes the binary log of `n`.
128
/// `n`: a 64-bit integer in Q0 (no fraction).
129
/// Returns a 64-bit log in Q57.
130
0
pub const fn blog64(n: i64) -> i64 {
131
0
  if n <= 0 {
132
0
    return -1;
133
0
  }
134
0
  let ipart = 63 - n.leading_zeros() as i32;
135
0
  let w = if ipart > 61 { n >> (ipart - 61) } else { n << (61 - ipart) };
136
0
  if (w & (w - 1)) == 0 {
137
0
    return q57(ipart);
138
0
  }
139
  // z is the fractional part of the log in Q61 format.
140
0
  let mut z: i64 = 0;
141
  // Rust has 128 bit multiplies, so it should be possible to do this
142
  //  faster without losing accuracy.
143
  // x and y are the cosh() and sinh(), respectively, in Q61 format.
144
  // We are computing z = 2*atanh(y/x) = 2*atanh((w - 1)/(w + 1)).
145
0
  let mut x = w + (1i64 << 61);
146
0
  let mut y = w - (1i64 << 61);
147
  // Repeat iteration 4.
148
  // Repeat iteration 13.
149
  // Repeat iteration 40.
150
0
  let bounds = [3, 12, 39, 61];
151
0
  let mut i = 0;
152
0
  let mut j = 0;
153
  loop {
154
0
    let end = bounds[j];
155
    loop {
156
0
      let mask = -((y < 0) as i64);
157
      // ATANH_LOG2 has converged at iteration 32.
158
0
      z += ((ATANH_LOG2[if i < 31 { i } else { 31 }] >> i) + mask) ^ mask;
159
0
      let u = x >> (i + 1);
160
0
      x -= ((y >> (i + 1)) + mask) ^ mask;
161
0
      y -= (u + mask) ^ mask;
162
0
      if i == end {
163
0
        break;
164
0
      }
165
0
      i += 1;
166
    }
167
0
    j += 1;
168
0
    if j == bounds.len() {
169
0
      break;
170
0
    }
171
  }
172
0
  z = (z + 8) >> 4;
173
0
  q57(ipart) + z
174
0
}
175
176
/// Computes the binary log of `n`.
177
/// `n`: an unsigned 32-bit integer in Q0 (no fraction).
178
/// Returns a signed 32-bit log in Q24.
179
#[allow(unused)]
180
0
pub const fn blog32(n: u32) -> i32 {
181
0
  if n == 0 {
182
0
    return -1;
183
0
  }
184
0
  let ipart = 31 - n.leading_zeros() as i32;
185
0
  let n = n as i64;
186
0
  let w = if ipart > 61 { n >> (ipart - 61) } else { n << (61 - ipart) };
187
0
  if (w & (w - 1)) == 0 {
188
0
    return ipart << 24;
189
0
  }
190
  // z is the fractional part of the log in Q61 format.
191
0
  let mut z: i64 = 0;
192
  // Rust has 128 bit multiplies, so it should be possible to do this
193
  //  faster without losing accuracy.
194
  // x and y are the cosh() and sinh(), respectively, in Q61 format.
195
  // We are computing z = 2*atanh(y/x) = 2*atanh((w - 1)/(w + 1)).
196
0
  let mut x = w + (1i64 << 61);
197
0
  let mut y = w - (1i64 << 61);
198
  // Repeat iteration 4.
199
  // Repeat iteration 13.
200
0
  let bounds = [3, 12, 29];
201
0
  let mut i = 0;
202
0
  let mut j = 0;
203
  loop {
204
0
    let end = bounds[j];
205
    loop {
206
0
      let mask = -((y < 0) as i64);
207
0
      z += ((ATANH_LOG2[i] >> i) + mask) ^ mask;
208
0
      let u = x >> (i + 1);
209
0
      x -= ((y >> (i + 1)) + mask) ^ mask;
210
0
      y -= (u + mask) ^ mask;
211
0
      if i == end {
212
0
        break;
213
0
      }
214
0
      i += 1;
215
    }
216
0
    j += 1;
217
0
    if j == bounds.len() {
218
0
      break;
219
0
    }
220
  }
221
  const SHIFT: usize = 61 - 24;
222
0
  z = (z + (1 << SHIFT >> 1)) >> SHIFT;
223
0
  (ipart << 24) + z as i32
224
0
}
225
226
/// Converts a Q57 fixed-point fraction to Q24 by rounding.
227
0
pub const fn q57_to_q24(v: i64) -> i32 {
228
0
  (((v >> 32) + 1) >> 1) as i32
229
0
}
230
231
/// Converts a Q24 fixed-point fraction to Q57.
232
0
pub const fn q24_to_q57(v: i32) -> i64 {
233
0
  (v as i64) << 33
234
0
}
235
236
/// Binary exponentiation of a `log_scale` with 24-bit fractional precision and
237
///  saturation.
238
/// `log_scale`: A binary logarithm in Q24 format.
239
/// Returns the binary exponential in Q24 format, saturated to 2**47 - 1 if
240
///  `log_scale` was too large.
241
0
pub const fn bexp_q24(log_scale: i32) -> i64 {
242
0
  if log_scale < 23 << 24 {
243
0
    let ret = bexp64(((log_scale as i64) << 33) + q57(24));
244
0
    if ret < (1i64 << 47) - 1 {
245
0
      return ret;
246
0
    }
247
0
  }
248
0
  (1i64 << 47) - 1
249
0
}
250
251
/// Polynomial approximation of a binary exponential.
252
/// Q10 input, Q0 output.
253
#[allow(unused)]
254
0
pub const fn bexp32_q10(z: i32) -> u32 {
255
0
  let ipart = z >> 10;
256
0
  let mut n = ((z & ((1 << 10) - 1)) << 4) as u32;
257
0
  n = ({
258
0
    n * (((n * (((n * (((n * 3548) >> 15) + 6817)) >> 15) + 15823)) >> 15)
259
0
      + 22708)
260
0
  } >> 15)
261
0
    + 16384;
262
0
  if 14 - ipart > 0 {
263
0
    (n + (1 << (13 - ipart))) >> (14 - ipart)
264
  } else {
265
0
    n << (ipart - 14)
266
  }
267
0
}
268
269
/// Polynomial approximation of a binary logarithm.
270
/// Q0 input, Q11 output.
271
0
pub const fn blog32_q11(w: u32) -> i32 {
272
0
  if w == 0 {
273
0
    return -1;
274
0
  }
275
0
  let ipart = 32 - w.leading_zeros() as i32;
276
0
  let n = if ipart - 16 > 0 { w >> (ipart - 16) } else { w << (16 - ipart) }
277
    as i32
278
    - 32768
279
    - 16384;
280
0
  let fpart = ({
281
0
    n * (((n * (((n * (((n * -1402) >> 15) + 2546)) >> 15) - 5216)) >> 15)
282
0
      + 15745)
283
0
  } >> 15)
284
0
    - 6797;
285
0
  (ipart << 11) + (fpart >> 3)
286
0
}
287
288
#[cfg(test)]
289
mod test {
290
  use super::*;
291
292
  #[test]
293
  fn blog64_vectors() {
294
    assert!(blog64(1793) == 0x159dc71e24d32daf);
295
    assert!(blog64(0x678dde6e5fd29f05) == 0x7d6373ad151ca685);
296
  }
297
298
  #[test]
299
  fn bexp64_vectors() {
300
    assert!(bexp64(0x159dc71e24d32daf) == 1793);
301
    assert!((bexp64(0x7d6373ad151ca685) - 0x678dde6e5fd29f05).abs() < 29);
302
  }
303
304
  #[test]
305
  fn blog64_bexp64_round_trip() {
306
    for a in 1..=std::u16::MAX as i64 {
307
      let b = std::i64::MAX / a;
308
      let (log_a, log_b, log_ab) = (blog64(a), blog64(b), blog64(a * b));
309
      assert!((log_a + log_b - log_ab).abs() < 4);
310
      assert!(bexp64(log_a) == a);
311
      assert!((bexp64(log_b) - b).abs() < 128);
312
      assert!((bexp64(log_ab) - a * b).abs() < 128);
313
    }
314
  }
315
316
  #[test]
317
  fn blog32_vectors() {
318
    assert_eq!(blog32(0), -1);
319
    assert_eq!(blog32(1793), q57_to_q24(0x159dc71e24d32daf));
320
  }
321
322
  #[test]
323
  fn bexp_q24_vectors() {
324
    assert_eq!(bexp_q24(i32::MAX), (1i64 << 47) - 1);
325
    assert_eq!(
326
      (bexp_q24(q57_to_q24(0x159dc71e24d32daf)) + (1 << 24 >> 1)) >> 24,
327
      1793
328
    );
329
  }
330
331
  #[test]
332
  fn blog32_bexp_q24_round_trip() {
333
    for a in 1..=std::u16::MAX as u32 {
334
      let b = (std::u32::MAX >> 9) / a;
335
      let (log_a, log_b, log_ab) = (blog32(a), blog32(b), blog32(a * b));
336
      assert!((log_a + log_b - log_ab).abs() < 4);
337
      assert!((bexp_q24(log_a) - (i64::from(a) << 24)).abs() < (1 << 24 >> 1));
338
      assert!(((bexp_q24(log_b) >> 24) - i64::from(b)).abs() < 128);
339
      assert!(
340
        ((bexp_q24(log_ab) >> 24) - i64::from(a) * i64::from(b)).abs() < 128
341
      );
342
    }
343
  }
344
345
  #[test]
346
  fn blog32_q11_bexp32_q10_round_trip() {
347
    for a in 1..=std::i16::MAX as i32 {
348
      let b = std::i16::MAX as i32 / a;
349
      let (log_a, log_b, log_ab) = (
350
        blog32_q11(a as u32),
351
        blog32_q11(b as u32),
352
        blog32_q11(a as u32 * b as u32),
353
      );
354
      assert!((log_a + log_b - log_ab).abs() < 4);
355
      assert!((bexp32_q10((log_a + 1) >> 1) as i32 - a).abs() < 18);
356
      assert!((bexp32_q10((log_b + 1) >> 1) as i32 - b).abs() < 2);
357
      assert!((bexp32_q10((log_ab + 1) >> 1) as i32 - a * b).abs() < 18);
358
    }
359
  }
360
}