diff --git a/libavutil/Makefile b/libavutil/Makefile index 230ef9967d..c46b4f19a3 100644 --- a/libavutil/Makefile +++ b/libavutil/Makefile @@ -287,6 +287,7 @@ TESTPROGS = adler32 \ lfg \ lls \ log \ + mathematics \ md5 \ murmur3 \ opt \ diff --git a/libavutil/tests/mathematics.c b/libavutil/tests/mathematics.c new file mode 100644 index 0000000000..9b88abad92 --- /dev/null +++ b/libavutil/tests/mathematics.c @@ -0,0 +1,175 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include + +#include "libavutil/avutil.h" +#include "libavutil/macros.h" +#include "libavutil/mathematics.h" +#include "libavutil/rational.h" + +int main(void) +{ + int64_t last; + + /* av_gcd */ + printf("Testing av_gcd()\n"); + static const struct { int64_t a, b, expected; } gcd_tests[] = { + { 0, 0, 0 }, + { 1, 0, 1 }, + { 0, 1, 1 }, + { 6, 4, 2 }, + { 12, 8, 4 }, + { 17, 13, 1 }, + { 100, 75, 25 }, + { -6, 4, 2 }, + { 6, -4, 2 }, + }; + for (int i = 0; i < FF_ARRAY_ELEMS(gcd_tests); i++) + printf("gcd(%"PRId64", %"PRId64") = %"PRId64" %s\n", + gcd_tests[i].a, gcd_tests[i].b, + av_gcd(gcd_tests[i].a, gcd_tests[i].b), + av_gcd(gcd_tests[i].a, gcd_tests[i].b) == gcd_tests[i].expected ? "OK" : "FAIL"); + + /* av_rescale */ + printf("\nTesting av_rescale()\n"); + printf("rescale(6, 3, 2) = %"PRId64"\n", av_rescale(6, 3, 2)); + printf("rescale(0, 3, 2) = %"PRId64"\n", av_rescale(0, 3, 2)); + printf("rescale(1, 1, 1) = %"PRId64"\n", av_rescale(1, 1, 1)); + printf("rescale(-6, 3, 2) = %"PRId64"\n", av_rescale(-6, 3, 2)); + printf("rescale(90000, 1, 90000) = %"PRId64"\n", av_rescale(90000, 1, 90000)); + + /* av_rescale_rnd with different rounding modes */ + printf("\nTesting av_rescale_rnd()\n"); + static const struct { + int64_t a, b, c; + enum AVRounding rnd; + int64_t expected; + } rnd_tests[] = { + { 7, 1, 2, AV_ROUND_ZERO, 3 }, + { 7, 1, 2, AV_ROUND_INF, 4 }, + { 7, 1, 2, AV_ROUND_DOWN, 3 }, + { 7, 1, 2, AV_ROUND_UP, 4 }, + { 7, 1, 2, AV_ROUND_NEAR_INF, 4 }, + { -7, 1, 2, AV_ROUND_ZERO, -3 }, + { -7, 1, 2, AV_ROUND_INF, -4 }, + { -7, 1, 2, AV_ROUND_DOWN, -4 }, + { -7, 1, 2, AV_ROUND_UP, -3 }, + { 6, 1, 2, AV_ROUND_NEAR_INF, 3 }, + }; + for (int i = 0; i < FF_ARRAY_ELEMS(rnd_tests); i++) { + int64_t r = av_rescale_rnd(rnd_tests[i].a, rnd_tests[i].b, + rnd_tests[i].c, rnd_tests[i].rnd); + printf("rescale_rnd(%"PRId64", %"PRId64", %"PRId64", %d) = %"PRId64" %s\n", + rnd_tests[i].a, rnd_tests[i].b, rnd_tests[i].c, + rnd_tests[i].rnd, r, + r == rnd_tests[i].expected ? "OK" : "FAIL"); + } + + /* AV_ROUND_PASS_MINMAX */ + printf("\nTesting AV_ROUND_PASS_MINMAX\n"); + printf("INT64_MIN passthrough: %s\n", + av_rescale_rnd(INT64_MIN, 1, 2, + AV_ROUND_UP | AV_ROUND_PASS_MINMAX) == INT64_MIN ? "OK" : "FAIL"); + printf("INT64_MAX passthrough: %s\n", + av_rescale_rnd(INT64_MAX, 1, 2, + AV_ROUND_UP | AV_ROUND_PASS_MINMAX) == INT64_MAX ? "OK" : "FAIL"); + printf("normal with PASS_MINMAX: %"PRId64"\n", + av_rescale_rnd(3, 1, 2, AV_ROUND_UP | AV_ROUND_PASS_MINMAX)); + + /* large value rescale (exercises 128-bit multiply path) */ + printf("\nTesting large value rescale\n"); + printf("rescale(INT64_MAX/2, 2, 1) = %"PRId64"\n", + av_rescale_rnd(INT64_MAX / 2, 2, 1, AV_ROUND_ZERO)); + printf("rescale(1000000007, 1000000009, 1000000007) = %"PRId64"\n", + av_rescale(1000000007LL, 1000000009LL, 1000000007LL)); + /* b and c both > INT_MAX triggers 128-bit multiply */ + printf("rescale_rnd(10, INT_MAX+1, INT_MAX+1, ZERO) = %"PRId64"\n", + av_rescale_rnd(10, (int64_t)INT32_MAX + 1, (int64_t)INT32_MAX + 1, AV_ROUND_ZERO)); + printf("rescale_rnd(7, 3000000000, 2000000000, NEAR_INF) = %"PRId64"\n", + av_rescale_rnd(7, 3000000000LL, 2000000000LL, AV_ROUND_NEAR_INF)); + + /* av_rescale_q */ + printf("\nTesting av_rescale_q()\n"); + printf("rescale_q(90000, 1/90000, 1/1000) = %"PRId64"\n", + av_rescale_q(90000, (AVRational){1, 90000}, (AVRational){1, 1000})); + printf("rescale_q(48000, 1/48000, 1/44100) = %"PRId64"\n", + av_rescale_q(48000, (AVRational){1, 48000}, (AVRational){1, 44100})); + + /* av_compare_ts */ + printf("\nTesting av_compare_ts()\n"); + printf("compare(1, 1/1, 1, 1/1) = %d\n", + av_compare_ts(1, (AVRational){1, 1}, 1, (AVRational){1, 1})); + printf("compare(1, 1/1, 2, 1/1) = %d\n", + av_compare_ts(1, (AVRational){1, 1}, 2, (AVRational){1, 1})); + printf("compare(2, 1/1, 1, 1/1) = %d\n", + av_compare_ts(2, (AVRational){1, 1}, 1, (AVRational){1, 1})); + printf("compare(1, 1/1000, 1, 1/90000) = %d\n", + av_compare_ts(1, (AVRational){1, 1000}, 1, (AVRational){1, 90000})); + /* large values trigger rescale-based comparison path */ + printf("compare(INT64_MAX/2, 1/1, INT64_MAX/3, 1/1) = %d\n", + av_compare_ts(INT64_MAX / 2, (AVRational){1, 1}, + INT64_MAX / 3, (AVRational){1, 1})); + + /* av_compare_mod */ + printf("\nTesting av_compare_mod()\n"); + printf("compare_mod(3, 1, 16) = %"PRId64"\n", av_compare_mod(3, 1, 16)); + printf("compare_mod(1, 3, 16) = %"PRId64"\n", av_compare_mod(1, 3, 16)); + printf("compare_mod(5, 5, 16) = %"PRId64"\n", av_compare_mod(5, 5, 16)); + + /* av_rescale_delta */ + printf("\nTesting av_rescale_delta()\n"); + last = AV_NOPTS_VALUE; + for (int i = 0; i < 4; i++) + printf("delta step %d: %"PRId64"\n", i, + av_rescale_delta((AVRational){1, 48000}, i * 1024, + (AVRational){1, 48000}, 1024, + &last, (AVRational){1, 44100})); + /* trigger clip-based path: use different in_tb and fs_tb */ + last = AV_NOPTS_VALUE; + for (int i = 0; i < 4; i++) + printf("delta clip %d: %"PRId64"\n", i, + av_rescale_delta((AVRational){1, 44100}, i * 940, + (AVRational){1, 48000}, 1024, + &last, (AVRational){1, 90000})); + + /* av_add_stable */ + printf("\nTesting av_add_stable()\n"); + printf("add_stable(0, 1/1, 1/1000, 500) = %"PRId64"\n", + av_add_stable((AVRational){1, 1}, 0, (AVRational){1, 1000}, 500)); + printf("add_stable(1000, 1/90000, 1/48000, 1024) = %"PRId64"\n", + av_add_stable((AVRational){1, 90000}, 1000, (AVRational){1, 48000}, 1024)); + /* non-exact division path (m >= d, general case) */ + printf("add_stable(0, 1/48000, 1/90000, 90000) = %"PRId64"\n", + av_add_stable((AVRational){1, 48000}, 0, (AVRational){1, 90000}, 90000)); + printf("add_stable(100, 1/1000, 1/90000, 3000) = %"PRId64"\n", + av_add_stable((AVRational){1, 1000}, 100, (AVRational){1, 90000}, 3000)); + /* repeated addition: verify no rounding error accumulation */ + { + int64_t ts = 0; + for (int i = 0; i < 10000; i++) + ts = av_add_stable((AVRational){1, 48000}, ts, (AVRational){1, 48000}, 1024); + printf("add_stable 10000x1024 at 1/48000: %"PRId64" (expected %"PRId64") %s\n", + ts, (int64_t)10000 * 1024, + ts == (int64_t)10000 * 1024 ? "OK" : "FAIL"); + } + + return 0; +} diff --git a/tests/fate/libavutil.mak b/tests/fate/libavutil.mak index 0ee1d4d981..857bac7641 100644 --- a/tests/fate/libavutil.mak +++ b/tests/fate/libavutil.mak @@ -116,6 +116,10 @@ FATE_LIBAVUTIL += fate-lfg fate-lfg: libavutil/tests/lfg$(EXESUF) fate-lfg: CMD = run libavutil/tests/lfg$(EXESUF) +FATE_LIBAVUTIL += fate-mathematics +fate-mathematics: libavutil/tests/mathematics$(EXESUF) +fate-mathematics: CMD = run libavutil/tests/mathematics$(EXESUF) + FATE_LIBAVUTIL += fate-md5 fate-md5: libavutil/tests/md5$(EXESUF) fate-md5: CMD = run libavutil/tests/md5$(EXESUF) diff --git a/tests/ref/fate/mathematics b/tests/ref/fate/mathematics new file mode 100644 index 0000000000..4e7e72ffac --- /dev/null +++ b/tests/ref/fate/mathematics @@ -0,0 +1,73 @@ +Testing av_gcd() +gcd(0, 0) = 0 OK +gcd(1, 0) = 1 OK +gcd(0, 1) = 1 OK +gcd(6, 4) = 2 OK +gcd(12, 8) = 4 OK +gcd(17, 13) = 1 OK +gcd(100, 75) = 25 OK +gcd(-6, 4) = 2 OK +gcd(6, -4) = 2 OK + +Testing av_rescale() +rescale(6, 3, 2) = 9 +rescale(0, 3, 2) = 0 +rescale(1, 1, 1) = 1 +rescale(-6, 3, 2) = -9 +rescale(90000, 1, 90000) = 1 + +Testing av_rescale_rnd() +rescale_rnd(7, 1, 2, 0) = 3 OK +rescale_rnd(7, 1, 2, 1) = 4 OK +rescale_rnd(7, 1, 2, 2) = 3 OK +rescale_rnd(7, 1, 2, 3) = 4 OK +rescale_rnd(7, 1, 2, 5) = 4 OK +rescale_rnd(-7, 1, 2, 0) = -3 OK +rescale_rnd(-7, 1, 2, 1) = -4 OK +rescale_rnd(-7, 1, 2, 2) = -4 OK +rescale_rnd(-7, 1, 2, 3) = -3 OK +rescale_rnd(6, 1, 2, 5) = 3 OK + +Testing AV_ROUND_PASS_MINMAX +INT64_MIN passthrough: OK +INT64_MAX passthrough: OK +normal with PASS_MINMAX: 2 + +Testing large value rescale +rescale(INT64_MAX/2, 2, 1) = 9223372036854775806 +rescale(1000000007, 1000000009, 1000000007) = 1000000009 +rescale_rnd(10, INT_MAX+1, INT_MAX+1, ZERO) = 10 +rescale_rnd(7, 3000000000, 2000000000, NEAR_INF) = 11 + +Testing av_rescale_q() +rescale_q(90000, 1/90000, 1/1000) = 1000 +rescale_q(48000, 1/48000, 1/44100) = 44100 + +Testing av_compare_ts() +compare(1, 1/1, 1, 1/1) = 0 +compare(1, 1/1, 2, 1/1) = -1 +compare(2, 1/1, 1, 1/1) = 1 +compare(1, 1/1000, 1, 1/90000) = 1 +compare(INT64_MAX/2, 1/1, INT64_MAX/3, 1/1) = 1 + +Testing av_compare_mod() +compare_mod(3, 1, 16) = 2 +compare_mod(1, 3, 16) = -2 +compare_mod(5, 5, 16) = 0 + +Testing av_rescale_delta() +delta step 0: 0 +delta step 1: 941 +delta step 2: 1882 +delta step 3: 2822 +delta clip 0: 0 +delta clip 1: 1920 +delta clip 2: 3838 +delta clip 3: 5756 + +Testing av_add_stable() +add_stable(0, 1/1, 1/1000, 500) = 0 +add_stable(1000, 1/90000, 1/48000, 1024) = 2920 +add_stable(0, 1/48000, 1/90000, 90000) = 48000 +add_stable(100, 1/1000, 1/90000, 3000) = 133 +add_stable 10000x1024 at 1/48000: 10240000 (expected 10240000) OK