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 | pub const fn q57(v: i32) -> i64 { |
12 | debug_assert!(v >= -64 && v <= 63); |
13 | (v as i64) << 57 |
14 | } |
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 | pub const fn bexp64(logq57: i64) -> i64 { |
35 | let ipart = (logq57 >> 57) as i32; |
36 | if ipart < 0 { |
37 | return 0; |
38 | } |
39 | if ipart >= 63 { |
40 | return 0x7FFFFFFFFFFFFFFF; |
41 | } |
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 | let mut z = logq57 - q57(ipart); |
46 | let mut w: i64; |
47 | if z != 0 { |
48 | // Rust has 128 bit multiplies, so it should be possible to do this |
49 | // faster without losing accuracy. |
50 | 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 | w = 0x26A3D0E401DD846D; |
59 | let mut i: i64 = 0; |
60 | loop { |
61 | let mask = -((z < 0) as i64); |
62 | w += ((w >> (i + 1)) + mask) ^ mask; |
63 | z -= (ATANH_LOG2[i as usize] + mask) ^ mask; |
64 | // Repeat iteration 4. |
65 | if i >= 3 { |
66 | break; |
67 | } |
68 | z *= 2; |
69 | i += 1; |
70 | } |
71 | loop { |
72 | let mask = -((z < 0) as i64); |
73 | w += ((w >> (i + 1)) + mask) ^ mask; |
74 | z -= (ATANH_LOG2[i as usize] + mask) ^ mask; |
75 | // Repeat iteration 13. |
76 | if i >= 12 { |
77 | break; |
78 | } |
79 | z *= 2; |
80 | i += 1; |
81 | } |
82 | while i < 32 { |
83 | let mask = -((z < 0) as i64); |
84 | w += ((w >> (i + 1)) + mask) ^ mask; |
85 | z = (z - ((ATANH_LOG2[i as usize] + mask) ^ mask)) * 2; |
86 | i += 1; |
87 | } |
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 | let mut wlo: i32 = 0; |
94 | 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 | let mask = -((z < 0) as i64); |
101 | wlo += (((w >> i) + mask) ^ mask) as i32; |
102 | z -= (ATANH_LOG2[31] + mask) ^ mask; |
103 | // Repeat iteration 40. |
104 | if i >= 39 { |
105 | break; |
106 | } |
107 | z *= 2; |
108 | i += 1; |
109 | } |
110 | while i < 61 { |
111 | let mask = -((z < 0) as i64); |
112 | wlo += (((w >> i) + mask) ^ mask) as i32; |
113 | z = (z - ((ATANH_LOG2[31] + mask) ^ mask)) * 2; |
114 | i += 1; |
115 | } |
116 | } |
117 | w = (w << 1) + (wlo as i64); |
118 | } else { |
119 | w = 1i64 << 62; |
120 | } |
121 | if ipart < 62 { |
122 | w = ((w >> (61 - ipart)) + 1) >> 1; |
123 | } |
124 | w |
125 | } |
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 | pub const fn blog64(n: i64) -> i64 { |
131 | if n <= 0 { |
132 | return -1; |
133 | } |
134 | let ipart = 63 - n.leading_zeros() as i32; |
135 | let w = if ipart > 61 { n >> (ipart - 61) } else { n << (61 - ipart) }; |
136 | if (w & (w - 1)) == 0 { |
137 | return q57(ipart); |
138 | } |
139 | // z is the fractional part of the log in Q61 format. |
140 | 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 | let mut x = w + (1i64 << 61); |
146 | let mut y = w - (1i64 << 61); |
147 | // Repeat iteration 4. |
148 | // Repeat iteration 13. |
149 | // Repeat iteration 40. |
150 | let bounds = [3, 12, 39, 61]; |
151 | let mut i = 0; |
152 | let mut j = 0; |
153 | loop { |
154 | let end = bounds[j]; |
155 | loop { |
156 | let mask = -((y < 0) as i64); |
157 | // ATANH_LOG2 has converged at iteration 32. |
158 | z += ((ATANH_LOG2[if i < 31 { i } else { 31 }] >> i) + mask) ^ mask; |
159 | let u = x >> (i + 1); |
160 | x -= ((y >> (i + 1)) + mask) ^ mask; |
161 | y -= (u + mask) ^ mask; |
162 | if i == end { |
163 | break; |
164 | } |
165 | i += 1; |
166 | } |
167 | j += 1; |
168 | if j == bounds.len() { |
169 | break; |
170 | } |
171 | } |
172 | z = (z + 8) >> 4; |
173 | q57(ipart) + z |
174 | } |
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 | pub const fn blog32(n: u32) -> i32 { |
181 | if n == 0 { |
182 | return -1; |
183 | } |
184 | let ipart = 31 - n.leading_zeros() as i32; |
185 | let n = n as i64; |
186 | let w = if ipart > 61 { n >> (ipart - 61) } else { n << (61 - ipart) }; |
187 | if (w & (w - 1)) == 0 { |
188 | return ipart << 24; |
189 | } |
190 | // z is the fractional part of the log in Q61 format. |
191 | 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 | let mut x = w + (1i64 << 61); |
197 | let mut y = w - (1i64 << 61); |
198 | // Repeat iteration 4. |
199 | // Repeat iteration 13. |
200 | let bounds = [3, 12, 29]; |
201 | let mut i = 0; |
202 | let mut j = 0; |
203 | loop { |
204 | let end = bounds[j]; |
205 | loop { |
206 | let mask = -((y < 0) as i64); |
207 | z += ((ATANH_LOG2[i] >> i) + mask) ^ mask; |
208 | let u = x >> (i + 1); |
209 | x -= ((y >> (i + 1)) + mask) ^ mask; |
210 | y -= (u + mask) ^ mask; |
211 | if i == end { |
212 | break; |
213 | } |
214 | i += 1; |
215 | } |
216 | j += 1; |
217 | if j == bounds.len() { |
218 | break; |
219 | } |
220 | } |
221 | const SHIFT: usize = 61 - 24; |
222 | z = (z + (1 << SHIFT >> 1)) >> SHIFT; |
223 | (ipart << 24) + z as i32 |
224 | } |
225 | |
226 | /// Converts a Q57 fixed-point fraction to Q24 by rounding. |
227 | pub const fn q57_to_q24(v: i64) -> i32 { |
228 | (((v >> 32) + 1) >> 1) as i32 |
229 | } |
230 | |
231 | /// Converts a Q24 fixed-point fraction to Q57. |
232 | pub const fn q24_to_q57(v: i32) -> i64 { |
233 | (v as i64) << 33 |
234 | } |
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 | pub const fn bexp_q24(log_scale: i32) -> i64 { |
242 | if log_scale < 23 << 24 { |
243 | let ret: i64 = bexp64(((log_scale as i64) << 33) + q57(24)); |
244 | if ret < (1i64 << 47) - 1 { |
245 | return ret; |
246 | } |
247 | } |
248 | (1i64 << 47) - 1 |
249 | } |
250 | |
251 | /// Polynomial approximation of a binary exponential. |
252 | /// Q10 input, Q0 output. |
253 | #[allow (unused)] |
254 | pub const fn bexp32_q10(z: i32) -> u32 { |
255 | let ipart: i32 = z >> 10; |
256 | let mut n: u32 = ((z & ((1 << 10) - 1)) << 4) as u32; |
257 | n = ({ |
258 | n * (((n * (((n * (((n * 3548) >> 15) + 6817)) >> 15) + 15823)) >> 15) |
259 | + 22708) |
260 | } >> 15) |
261 | + 16384; |
262 | if 14 - ipart > 0 { |
263 | (n + (1 << (13 - ipart))) >> (14 - ipart) |
264 | } else { |
265 | n << (ipart - 14) |
266 | } |
267 | } |
268 | |
269 | /// Polynomial approximation of a binary logarithm. |
270 | /// Q0 input, Q11 output. |
271 | pub const fn blog32_q11(w: u32) -> i32 { |
272 | if w == 0 { |
273 | return -1; |
274 | } |
275 | let ipart: i32 = 32 - w.leading_zeros() as i32; |
276 | let n: i32 = if ipart - 16 > 0 { w >> (ipart - 16) } else { w << (16 - ipart) } |
277 | as i32 |
278 | - 32768 |
279 | - 16384; |
280 | let fpart: i32 = ({ |
281 | n * (((n * (((n * (((n * -1402) >> 15) + 2546)) >> 15) - 5216)) >> 15) |
282 | + 15745) |
283 | } >> 15) |
284 | - 6797; |
285 | (ipart << 11) + (fpart >> 3) |
286 | } |
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 | } |
361 | |