text: /*****************************************************************************
   * This file is part of uvg266 VVC encoder.
   *
   * Copyright (c) 2021, Tampere University, ITU/ISO/IEC, project contributors
   * All rights reserved.
   * 
   * Redistribution and use in source and binary forms, with or without modification,
   * are permitted provided that the following conditions are met:
   * 
   * * Redistributions of source code must retain the above copyright notice, this
   *   list of conditions and the following disclaimer.
   * 
   * * Redistributions in binary form must reproduce the above copyright notice, this
   *   list of conditions and the following disclaimer in the documentation and/or
   *   other materials provided with the distribution.
   * 
   * * Neither the name of the Tampere University or ITU/ISO/IEC nor the names of its
   *   contributors may be used to endorse or promote products derived from
   *   this software without specific prior written permission.
   * 
   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
   * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
   * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
   * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
   * INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
   * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION HOWEVER CAUSED AND ON
   * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   * INCLUDING NEGLIGENCE OR OTHERWISE ARISING IN ANY WAY OUT OF THE USE OF THIS
   ****************************************************************************/
  
  #include "greatest/greatest.h"
  
  #include "test_strategies.h"
  
  #include "src/image.h"
  
  #include <string.h>
  
  
  //////////////////////////////////////////////////////////////////////////
  // EXTERNAL FUNCTIONS
  
  //////////////////////////////////////////////////////////////////////////
  // DEFINES
  #define TEST_SAD(X, Y) uvg_image_calc_sad(g_pic, g_ref, 0, 0, (X), (Y), 8, 8, NULL)
  
  //////////////////////////////////////////////////////////////////////////
  // GLOBALS
  static const uvg_pixel ref_data[64] = {
    1,2,2,2,2,2,2,3,
    4,5,5,5,5,5,5,6,
    4,5,5,5,5,5,5,6,
    4,5,5,5,5,5,5,6,
    4,5,5,5,5,5,5,6,
    4,5,5,5,5,5,5,6,
    4,5,5,5,5,5,5,6,
    7,8,8,8,8,8,8,9
  };
  
  static const uvg_pixel pic_data[64] = {
    1,1,1,1,1,1,1,1,
    1,1,1,1,1,1,1,1,
    1,1,1,1,1,1,1,1,
    1,1,1,1,1,1,1,1,
    1,1,1,1,1,1,1,1,
    1,1,1,1,1,1,1,1,
    1,1,1,1,1,1,1,1,
    1,1,1,1,1,1,1,1
  };
  
  static uvg_picture *g_pic = 0;
  static uvg_picture *g_ref = 0;
  static uvg_picture *g_big_pic = 0;
  static uvg_picture *g_big_ref = 0;
  static uvg_picture *g_64x64_zero = 0;
  static uvg_picture *g_64x64_max = 0;
  
  static struct sad_test_env_t {
    int width;
    int height;
    void * tested_func;
    const strategy_t * strategy;
    char msg[255];
  } sad_test_env;
  
  //////////////////////////////////////////////////////////////////////////
  // SETUP, TEARDOWN AND HELPER FUNCTIONS
  static void setup_tests()
  {
    g_pic = uvg_image_alloc(UVG_CSP_420, 8, 8);
    for (int y = 0; y < 8; ++y) {
      for (int x = 0; x < 8; ++x) {
        g_pic->y[y*g_pic->stride + x] = pic_data[8*y + x] + 48;
      }
    }
  
    g_ref = uvg_image_alloc(UVG_CSP_420, 8, 8);
    for (int y = 0; y < 8; ++y) {
      for (int x = 0; x < 8; ++x) {
        g_ref->y[y*g_ref->stride + x] = ref_data[8*y + x] + 48;
      }
    }
  
    int i = 0;
    g_big_pic = uvg_image_alloc(UVG_CSP_420, 64, 64);
    for (int y = 0; y < 64; ++y) {
      for (int x = 0; x < 64; ++x) {
        i = ((64 * y) + x);
        g_big_pic->y[y*g_big_pic->stride + x] = (i*i / 32 + i) % 255;
      }
    }
  
    i = 0;
    g_big_ref = uvg_image_alloc(UVG_CSP_420, 64, 64);
    for (int y = 0; y < 64; ++y) {
      for (int x = 0; x < 64; ++x) {
        i = ((64 * y) + x);
        g_big_ref->y[y*g_big_ref->stride + x] = (i*i / 16 + i) % 255;
      }
    }
  
    g_64x64_zero = uvg_image_alloc(UVG_CSP_420, 64, 64);
    memset(g_64x64_zero->y, 0, 64 * 64 * sizeof(uvg_pixel));
    
    g_64x64_max = uvg_image_alloc(UVG_CSP_420, 64, 64);
    memset(g_64x64_max->y, PIXEL_MAX, 64 * 64 * sizeof(uvg_pixel));
  }
  
  static void tear_down_tests()
  {
    uvg_image_free(g_pic);
    uvg_image_free(g_ref);
    uvg_image_free(g_big_pic);
    uvg_image_free(g_big_ref);
    uvg_image_free(g_64x64_zero);
    uvg_image_free(g_64x64_max);
  }
  
  
  //////////////////////////////////////////////////////////////////////////
  // OVERLAPPING BOUNDARY TESTS
  TEST test_topleft(void)
  {
    ASSERT_EQ(
      1*(4*4) + (2+4)*(4*4) + 5*(4*4) - 64,
      TEST_SAD(-3, -3));
    PASS();
  }
  
  TEST test_top(void)
  {
    ASSERT_EQ(
      (1+3)*4 + 2*(6*4) + (4+6)*4 + 5*(6*4) - 64,
      TEST_SAD(0, -3));
    PASS();
  }
  
  TEST test_topright(void)
  {
    ASSERT_EQ(
      3*(4*4) + (2+6)*(4*4) + 5*(4*4) - 64,
      TEST_SAD(3, -3));
    PASS();
  }
  
  TEST test_left(void)
  {
    ASSERT_EQ(
      (1+7)*4 + 4*(6*4) + (2+8)*4 + 5*(6*4) - 64,
      TEST_SAD(-3, 0));
    PASS();
  }
  
  TEST test_no_offset(void)
  {
    ASSERT_EQ(
      (1+3+7+9) + (2+4+6+8)*6 + 5*(6*6) - 64,
      TEST_SAD(0, 0));
    PASS();
  }
  
  TEST test_right(void)
  {
    ASSERT_EQ(
      (3+9)*4 + 6*(4*6) + (2+8)*4 + 5*(6*4) - 64,
      TEST_SAD(3, 0));
    PASS();
  }
  
  TEST test_bottomleft(void)
  {
    ASSERT_EQ(
      7*(4*4) + (4+8)*(4*4) + 5*(4*4) - 64,
      TEST_SAD(-3, 3));
    PASS();
  }
  
  TEST test_bottom(void)
  {
    ASSERT_EQ(
      (7+9)*4 + 8*(6*4) + (4+6)*4 + 5*(6*4) - 64,
      TEST_SAD(0, 3));
    PASS();
  }
  
  TEST test_bottomright(void)
  {
    ASSERT_EQ(
      9*(4*4) + (6+8)*(4*4) + 5*(4*4) - 64,
      TEST_SAD(3, 3));
    PASS();
  }
  
  //////////////////////////////////////////////////////////////////////////
  // OUT OF FRAME TESTS
  
  #define DIST 10
  TEST test_topleft_out(void)
  {
    ASSERT_EQ(
      1*(8*8) - 64,
      TEST_SAD(-DIST, -DIST));
    PASS();
  }
  
  TEST test_top_out(void)
  {
    ASSERT_EQ(
      (1+3)*8 + 2*(6*8) - 64,
      TEST_SAD(0, -DIST));
    PASS();
  }
  
  TEST test_topright_out(void)
  {
    ASSERT_EQ(
      3*(8*8) - 64,
      TEST_SAD(DIST, -DIST));
    PASS();
  }
  
  TEST test_left_out(void)
  {
    ASSERT_EQ(
      (1+7)*8 + 4*(6*8) - 64,
      TEST_SAD(-DIST, 0));
    PASS();
  }
  
  TEST test_right_out(void)
  {
    ASSERT_EQ(
      (3+9)*8 + 6*(6*8) - 64,
      TEST_SAD(DIST, 0));
    PASS();
  }
  
  TEST test_bottomleft_out(void)
  {
    ASSERT_EQ(
      7*(8*8) - 64,
      TEST_SAD(-DIST, DIST));
    PASS();
  }
  
  TEST test_bottom_out(void)
  {
    ASSERT_EQ(
      (7+9)*8 + 8*(6*8) - 64,
      TEST_SAD(0, DIST));
    PASS();
  }
  
  TEST test_bottomright_out(void)
  {
    ASSERT_EQ(
      9*(8*8) - 64,
      TEST_SAD(DIST, DIST));
    PASS();
  }
  
  static unsigned simple_sad(const uvg_pixel* buf1, const uvg_pixel* buf2, unsigned stride,
                             unsigned width, unsigned height)
  {
    unsigned sum = 0;
    for (unsigned y = 0; y < height; ++y) {
      for (unsigned x = 0; x < width; ++x) {
        sum += abs((int)buf1[y * stride + x] - (int)buf2[y * stride + x]);
      }
    }
    return sum;
  }
  
  TEST test_reg_sad(void)
  {
    unsigned width = sad_test_env.width;
    unsigned height = sad_test_env.height;
    unsigned stride = 64;
  
    unsigned correct_result = simple_sad(g_big_pic->y, g_big_ref->y, stride, width, height);
    
    unsigned(*tested_func)(const uvg_pixel *, const uvg_pixel *, int, int, unsigned, unsigned) = sad_test_env.tested_func;
    unsigned result = tested_func(g_big_pic->y, g_big_ref->y, width, height, stride, stride);
    
    sprintf(sad_test_env.msg, "%s(%ux%u):%s",
            sad_test_env.strategy->type,
            width,
            height,
            sad_test_env.strategy->strategy_name);
  
    if (result != correct_result) {
      FAILm(sad_test_env.msg);
    }
  
    PASSm(sad_test_env.msg);
  }
  
  
  TEST test_reg_sad_overflow(void)
  {
    unsigned width = sad_test_env.width;
    unsigned height = sad_test_env.height;
    unsigned stride = 64;
  
    unsigned correct_result = simple_sad(g_64x64_zero->y, g_64x64_max->y, stride, width, height);
  
    unsigned(*tested_func)(const uvg_pixel *, const uvg_pixel *, int, int, unsigned, unsigned) = sad_test_env.tested_func;
    unsigned result = tested_func(g_64x64_zero->y, g_64x64_max->y, width, height, stride, stride);
  
    sprintf(sad_test_env.msg, "overflow %s(%ux%u):%s",
      sad_test_env.strategy->type,
      width,
      height,
      sad_test_env.strategy->strategy_name);
  
    if (result != correct_result) {
      FAILm(sad_test_env.msg);
    }
  
    PASSm(sad_test_env.msg);
  }
  
  
  //////////////////////////////////////////////////////////////////////////
  // TEST FIXTURES
  SUITE(sad_tests)
  {
    //SET_SETUP(sad_setup);
    //SET_TEARDOWN(sad_teardown);
  
    setup_tests();
  
    for (volatile unsigned i = 0; i < strategies.count; ++i) {
      if (strcmp(strategies.strategies[i].type, "reg_sad") != 0) {
        continue;
      }
  
      // Change the global reg_sad function pointer.
      uvg_reg_sad = strategies.strategies[i].fptr;
  
      // Tests for movement vectors that overlap frame.
      RUN_TEST(test_topleft);
      RUN_TEST(test_top);
      RUN_TEST(test_topright);
  
      RUN_TEST(test_left);
      RUN_TEST(test_no_offset);
      RUN_TEST(test_right);
  
      RUN_TEST(test_bottomleft);
      RUN_TEST(test_bottom);
      RUN_TEST(test_bottomright);
  
      // Tests for movement vectors that are outside the frame.
      RUN_TEST(test_topleft_out);
      RUN_TEST(test_top_out);
      RUN_TEST(test_topright_out);
  
      RUN_TEST(test_left_out);
      RUN_TEST(test_right_out);
  
      RUN_TEST(test_bottomleft_out);
      RUN_TEST(test_bottom_out);
      RUN_TEST(test_bottomright_out);
  
      struct dimension {
        int width;
        int height;
      };
      static const struct dimension tested_dims[] = {
        // Square motion partitions
        {64, 64}, {32, 32}, {16, 16}, {8, 8},
        // Symmetric motion partitions
        {64, 32}, {32, 64}, {32, 16}, {16, 32}, {16, 8}, {8, 16}, {8, 4}, {4, 8},
        // Asymmetric motion partitions
        {48, 16}, {16, 48}, {24, 16}, {16, 24}, {12, 4}, {4, 12}
      };
  
      sad_test_env.tested_func = strategies.strategies[i].fptr;
      sad_test_env.strategy = &strategies.strategies[i];
      int num_dim_tests = sizeof(tested_dims) / sizeof(tested_dims[0]);
      for (volatile int dim_test = 0; dim_test < num_dim_tests; ++dim_test) {
        sad_test_env.width = tested_dims[dim_test].width;
        sad_test_env.height = tested_dims[dim_test].height;
        RUN_TEST(test_reg_sad);
        RUN_TEST(test_reg_sad_overflow);
      }
    }
    
    tear_down_tests();
  }
  