+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <math.h>
+#include <assert.h>
+#include <time.h>
+#include <sys/time.h>
+
+#include "idct.h"
+
+// Generate random coefficients in the range [-15..15].
+void gen_random_coeffs(int16_t* dst, size_t len)
+{
+ // Standard NR LCG (we avoid rand() to get consistent behavior across platforms).
+ static uint32_t seed = 1234;
+ for (unsigned i = 0; i < len; ++i) {
+ seed = seed * 1664525U + 1013904223U;
+ if (seed >> 31) {
+ dst[i] = (uint8_t)(seed >> 27) & 0x7;
+ } else {
+ dst[i] = -((uint8_t)(seed >> 27) & 0x7);
+ }
+ }
+}
+
+// Test that the input is pretty close to the reference for random inputs.
+// (If the reference funtion is given in, this becomes a simple test of its
+// determinism.)
+void test_random_inputs(idct_func_t* idct)
+{
+ int16_t coeff[DCTSIZE2];
+ uint32_t quant[DCTSIZE2];
+ uint8_t output[DCTSIZE2];
+ uint8_t reference[DCTSIZE2];
+
+ // Unit quantization (ie., no scaling).
+ for (unsigned i = 0; i < DCTSIZE2; ++i) {
+ quant[i] = 1;
+ }
+
+ for (unsigned i = 0; i < 1000; ++i) {
+ gen_random_coeffs(coeff, DCTSIZE2);
+
+ (*idct)(coeff, quant, output);
+ (idct_reference)(coeff, quant, reference);
+
+ // Find the RMS difference.
+ int diff_squared = 0;
+ for (unsigned i = 0; i < DCTSIZE2; ++i) {
+ diff_squared += (output[i] - reference[i]) * (output[i] - reference[i]);
+ }
+
+ assert(diff_squared <= 5);
+ }
+}
+
+// Test that a single DC coefficient becomes spread out to all blocks.
+void test_dc_becomes_spread_out(idct_func_t* idct)
+{
+ int16_t coeff[DCTSIZE2] = { 0 };
+ uint32_t quant[DCTSIZE2];
+ uint8_t output[DCTSIZE2];
+
+ // Unit quantization (ie., no scaling).
+ for (unsigned i = 0; i < DCTSIZE2; ++i) {
+ quant[i] = 1;
+ }
+
+ for (unsigned i = 0; i < 255*8; ++i) {
+ uint32_t reference_value = i / 8;
+ coeff[0] = i;
+
+ (*idct)(coeff, quant, output);
+
+ for (unsigned i = 0; i < DCTSIZE2; ++i) {
+ assert(abs(output[i] - reference_value) <= 1);
+ }
+ }
+}
+
+double timediff(const struct timeval* a, const struct timeval* b)
+{
+ return (double)(b->tv_sec - a->tv_sec) +
+ (double)(b->tv_usec - a->tv_usec) * 1e-6;
+}
+
+void test_performance(idct_func_t* idct)
+{
+ const unsigned num_runs = (idct == idct_reference) ? 5000 : 5000000;
+
+ int16_t coeff[DCTSIZE2];
+ uint32_t quant[DCTSIZE2];
+ uint8_t output[DCTSIZE2];
+
+ gen_random_coeffs(coeff, DCTSIZE2);
+
+ struct timeval start, now;
+ gettimeofday(&start, NULL);
+
+ for (unsigned i = 0; i < num_runs; ++i) {
+ (*idct)(coeff, quant, output);
+ }
+
+ gettimeofday(&now, NULL);
+
+ double diff = timediff(&start, &now);
+ printf("%u runs in %.2f seconds = %.2f DCTs/sec\n",
+ num_runs, diff, num_runs / diff);
+}
+
+void test_all_idct(idct_func_t* idct)
+{
+ printf(" test_dc_becomes_spread_out()\n");
+ test_dc_becomes_spread_out(idct);
+
+ printf(" test_random_inputs()\n");
+ test_random_inputs(idct);
+
+ printf(" performance test: ");
+ test_performance(idct);
+}
+
+int main(void)
+{
+ printf("idct_reference:\n");
+ test_all_idct(idct_reference);
+
+ printf("All tests pass.\n");
+ return 0;
+}