Coverage Report

Created: 2026-10-28 07:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ffmpeg/libswscale/rational64.c
Line
Count
Source
1
/*
2
 * 64-bit rational numbers
3
 * Copyright (c) 2025 Niklas Haas
4
 * Copyright (c) 2003 Michael Niedermayer <michaelni@gmx.at>
5
 *
6
 * This file is part of FFmpeg.
7
 *
8
 * FFmpeg is free software; you can redistribute it and/or
9
 * modify it under the terms of the GNU Lesser General Public
10
 * License as published by the Free Software Foundation; either
11
 * version 2.1 of the License, or (at your option) any later version.
12
 *
13
 * FFmpeg is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16
 * Lesser General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU Lesser General Public
19
 * License along with FFmpeg; if not, write to the Free Software
20
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21
 */
22
23
/**
24
 * @file
25
 * 64-bit rational numbers
26
 * @author Niklas Haas
27
 */
28
29
#include <limits.h>
30
31
#include "libavutil/attributes.h"
32
#include "libavutil/common.h"
33
#include "libavutil/int128.h"
34
#include "libavutil/intmath.h"
35
#include "rational64.h"
36
37
static av_always_inline uint64_t high64(av_int128 a)
38
0
{
39
0
    return av_from128u(av_shr128(a, 64));
40
0
}
41
42
/* unsigned version of av_gcd(); see libavutil/mathematics.c */
43
static av_always_inline uint64_t gcd_u64(uint64_t a, uint64_t b)
44
0
{
45
0
    if (!a)
46
0
        return b;
47
0
    if (!b)
48
0
        return a;
49
50
0
    const int k = ff_ctzll(a | b);
51
0
    a >>= ff_ctzll(a);
52
0
    do {
53
0
        b >>= ff_ctzll(b);
54
0
        if (a > b)
55
0
            FFSWAP(uint64_t, a, b);
56
0
        b -= a;
57
0
    } while (b);
58
0
    return a << k;
59
0
}
60
61
static av_int128 gcd128(av_int128 a, av_int128 b)
62
0
{
63
0
    while (av_test128(b)) {
64
0
        av_int128 tmp = b;
65
0
        b = av_mod128(a, b);
66
0
        a = tmp;
67
0
    }
68
0
    return a;
69
0
}
70
71
static AVRational64 reduce64(av_int128 num, av_int128 den)
72
0
{
73
0
    const av_int128 zero = av_to128i(0);
74
0
    const av_int128 max  = av_to128i(INT64_MAX);
75
0
    const int num_sign = av_cmp128(num, zero) < 0;
76
0
    const int den_sign = av_cmp128(den, zero) < 0;
77
0
    if (num_sign)
78
0
        num = av_sub128(zero, num);
79
0
    if (den_sign)
80
0
        den = av_sub128(zero, den);
81
82
0
    if (!high64(num) && !high64(den)) {
83
        /* Fast path: both values already fit into 64 bits */
84
0
        uint64_t n = av_from128u(num), d = av_from128u(den);
85
0
        const uint64_t gcd = gcd_u64(n, d);
86
0
        if (gcd) {
87
0
            n /= gcd;
88
0
            d /= gcd;
89
0
        }
90
91
0
        if (!((n | d) >> 63)) {
92
0
            AVRational64 res = { n, d };
93
0
            if (num_sign ^ den_sign)
94
0
                res.num = -res.num;
95
0
            return res;
96
0
        }
97
98
        /* Fall back to 128-bit arithmetic for values > INT64_MAX */
99
0
        num = av_to128u(n);
100
0
        den = av_to128u(d);
101
0
    }
102
103
0
    const av_int128 gcd = gcd128(num, den);
104
0
    if (av_test128(gcd)) {
105
0
        num = av_div128(num, gcd);
106
0
        den = av_div128(den, gcd);
107
0
    }
108
109
0
    av_uint128 a0n = av_to128u(0), a0d = av_to128u(1);
110
0
    av_uint128 a1n = av_to128u(1), a1d = av_to128u(0);
111
0
    if (av_cmp128(num, max) <= 0 && av_cmp128(den, max) <= 0) {
112
0
        a1n = num;
113
0
        a1d = den;
114
0
        goto done;
115
0
    }
116
117
0
    while (av_test128(den)) {
118
0
        av_int128 x        = av_div128(num, den);
119
0
        av_int128 next_den = av_sub128(num, av_mul128(den, x));
120
0
        av_uint128 a2n     = av_add128(av_mul128(x, a1n), a0n);
121
0
        av_uint128 a2d     = av_add128(av_mul128(x, a1d), a0d);
122
123
0
        if (av_cmp128(a2n, max) > 0 || av_cmp128(a2d, max) > 0) {
124
0
            if (av_test128(a1n))
125
0
                x = av_div128(av_sub128(max, a0n), a1n);
126
0
            if (av_test128(a1d)) {
127
0
                av_uint128 tmp = av_div128(av_sub128(max, a0d), a1d);
128
0
                x = av_min128(x, tmp);
129
0
            }
130
131
0
            av_uint128 x1d = av_mul128(x, a1d);
132
0
            av_uint128 a = av_mul128(den, av_add128(av_add128(x1d, x1d), a0d));
133
0
            av_uint128 b = av_mul128(num, a1d);
134
0
            if (av_cmp128(a, b) > 0) {
135
0
                a1n = av_add128(av_mul128(x, a1n), a0n);
136
0
                a1d = av_add128(x1d, a0d);
137
0
            }
138
0
            break;
139
0
        }
140
141
0
        a0n = a1n;
142
0
        a0d = a1d;
143
0
        a1n = a2n;
144
0
        a1d = a2d;
145
0
        num = den;
146
0
        den = next_den;
147
0
    }
148
149
0
done:;
150
0
    AVRational64 res = { av_from128i(a1n), av_from128i(a1d) };
151
0
    if (num_sign ^ den_sign)
152
0
        res.num = -res.num;
153
0
    return res;
154
0
}
155
156
static av_always_inline int fits31(int64_t x)
157
0
{
158
0
    return x >= -INT32_MAX && x <= INT32_MAX;
159
0
}
160
161
static av_always_inline int fits31_q(AVRational64 b, AVRational64 c)
162
0
{
163
0
    return fits31(b.num) && fits31(b.den) && fits31(c.num) && fits31(c.den);
164
0
}
165
166
/* Simplified version of reduce64() for small values */
167
static AVRational64 reduce64_fast(int64_t num, int64_t den)
168
0
{
169
0
    const int sign = (num < 0) ^ (den < 0);
170
0
    uint64_t n = num < 0 ? -(uint64_t) num : num;
171
0
    uint64_t d = den < 0 ? -(uint64_t) den : den;
172
0
    const uint64_t gcd = gcd_u64(n, d);
173
0
    if (gcd) {
174
0
        n /= gcd;
175
0
        d /= gcd;
176
0
    }
177
178
0
    AVRational64 res = { n, d };
179
0
    if (sign)
180
0
        res.num = -res.num;
181
0
    return res;
182
0
}
183
184
185
int ff_cmp_q64(AVRational64 a, AVRational64 b)
186
0
{
187
0
    int test;
188
0
    if (fits31_q(a, b)) {
189
0
        const int64_t p = a.num * b.den;
190
0
        const int64_t q = b.num * a.den;
191
0
        test = (p > q) - (p < q);
192
0
    } else {
193
0
        const av_int128 p = av_mul128(av_to128i(a.num), av_to128i(b.den));
194
0
        const av_int128 q = av_mul128(av_to128i(b.num), av_to128i(a.den));
195
0
        test = av_cmp128(p, q);
196
0
    }
197
198
0
    if (test)
199
0
        return (a.den < 0) ^ (b.den < 0) ? -test : test;
200
0
    else if (b.den && a.den)
201
0
        return 0;
202
0
    else if (a.num && b.num)
203
0
        return (a.num >> 63) - (b.num >> 63);
204
0
    else
205
0
        return INT_MIN;
206
0
}
207
208
AVRational64 ff_mul_q64(AVRational64 b, AVRational64 c)
209
0
{
210
0
    if (fits31_q(b, c))
211
0
        return reduce64_fast(b.num * c.num, b.den * c.den);
212
213
0
    return reduce64(av_mul128(av_to128i(b.num), av_to128i(c.num)),
214
0
                    av_mul128(av_to128i(b.den), av_to128i(c.den)));
215
0
}
216
217
AVRational64 ff_div_q64(AVRational64 b, AVRational64 c)
218
0
{
219
0
    return ff_mul_q64(b, ff_inv_q64(c));
220
0
}
221
222
AVRational64 ff_add_q64(AVRational64 b, AVRational64 c)
223
0
{
224
0
    if (fits31_q(b, c))
225
0
        return reduce64_fast(b.num * c.den + c.num * b.den, b.den * c.den);
226
227
0
    return reduce64(av_add128(av_mul128(av_to128i(b.num), av_to128i(c.den)),
228
0
                              av_mul128(av_to128i(c.num), av_to128i(b.den))),
229
0
                    av_mul128(av_to128i(b.den), av_to128i(c.den)));
230
0
}
231
232
AVRational64 ff_sub_q64(AVRational64 b, AVRational64 c)
233
0
{
234
0
    if (fits31_q(b, c))
235
0
        return reduce64_fast(b.num * c.den - c.num * b.den, b.den * c.den);
236
237
0
    return reduce64(av_sub128(av_mul128(av_to128i(b.num), av_to128i(c.den)),
238
0
                              av_mul128(av_to128i(c.num), av_to128i(b.den))),
239
0
                    av_mul128(av_to128i(b.den), av_to128i(c.den)));
240
0
}