/* -----------------------------------------------------------------------------
The copyright in this software is being made available under the Clear BSD
License, included below. No patent rights, trademark rights and/or
other Intellectual Property Rights other than the copyrights concerning
the Software are granted under this license.

The Clear BSD License

Copyright (c) 2024-2026, Fraunhofer-Gesellschaft zur Förderung der angewandten Forschung e.V. & The VVenC Authors.
All rights reserved.

Redistribution and use in source and binary forms, with or without modification,
are permitted (subject to the limitations in the disclaimer below) 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 copyright holder nor the names of its
     contributors may be used to endorse or promote products derived from this
     software without specific prior written permission.

NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY
THIS LICENSE. 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 (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.


------------------------------------------------------------------------------------------- */

/**
  \ingroup vvenclibtest
  \file    vvenclibtest.cpp
  \brief   This vvenclibtest.cpp file contains the main entry point of the test application.
*/

#include <cmath>
#include <iostream>
#include <limits.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <string>
#include <time.h>

#include "CommonLib/AdaptiveLoopFilter.h"
#include "CommonLib/AffineGradientSearch.h"
#include "CommonLib/InterPrediction.h"
#include "CommonLib/IntraPrediction.h"
#include "CommonLib/MCTF.h"
#include "CommonLib/RdCost.h"
#include "CommonLib/TrQuant_EMT.h"
#include "CommonLib/TypeDef.h"
#include "CommonLib/Unit.h"

#include "apputils/ParseArg.h"

using namespace vvenc;
namespace po = apputils::program_options;

#define NUM_CASES 100

static bool g_fastUnitTest = false;

template<typename T>
static inline bool compare_value( const std::string& context, const T ref, const T opt )
{
  if( opt != ref )
  {
    std::cerr << "failed: " << context << "\n"
              << "  mismatch:  ref=" << ref << "  opt=" << opt << "\n";
  }
  return opt == ref;
}

template<typename T>
static inline bool compare_values_1d( const std::string& context, const T* ref, const T* opt, unsigned length )
{
  for( unsigned idx = 0; idx < length; ++idx )
  {
    if( ref[idx] != opt[idx] )
    {
      std::cout << "failed: " << context << "\n"
                << "  mismatch:  ref[" << idx << "]=" << ref[idx] << "  opt[" << idx << "]=" << opt[idx] << "\n";
      return false;
    }
  }
  return true;
}

template<typename T, typename U = T>
static inline bool compare_values_2d( const std::string& context, const T* ref, const T* opt, unsigned rows,
                                      unsigned cols, unsigned stride = 0, U tolerance = U( 0 ) )
{
  stride = stride != 0 ? stride : cols;

  auto abs_diff = []( T value1, T value2 ) -> U { return static_cast<U>( std::abs( value1 - value2 ) ); };

  for( unsigned row = 0; row < rows; ++row )
  {
    for( unsigned col = 0; col < cols; ++col )
    {
      unsigned idx = row * stride + col;
      if( abs_diff( ref[idx], opt[idx] ) > tolerance )
      {
        std::cout << "failed: " << context << "\n"
                  << "  mismatch:  ref[" << row << "*" << stride << "+" << col << "]=" << ref[idx]
                  << "  opt[" << row << "*" << stride << "+" << col << "]=" << opt[idx] << "\n";
        return false;
      }
    }
  }
  return true;
}

template<typename T>
class MinMaxGenerator
{
public:
  explicit MinMaxGenerator( unsigned bits, bool is_signed = true )
  {
    if( is_signed )
    {
      T half = 1 << ( bits - 1 );
      m_min = -half;
      m_max =  half - 1;
    }
    else
    {
      m_min = 0;
      m_max = ( 1 << bits ) - 1;
    }
  }

  T operator()() const
  {
    return ( rand() & 1 ) ? m_max : m_min;
  }

  std::string input_type() const
  {
    return "MinOrMax";
  }

private:
  T m_min;
  T m_max;
};

template<typename T>
class InputGenerator
{
public:
  explicit InputGenerator( unsigned bits, bool is_signed = true ) : m_bits( bits ), m_signed( is_signed )
  {
  }

  T operator()() const
  {
    if( !m_signed )
    {
      return static_cast<T>( rand() & ( ( 1 << m_bits ) - 1 ) );
    }
    else
    {
      return ( rand() & ( ( 1 << m_bits ) - 1 ) ) - ( 1 << m_bits >> 1 );
    }
  }

  std::string input_type() const
  {
    return "Rand";
  }

private:
  unsigned m_bits;
  bool m_signed;
};

template<typename T>
class TrafoGenerator
{
public:
  explicit TrafoGenerator( unsigned bits ) : m_bits( bits )
  {
  }

  T operator()() const
  {
    return ( rand() & ( ( 1 << m_bits ) - 1 ) ) - ( 1 << m_bits >> 1 );
  }

private:
  unsigned m_bits;
};

class DimensionGenerator
{
public:
  unsigned get( unsigned min, unsigned max, unsigned mod = 1 ) const
  {
    CHECK( max < min, "max should be >= min" );
    CHECK( mod == 0, "mod must be >= 1" );                   // Avoids div by 0.
    CHECK( min == 0 && max == UINT_MAX, "range too large" ); // Avoids (max-min+1)==0.
    unsigned ret = rand() % ( max - min + 1 ) + min;
    ret -= ret % mod;
    return ret;
  }

  template<typename T>
  T getOneOf( const std::vector<T>& values ) const
  {
    CHECK( values.empty(), "getOneOf: values vector must not be empty" );
    return values[rand() % values.size()];
  }
};

#if ENABLE_SIMD_OPT_INTRAPRED
static bool check_IntraPredAngleLuma( IntraPrediction* ref, IntraPrediction* opt, unsigned num_cases )
{
  static constexpr unsigned int bd = 10; // default bit-depth = 10
  ClpRng clpRng{ bd };
  DimensionGenerator dim;
  InputGenerator<Pel> ref_gen{ bd, /*is_signed=*/false }; // unsigned 10-bit

  ptrdiff_t dstStride = MAX_CU_SIZE;
  static constexpr size_t refMain_size = 2 * MAX_CU_SIZE + 3 + 33 * MAX_REF_LINE_IDX;
  static constexpr size_t dstBuf_size = MAX_CU_SIZE * MAX_CU_SIZE;
  std::vector<Pel> refMain( refMain_size );
  std::vector<Pel> dstBuf_ref( dstBuf_size );
  std::vector<Pel> dstBuf_opt( dstBuf_size );

  bool passed = true;

  for( bool useCubic : { true, false } )
  {
    std::ostringstream sstm_test;
    sstm_test << "IntraPrediction::IntraPredAngleLuma" << " useCubic=" << std::boolalpha << useCubic;
    std::cout << "Testing " << sstm_test.str() << '\n';

    for( unsigned n = 0; n < num_cases; n++ )
    {
      int log2width = dim.get( 2, 6 );
      int log2height = dim.get( 2, 6 );
      int width = 1 << log2width;   // min: 4, max: 64
      int height = 1 << log2height; // min: 4, max: 64
      int deltaPos = dim.get( 16, 128 );
      int intraPredAngle = deltaPos;

      std::generate( refMain.begin(), refMain.end(), ref_gen );
      std::fill( dstBuf_ref.begin(), dstBuf_ref.end(), Pel{} );
      std::fill( dstBuf_opt.begin(), dstBuf_opt.end(), Pel{} );

      ref->IntraPredAngleLuma( dstBuf_ref.data(), dstStride, refMain.data(), width, height, deltaPos, intraPredAngle,
                               nullptr, useCubic, clpRng );
      opt->IntraPredAngleLuma( dstBuf_opt.data(), dstStride, refMain.data(), width, height, deltaPos, intraPredAngle,
                               nullptr, useCubic, clpRng );

      std::ostringstream sstm_subtest;
      sstm_subtest << sstm_test.str() << " width=" << width << " height=" << height << " deltaPos=" << deltaPos
                   << " intraPredAngle=" << intraPredAngle;

      passed = compare_values_1d( sstm_subtest.str(), dstBuf_ref.data(), dstBuf_opt.data(), dstBuf_size ) && passed;
    }
  }

  return passed;
}

static bool test_IntraPred()
{
  IntraPrediction ref{ /*enableOpt=*/false };
  IntraPrediction opt{ /*enableOpt=*/true };

  unsigned num_cases = NUM_CASES;
  bool passed        = true;

  passed = check_IntraPredAngleLuma( &ref, &opt, num_cases ) && passed;

  return passed;
}
#endif // ENABLE_SIMD_OPT_INTRAPRED

#if ENABLE_SIMD_TRAFO
static bool check_cpyCoeff( TCoeffOps* ref, TCoeffOps* opt, unsigned num_cases )
{
  DimensionGenerator dim;

  static constexpr size_t buf_size = MAX_CU_SIZE * MAX_CU_SIZE;

  // Use xMalloc to create aligned buffers for x86.
  Pel    *src     = ( Pel*    )xMalloc( Pel,    buf_size );
  TCoeff *dst_ref = ( TCoeff* )xMalloc( TCoeff, buf_size );
  TCoeff *dst_opt = ( TCoeff* )xMalloc( TCoeff, buf_size );

  bool passed = true;

  // This function copies the prediction residue, which is signed 11-bit.
  InputGenerator<Pel> inp_gen{ 11, /*is_signed=*/true };

  std::ostringstream sstm_test;
  sstm_test << "TCoeffOps::cpyCoeff";
  std::cout << "Testing " << sstm_test.str() << std::endl;

  for( unsigned n = 0; n < num_cases; n++ )
  {
    // Set height and width to powers of two: 4 to 128 (MAX_CU_SIZE).
    unsigned log2height = dim.get( 2, 7 );
    unsigned log2width = dim.get( 2, 7 );
    unsigned height = 1 << log2height;
    unsigned width = 1 << log2width;
    ptrdiff_t srcStride = dim.get( width, MAX_CU_SIZE );

    // Fill input buffers with signed data.
    std::generate( src, src + buf_size, inp_gen );

    // Clear output blocks.
    memset( dst_ref, 0, buf_size * sizeof( TCoeff ) );
    memset( dst_opt, 0, buf_size * sizeof( TCoeff ) );

    std::ostringstream sstm_subtest;

    if( width % 8 == 0 )
    {
      ref->cpyCoeff8( src, srcStride, dst_ref, width, height );
      opt->cpyCoeff8( src, srcStride, dst_opt, width, height );

      sstm_subtest << sstm_test.str() << "8";
    }
    else if( width % 4 == 0 )
    {
      ref->cpyCoeff4( src, srcStride, dst_ref, width, height );
      opt->cpyCoeff4( src, srcStride, dst_opt, width, height );

      sstm_subtest << sstm_test.str() << "4";
    }
    else // Shouldn't come here.
    {
      THROW( "Unsupported size" );
    }

    sstm_subtest << " srcStride=" << srcStride << " w=" << width << " h=" << height;

    passed = compare_values_2d( sstm_subtest.str(), dst_ref, dst_opt, height, width ) && passed;
  }

  xFree( src );
  xFree( dst_ref );
  xFree( dst_opt );

  return passed;
}

template<int W>
static bool check_roundClip( TCoeffOps* ref, TCoeffOps* opt, unsigned num_cases, unsigned width, unsigned height )
{
  static_assert( W == 4 || W == 8, "W must be either 4 or 8" );

  static constexpr size_t buf_size = MAX_CU_SIZE * MAX_CU_SIZE;

  // Use xMalloc to create aligned buffers for x86.
  TCoeff *dst_ref = ( TCoeff* )xMalloc( TCoeff, buf_size );
  TCoeff *dst_opt = ( TCoeff* )xMalloc( TCoeff, buf_size );

  bool passed = true;

  // Use input values up to signed 22-bit based from a real encode.
  static constexpr unsigned input_bd = 22;
  InputGenerator<TCoeff> inp_gen{ input_bd, /*is_signed=*/true };

  // Stride is same as width.
  unsigned stride = width;

  // Set min/max clip to signed 16-bit.
  const TCoeff outputMin = INT16_MIN;
  const TCoeff outputMax = INT16_MAX;

  std::ostringstream sstm_test;
  sstm_test << "TCoeffOps::roundClip" << W << " w=" << width << " h=" << height << " stride=" << stride;
  std::cout << "Testing " << sstm_test.str() << std::endl;

  for( unsigned output_bd : { 8, 10 } )
  {
    const TCoeff shift = input_bd - ( output_bd + 1 );
    const TCoeff round = 1 << ( shift - 1 );

    std::ostringstream sstm_subtest;
    sstm_subtest << sstm_test.str() << " round=" << round << " shift=" << shift << " bitDepth=" << output_bd;

    for( unsigned n = 0; n < num_cases; n++ )
    {
      // Fill input buffers with signed data.
      std::generate( dst_ref, dst_ref + buf_size, inp_gen );
      std::memcpy( dst_opt, dst_ref, buf_size * sizeof( TCoeff ) );

      if( W == 8 )
      {
        ref->roundClip8( dst_ref, width, height, stride, outputMin, outputMax, round, shift );
        opt->roundClip8( dst_opt, width, height, stride, outputMin, outputMax, round, shift );
      }
      else // W == 4
      {
        ref->roundClip4( dst_ref, width, height, stride, outputMin, outputMax, round, shift );
        opt->roundClip4( dst_opt, width, height, stride, outputMin, outputMax, round, shift );
      }

      passed = compare_values_2d( sstm_subtest.str(), dst_ref, dst_opt, height, stride ) && passed;
    }
  }

  xFree( dst_ref );
  xFree( dst_opt );

  return passed;
}

template<typename G, typename T>
static bool check_one_fastInvCore( TCoeffOps* ref, TCoeffOps* opt, unsigned idx, unsigned trSize, unsigned lines,
                                   unsigned reducedLines, unsigned cutoff, G input_generator, T trafo_generator )
{
  CHECK( lines == 0, "Lines must be non-zero" );
  CHECK( reducedLines > lines, "ReducedLines must be less than or equal to lines" );
  CHECK( cutoff == 0, "Cutoff must be non-zero" );
  CHECK( cutoff % 4, "Cutoff must be a multiple of four" );
  CHECK( cutoff > trSize, "Cutoff must not be larger than transformation size" );

  std::ostringstream sstm;
  sstm << "fastInvCore trSize=" << trSize << " lines=" << lines << " reducedLines=" << reducedLines << " cutoff=" << cutoff;

  TMatrixCoeff *it   = ( TMatrixCoeff* ) xMalloc( TMatrixCoeff, trSize * trSize );
  TCoeff       *src  = ( TCoeff* )       xMalloc( TCoeff,       trSize * lines );
  TCoeff       *dst0 = ( TCoeff* )       xMalloc( TCoeff,       trSize * lines );
  TCoeff       *dst1 = ( TCoeff* )       xMalloc( TCoeff,       trSize * lines );

  // Initialize source buffers.
  std::generate( it, it + trSize * trSize, trafo_generator );
  std::generate( src, src + trSize * lines, input_generator );
  memset( dst0, 0, trSize * lines * sizeof( TCoeff ) );
  memset( dst1, 0, trSize * lines * sizeof( TCoeff ) );

  ref->fastInvCore[ idx ]( it, src, dst0, lines, reducedLines, cutoff );
  opt->fastInvCore[ idx ]( it, src, dst1, lines, reducedLines, cutoff );

  auto ret = compare_values_2d( sstm.str(), dst0, dst1, trSize, lines );

  xFree( it );
  xFree( src );
  xFree( dst0 );
  xFree( dst1 );

  return ret;
}

template<typename G, typename T>
static bool check_one_fastFwdCore_2D( TCoeffOps* ref, TCoeffOps* opt, unsigned idx, unsigned trSize, unsigned line,
                                      unsigned reducedLine, unsigned cutoff, unsigned shift, G input_generator, T trafo_generator )
{
  CHECK( line == 0, "Line must be non-zero" );
  CHECK( reducedLine > line, "ReducedLine must be less than or equal to line" );
  CHECK( cutoff == 0, "Cutoff must be non-zero" );
  CHECK( cutoff % 4, "Cutoff must be a multiple of four" );
  CHECK( cutoff > trSize, "Cutoff must not be larger than transformation size" );
  CHECK( shift == 0, "Shift must be at least one" );

  std::ostringstream sstm;
  sstm << "fastFwdCore_2D trSize=" << trSize << " line=" << line << " reducedLine=" << reducedLine
       << " cutoff=" << cutoff << " shift=" << shift;

  TMatrixCoeff *tc   = ( TMatrixCoeff* ) xMalloc( TMatrixCoeff, trSize * trSize );
  TCoeff       *src  = ( TCoeff* )       xMalloc( TCoeff,       trSize * line );
  TCoeff       *dst0 = ( TCoeff* )       xMalloc( TCoeff,       trSize * line );
  TCoeff       *dst1 = ( TCoeff* )       xMalloc( TCoeff,       trSize * line );

  // Initialize source and destination buffers, make sure that destination
  // buffers match in elements that are not written to by the kernel being
  // tested.
  std::generate( tc,  tc + trSize * trSize, trafo_generator );
  std::generate( src, src + trSize * line,  input_generator );
  memset( dst0, 0, trSize * line * sizeof( TCoeff ) );
  memset( dst1, 0, trSize * line * sizeof( TCoeff ) );

  ref->fastFwdCore_2D[ idx ]( tc, src, dst0, line, reducedLine, cutoff, shift );
  opt->fastFwdCore_2D[ idx ]( tc, src, dst1, line, reducedLine, cutoff, shift );

  // Don't check for over-writes past reducedLine columns here, since the
  // existing x86 implementations would fail.
  auto ret = compare_values_2d( sstm.str(), dst0, dst1, trSize, reducedLine, line );

  xFree( tc );
  xFree( src );
  xFree( dst0 );
  xFree( dst1 );

  return ret;
}

static bool check_fastInvCore( TCoeffOps* ref, TCoeffOps* opt, unsigned num_cases, unsigned idx, unsigned trSize )
{
  printf( "Testing TCoeffOps::fastInvCore trSize=%d\n", trSize );
  InputGenerator<TCoeff> g{ 16 };
  TrafoGenerator<TMatrixCoeff> t{ 8 };
  DimensionGenerator rng;

  for( unsigned i = 0; i < num_cases; ++i )
  {
    // Clamp lines down to the next multiple of four when generating
    // reducedLines to avoid existing x86 implementations over-writing.
    unsigned lines        = 1 << rng.get( 1, 6 );
    unsigned reducedLines = lines == 2 ? lines : std::min( 32u, rng.get( 4, lines, 4 ) );
    unsigned cutoff       = rng.get( 4, trSize, 4 );  // Cutoff must be a non-zero multiple of four.
    if( !check_one_fastInvCore( ref, opt, idx, trSize, lines, reducedLines, cutoff, g, t ) )
    {
      return false;
    }
  }

  return true;
}

static bool check_fastFwdCore_2D( TCoeffOps* ref, TCoeffOps* opt, unsigned num_cases, unsigned idx, unsigned trSize )
{
  printf( "Testing TCoeffOps::fastFwdCore_2D trSize=%d\n", trSize );
  InputGenerator<TCoeff> g{ 11 }; // signed 10 bit in both positive/negative, i.e. 11 bit shifted to signed
  TrafoGenerator<TMatrixCoeff> t{ 8 };
  DimensionGenerator rng;

  for( unsigned i = 0; i < num_cases; ++i )
  {
    // Clamp line down to the next multiple of four when generating reducedLine
    // to avoid existing x86 implementations over-writing.
    unsigned line        = 1 << rng.get( 1, 6 );
    unsigned reducedLine = line == 2 ? 2 : std::min( 32u, rng.get( 4, line, 4 ) );
    unsigned cutoff      = rng.get( 4, trSize, 4 );  // Cutoff must be a non-zero multiple of four.
    unsigned shift       = rng.get( 1, 16 );          // Shift must be at least one to avoid UB.
    if( !check_one_fastFwdCore_2D( ref, opt, idx, trSize, line, reducedLine, cutoff, shift, g, t ) )
    {
      return false;
    }
  }

  return true;
}

static bool test_TCoeffOps()
{
  TCoeffOps ref;
  TCoeffOps opt;
#if defined( TARGET_SIMD_X86 )
  opt.initTCoeffOpsX86();
#endif
#if defined( TARGET_SIMD_ARM )
  opt.initTCoeffOpsARM();
#endif

  unsigned num_cases = NUM_CASES;
  bool passed        = true;

  passed = check_cpyCoeff( &ref, &opt, num_cases ) && passed;

  for( unsigned h : { 1, 2, 4, 8, 16, 32, 64 } )
  {
    passed = check_roundClip<4>( &ref, &opt, num_cases, 4, h ) && passed;

    for( unsigned w : { 8, 16, 32, 64 } )
    {
      passed = check_roundClip<8>( &ref, &opt, num_cases, w, h ) && passed;
    }
  }

  passed = check_fastInvCore( &ref, &opt, num_cases, 0, 4 ) && passed;
  passed = check_fastInvCore( &ref, &opt, num_cases, 1, 8 ) && passed;
  passed = check_fastInvCore( &ref, &opt, num_cases, 2, 16 ) && passed;
  passed = check_fastInvCore( &ref, &opt, num_cases, 3, 32 ) && passed;
  passed = check_fastInvCore( &ref, &opt, num_cases, 4, 64 ) && passed;

  passed = check_fastFwdCore_2D( &ref, &opt, num_cases, 0, 4 ) && passed;
  passed = check_fastFwdCore_2D( &ref, &opt, num_cases, 1, 8 ) && passed;
  passed = check_fastFwdCore_2D( &ref, &opt, num_cases, 2, 16 ) && passed;
  passed = check_fastFwdCore_2D( &ref, &opt, num_cases, 3, 32 ) && passed;
  passed = check_fastFwdCore_2D( &ref, &opt, num_cases, 4, 64 ) && passed;
  return passed;
}
#endif // ENABLE_SIMD_TRAFO

#if ENABLE_SIMD_OPT_MCTF

#define VVENC_MCTF_RANGE 6
template<typename G>
static bool check_one_applyBlock( MCTF* ref, MCTF* opt, unsigned srcStride, unsigned dstStride, int w, int h,
                                  unsigned bitDepth, int numRefs, G inputGenCorrectedPics )
{
  CHECK( srcStride < w, "OrgStride must be greater than or equal to width" );
  CHECK( dstStride < w, "BufStride must be greater than or equal to width" );

  std::ostringstream sstm;
  sstm << "applyBlock srcStride=" << srcStride << " dstStride=" << dstStride << " w=" << w << " h=" << h;

  InputGenerator<int> gBD{ bitDepth, /*is_signed=*/false };
  std::vector<int> verror( 2 * VVENC_MCTF_RANGE ); // BD unsigned
  std::generate( verror.begin(), verror.end(), gBD );

  const double refStrengths[2][VVENC_MCTF_RANGE] = {
      // abs(POC offset)
      // 1       2       3       4       5       6
      { 0.84375, 0.6, 0.4286, 0.3333, 0.2727, 0.2308 }, // RA
      { 1.12500, 1.0, 0.7143, 0.5556, 0.4545, 0.3846 }  // LD
  };
  std::vector<double> refStr( 2 * VVENC_MCTF_RANGE );
  std::copy( &refStrengths[0][0], &refStrengths[0][0] + 2 * VVENC_MCTF_RANGE, refStr.begin() );

  DimensionGenerator dg;
  ChromaFormat chromaFormat = dg.get( 0, 1 ) ? VVENC_CHROMA_400 : VVENC_CHROMA_420;
  ComponentID compID = ( ComponentID )dg.get( 0, 2 ); // 0 to 2

  const CompArea blk( compID, chromaFormat, Area( 0, 0, w, h ) );
  const ClpRng clpRng{ (int)bitDepth };
  const std::array<double, 3> overallStrength = { 0.5, 0.666667, 1.5 }; // Values taken from a real encoding.
  const double weightScaling = overallStrength[dg.get( 0, 2 )] * ( isChroma( compID ) ? 0.55 : 0.4 );
  const std::array<double, 3> sigmaSqVal = { 900.0, 2275.031250, 4608.0 }; // Values taken from a real encoding.
  double sigmaSq = sigmaSqVal[dg.get( 0, 2 )];

  std::vector<const Pel*> correctedPics( 2 * VVENC_MCTF_RANGE );
  std::vector<Pel> correctedPicsBuf( numRefs * w * h + w ); // allow one line overread
  std::generate( correctedPicsBuf.begin(), correctedPicsBuf.end(), inputGenCorrectedPics );
  for( int i = 0; i < numRefs; i++ )
  {
    correctedPics[i] = correctedPicsBuf.data() + i * w * h;
  }

  std::vector<Pel> src_buf( srcStride * ( h + 2 ) );
  std::generate( src_buf.begin(), src_buf.end(), gBD );

  std::vector<Pel> dst_buf_ref( dstStride * ( h + 2 ) );
  std::vector<Pel> dst_buf_opt( dstStride * ( h + 2 ) );
  
  CPelBuf src;
  PelBuf dst_ref, dst_opt;

  // source data is always padded allowing for overreads - we just offset add one line below and above
  src.buf = src_buf.data()         + srcStride;
  dst_ref.buf = dst_buf_ref.data() + dstStride;
  dst_opt.buf = dst_buf_opt.data() + dstStride;
  src.stride = srcStride;
  dst_ref.stride = dstStride;
  dst_opt.stride = dstStride;

  ref->m_applyBlock( src, dst_ref, blk, clpRng, correctedPics.data(), numRefs, verror.data(), refStr.data(),
                     weightScaling, sigmaSq );
  opt->m_applyBlock( src, dst_opt, blk, clpRng, correctedPics.data(), numRefs, verror.data(), refStr.data(),
                     weightScaling, sigmaSq );

  // The SIMDe implementation of applyBlock may differ by one bit compared to the reference implementation.
  // Adjusted tolerance to reflect this.
  return compare_values_2d( sstm.str(), dst_ref.buf, dst_opt.buf, h, w, dstStride, 1 );
}

static bool check_applyBlock( MCTF* ref, MCTF* opt, unsigned num_cases, int w, int h )
{
  printf( "Testing MCTF::applyBlock w=%d h=%d\n", w, h );
  InputGenerator<Pel> g2 { 2, /*is_signed=*/false };
  DimensionGenerator rng;

  for( unsigned bitDepth : { 8, 10 } )
  {
    InputGenerator<Pel> gBD{ bitDepth, /*is_signed=*/false };
    for( int numRefs : { 6, 8 } )
    {
      for( unsigned i = 0; i < num_cases; ++i )
      {
        unsigned srcStride = rng.get( w + 2, 128 );
        unsigned dstStride = rng.get( w + 2, 128 );

        if( !check_one_applyBlock( ref, opt, srcStride, dstStride, w, h, bitDepth, numRefs, gBD ) )
        {
          return false;
        }
      }
      // Test scenarios with high noise (as corner case) - dst buffer having high variance from src buffer.
      unsigned srcStride = rng.get( w + 2, 128 );
      unsigned dstStride = rng.get( w + 2, 128 );

      if( !check_one_applyBlock( ref, opt, srcStride, dstStride, w, h, bitDepth, numRefs, g2 ) )
      {
        return false;
      }
    }
  }

  return true;
}

template<ChannelType Ch, int NumTaps, typename G>
static bool check_one_applyFrac( MCTF* ref, MCTF* opt, unsigned orgStride, unsigned dstStride, int w, int h,
                                 int xFilterIndex, int yFilterIndex, int bitDepth, G input_generator )
{
  CHECK( orgStride < w, "OrgStride must be greater than or equal to width" );
  CHECK( dstStride < w, "DstStride must be greater than or equal to width" );

  std::ostringstream sstm;
  sstm << "applyFrac" << NumTaps << "Tap_" << ( Ch == CH_L ? 8 : 4 ) << "x"
       << " orgStride=" << orgStride << " dstStride=" << dstStride << " w=" << w << " h=" << h;

  const int channelIndex = static_cast<int>( Ch ); // Chroma or Luma.
  const int centreTapOffset = 3;

  std::vector<Pel> orgBuf( orgStride * ( h + 3 * centreTapOffset ) );
  std::vector<Pel> dst_ref( dstStride * h );
  std::vector<Pel> dst_opt( dstStride * h );

  std::generate( orgBuf.begin(), orgBuf.end(), input_generator );

  Pel* org = orgBuf.data() + centreTapOffset * orgStride + centreTapOffset;

  if( NumTaps == 4 )
  {
    const int16_t* xFilter = MCTF::m_interpolationFilter4[xFilterIndex];
    const int16_t* yFilter = MCTF::m_interpolationFilter4[yFilterIndex];

    ref->m_applyFrac[channelIndex][1]( org, orgStride, dst_ref.data(), dstStride, w, h, xFilter, yFilter, bitDepth );
    opt->m_applyFrac[channelIndex][1]( org, orgStride, dst_opt.data(), dstStride, w, h, xFilter, yFilter, bitDepth );
  }
  else
  {
    const int16_t* xFilter = MCTF::m_interpolationFilter8[xFilterIndex];
    const int16_t* yFilter = MCTF::m_interpolationFilter8[yFilterIndex];

    ref->m_applyFrac[channelIndex][0]( org, orgStride, dst_ref.data(), dstStride, w, h, xFilter, yFilter, bitDepth );
    opt->m_applyFrac[channelIndex][0]( org, orgStride, dst_opt.data(), dstStride, w, h, xFilter, yFilter, bitDepth );
  }

  return compare_values_2d( sstm.str(), dst_ref.data(), dst_opt.data(), h, w, dstStride );
}

template<ChannelType Ch, int NumTaps>
static bool check_applyFrac( MCTF* ref, MCTF* opt, int w, int h )
{
  printf( "Testing MCTF::applyFrac%dTap_%dx w=%d h=%d\n", NumTaps, Ch == CH_L ? 8 : 4, w, h );

  DimensionGenerator rng;
  constexpr int motionVectorFactor = 16;

  for( unsigned bitDepth : { 8, 10 } )
  {
    InputGenerator<TCoeff> g{ bitDepth, /*is_signed=*/false };

    for( int xIndex = 0; xIndex < motionVectorFactor; ++xIndex )
    {
      for( int yIndex = 0; yIndex < motionVectorFactor; ++yIndex )
      {
        // Stride is often the width of a video frame, so use the width of 8K as an upper bound.
        unsigned orgStride = rng.get( w, g_fastUnitTest ? 512 : 8192 );
        unsigned dstStride = rng.get( w, g_fastUnitTest ? 512 : 8192 );
        if( !check_one_applyFrac<Ch, NumTaps>( ref, opt, orgStride, dstStride, w, h, xIndex, yIndex, bitDepth, g ) )
        {
          return false;
        }
      }
    }
  }

  return true;
}

template<typename G>
static bool check_one_applyPlanarCorrection( MCTF* ref, MCTF* opt, unsigned orgStride, unsigned dstStride, int size,
                                             int bitDepth, uint16_t motionerror, G input_generator )
{
  CHECK( orgStride < size, "OrgStride must be greater than or equal to width" );
  CHECK( dstStride < size, "DstStride must be greater than or equal to width" );

  std::ostringstream sstm;
  sstm << "applyPlanarCorrection orgStride=" << orgStride << " dstStride=" << dstStride << " w=" << size
       << " h=" << size << " motionerror=" << motionerror;

  std::vector<Pel> org( orgStride * size );
  std::vector<Pel> dst_ref( dstStride * size );
  std::vector<Pel> dst_opt( dstStride * size );

  // Initialize source buffers.
  std::generate( org.begin(), org.end(), input_generator );
  std::generate( dst_ref.begin(), dst_ref.end(), input_generator );
  dst_opt = dst_ref;

  const ClpRng clpRng{ bitDepth };
  ref->m_applyPlanarCorrection( org.data(), orgStride, dst_ref.data(), dstStride, size, size, clpRng, motionerror );
  opt->m_applyPlanarCorrection( org.data(), orgStride, dst_opt.data(), dstStride, size, size, clpRng, motionerror );
  return compare_values_2d( sstm.str(), dst_ref.data(), dst_opt.data(), size, size, dstStride );
}

static bool check_applyPlanarCorrection( MCTF* ref, MCTF* opt, unsigned num_cases, int size )
{
  printf( "Testing MCTF::applyPlanarCorrection w=%d h=%d\n", size, size );
  InputGenerator<TCoeff> g{ 10, /*is_signed=*/false };
  DimensionGenerator rng;

  for( unsigned i = 0; i < num_cases; ++i )
  {
    for( int bitDepth = 8; bitDepth <= 10; bitDepth += 2 )
    {
      unsigned orgStride = rng.get( size, 128 );
      unsigned dstStride = rng.get( size, 128 );
      uint16_t motionerror = rng.get( 1, 32 );
      if( !check_one_applyPlanarCorrection( ref, opt, orgStride, dstStride, size, bitDepth, motionerror, g ) )
      {
        return false;
      }
    }
  }

  return true;
}

template<bool lowRes, typename G>
static bool check_one_motionErrorLumaFrac8( MCTF* ref, MCTF* opt, unsigned orgStride, unsigned bufStride, unsigned w,
                                            unsigned h, unsigned xFilterIndex, unsigned yFilterIndex, int bitDepth,
                                            unsigned besterror, G input_generator )
{
  CHECK( orgStride < w, "OrgStride must be greater than or equal to width" );
  CHECK( bufStride < w, "BufStride must be greater than or equal to width" );
  CHECK( w % 8, "Width must be a multiple of eight" );
  CHECK( h % 8, "Height must be a multiple of eight" );

  std::ostringstream sstm;
  sstm << "motionErrorLumaFrac8 orgStride=" << orgStride << " bufStride=" << bufStride << " w=" << w << " h=" << h
       << " bitDepth" << bitDepth << " besterror=" << besterror;

  std::vector<Pel> org( orgStride * h );
  // Initialize source buffer.
  std::generate( org.begin(), org.end(), input_generator );

  int channelIndex;
  const int16_t* xFilter;
  const int16_t* yFilter;

  if( lowRes )
  {
    channelIndex = 1;
    xFilter = MCTF::m_interpolationFilter4[xFilterIndex];
    yFilter = MCTF::m_interpolationFilter4[yFilterIndex];
  }
  else
  {
    channelIndex = 0;
    xFilter = MCTF::m_interpolationFilter8[xFilterIndex];
    yFilter = MCTF::m_interpolationFilter8[yFilterIndex];
  }

  // Common buffer size used for m_motionErrorLumaFrac8[0] and m_motionErrorLumaFrac8[1].
  std::vector<Pel> buf( bufStride * ( h + 10 ) + 3 );
  std::generate( buf.begin(), buf.end(), input_generator );
  const Pel* buff = buf.data() + 3 * bufStride + 3;

  int error_ref = ref->m_motionErrorLumaFrac8[channelIndex]( org.data(), orgStride, buff, bufStride, w, h, xFilter,
                                                             yFilter, bitDepth, besterror );
  int error_opt = opt->m_motionErrorLumaFrac8[channelIndex]( org.data(), orgStride, buff, bufStride, w, h, xFilter,
                                                             yFilter, bitDepth, besterror );
  return compare_value( sstm.str(), error_ref, error_opt );
}

template<bool lowRes>
static bool check_motionErrorLumaFrac8( MCTF* ref, MCTF* opt, int w, int h )
{
  printf( "Testing MCTF::motionErrorLumaFrac8 w=%d h=%d\n", w, h );
  InputGenerator<TCoeff> g{ 10 };
  DimensionGenerator rng;

  constexpr unsigned motionVectorFactor = 16;
  constexpr unsigned bitDepth = 10;

  for( unsigned xIndex = 1; xIndex < motionVectorFactor; ++xIndex )
  {
    for( unsigned yIndex = 1; yIndex < motionVectorFactor; ++yIndex )
    {
      // Stride is often the width of a video frame, so use the width of 8K as an upper bound.
      unsigned orgStride = rng.get( w, g_fastUnitTest ? 512 : 8192 );
      unsigned bufStride = rng.get( w, g_fastUnitTest ? 512 : 8192 );
      unsigned besterror = INT_MAX;
      if( !check_one_motionErrorLumaFrac8<lowRes>( ref, opt, orgStride, bufStride, w, h, xIndex, yIndex, bitDepth,
                                                   besterror, g ) )
      {
        return false;
      }
    }
  }

  return true;
}

template<typename G>
static bool check_one_motionErrorLumaInt8( MCTF* ref, MCTF* opt, unsigned orgStride, unsigned bufStride, unsigned w,
                                           unsigned h, unsigned besterror, G input_generator )
{
  CHECK( orgStride < w, "OrgStride must be greater than or equal to width" );
  CHECK( bufStride < w, "BufStride must be greater than or equal to width" );
  CHECK( w % 8, "Width must be a multiple of eight" );
  CHECK( h % 8, "Height must be a multiple of eight" );

  std::ostringstream sstm;
  sstm << "motionErrorLumaInt8 orgStride=" << orgStride << " bufStride=" << bufStride << " w=" << w << " h=" << h
       << " besterror=" << besterror;

  std::vector<Pel> org( orgStride * h );
  std::vector<Pel> buf( bufStride * h );

  // Initialize source buffers.
  std::generate( org.begin(), org.end(), input_generator );
  std::generate( buf.begin(), buf.end(), input_generator );

  int error_ref = ref->m_motionErrorLumaInt8( org.data(), orgStride, buf.data(), bufStride, w, h, besterror );
  int error_opt = opt->m_motionErrorLumaInt8( org.data(), orgStride, buf.data(), bufStride, w, h, besterror );
  return compare_value( sstm.str(), error_ref, error_opt );
}

static bool check_motionErrorLumaInt8( MCTF* ref, MCTF* opt, unsigned num_cases, int w, int h )
{
  printf( "Testing MCTF::motionErrorLumaInt8 w=%d h=%d\n", w, h );
  InputGenerator<TCoeff> g{ 10 };
  DimensionGenerator rng;

  for( unsigned i = 0; i < num_cases; ++i )
  {
    unsigned orgStride = rng.get( w, 128 );
    unsigned bufStride = rng.get( w, 128 );
    unsigned besterror = INT_MAX;
    if( !check_one_motionErrorLumaInt8( ref, opt, orgStride, bufStride, w, h, besterror, g ) )
    {
      return false;
    }
  }

  return true;
}

static bool test_MCTF()
{
  MCTF ref{ /*enableOpt=*/false };
  MCTF opt{ /*enableOpt=*/true };

  unsigned num_cases = NUM_CASES;
  bool passed = true;

  std::vector<unsigned> sizes = { 4, 8, 16, 24, 32, 40, 48, 56, 64 };
  for( unsigned w : sizes )
  {
    for( unsigned h : sizes )
    {
      passed = check_applyBlock( &ref, &opt, num_cases, w, h ) && passed;
    }
  }

  for( unsigned w : sizes )
  {
    for( unsigned h : sizes )
    {
      // applyFrac4Tap_4x testing.
      passed = check_applyFrac<CH_C, /*NumTaps=*/4>( &ref, &opt, w, h ) && passed;
      // applyFrac6Tap_4x testing.
      passed = check_applyFrac<CH_C, /*NumTaps=*/6>( &ref, &opt, w, h ) && passed;

      if( w >= 8 && h >= 8 )
      {
        // applyFrac4Tap_8x testing.
        passed = check_applyFrac<CH_L, /*NumTaps=*/4>( &ref, &opt, w, h ) && passed;
        // applyFrac6Tap_8x testing.
        passed = check_applyFrac<CH_L, /*NumTaps=*/6>( &ref, &opt, w, h ) && passed;
      }
    }
  }

  for( int size = 4; size <= 32; size *= 2 )
  {
    passed = check_applyPlanarCorrection( &ref, &opt, num_cases, size ) && passed;
  }

  for( unsigned w = 8; w <= 64; w += 8 )
  {
    for( unsigned h = 8; h <= 64; h += 8 )
    {
      // motionErrorLumaFrac4 testing.
      passed = check_motionErrorLumaFrac8</*lowRes=*/true>( &ref, &opt, w, h ) && passed;
      // motionErrorLumaFrac6 testing.
      passed = check_motionErrorLumaFrac8</*lowRes=*/false>( &ref, &opt, w, h ) && passed;

      passed = check_motionErrorLumaInt8( &ref, &opt, num_cases, w, h ) && passed;
    }
  }
  return passed;
}
#endif

#if ENABLE_SIMD_OPT_BDOF
template<typename G>
static bool check_one_biDirOptFlow( InterPredInterpolation* ref, InterPredInterpolation* opt, int width, int height,
                                    ptrdiff_t dstStride, int shift, int offset, int limit, ClpRng clpRng, unsigned bitDepth,
                                    G input_generator )
{
  CHECK( width % 8, "Width must be a multiple of eight" );
  CHECK( height % 8, "Height must be a multiple of eight" );

  std::ostringstream sstm;
  sstm << "biDirOptFlow width=" << width << " height=" << height << " shift=" << shift << " offset=" << offset
       << " limit=" << limit;

  int srcStride  = width + 2 * BDOF_EXTEND_SIZE + 2;
  int gradStride = width + 2 * BDOF_EXTEND_SIZE;

  // need to add 2 samples to allow SIMD reads of 6 values by using 128-bit read
  std::vector<Pel> srcY0 ( srcStride  * ( height + 2 ) + 2 );
  std::vector<Pel> srcY1 ( srcStride  * ( height + 2 ) + 2 );
  std::vector<Pel> gradX0( gradStride * ( height + 2 ) + 2 );
  std::vector<Pel> gradX1( gradStride * ( height + 2 ) + 2 );
  std::vector<Pel> gradY0( gradStride * ( height + 2 ) + 2 );
  std::vector<Pel> gradY1( gradStride * ( height + 2 ) + 2 );
  std::vector<Pel> dstYref( dstStride * height );
  std::vector<Pel> dstYopt( dstStride * height );

  // Initialize source buffers.
  std::generate( srcY0.begin(),  srcY0.end(),  input_generator );
  std::generate( srcY1.begin(),  srcY1.end(),  input_generator );
  std::generate( gradX0.begin(), gradX0.end(), input_generator );
  std::generate( gradX1.begin(), gradX1.end(), input_generator );
  std::generate( gradY0.begin(), gradY0.end(), input_generator );
  std::generate( gradY1.begin(), gradY1.end(), input_generator );

  ref->xFpBiDirOptFlow( srcY0.data(), srcY1.data(), gradX0.data(), gradX1.data(), gradY0.data(), gradY1.data(), width,
                        height, dstYref.data(), dstStride, shift, offset, limit, clpRng, bitDepth );
  opt->xFpBiDirOptFlow( srcY0.data(), srcY1.data(), gradX0.data(), gradX1.data(), gradY0.data(), gradY1.data(), width,
                        height, dstYopt.data(), dstStride, shift, offset, limit, clpRng, bitDepth );
  return compare_values_2d( sstm.str(), dstYref.data(), dstYopt.data(), height, (unsigned) dstStride );
}

static bool check_biDirOptFlow( InterPredInterpolation* ref, InterPredInterpolation* opt, unsigned num_cases, int width,
                                int height )
{
  printf( "Testing InterPred::xFpBiDirOptFlow w=%d h=%d\n", width, height );
  DimensionGenerator rng;

  for( unsigned i = 0; i < num_cases; ++i )
  {
    // Width is either 8 or 16.
    // DstStride is a multiple of eight in the range width to 128 inclusive.
    unsigned dstStride = rng.get( width, 128, 8 );

    for( int bitDepth : { 8, 10 } )
    {
      InputGenerator<Pel> g{ (unsigned) bitDepth };
      const unsigned shift = IF_INTERNAL_PREC + 1 - bitDepth;
      const int offset = ( 1 << ( shift - 1 ) ) + 2 * IF_INTERNAL_OFFS;
      const int limit = ( 1 << 4 ) - 1;
      ClpRng clpRng{ bitDepth };

      if( !check_one_biDirOptFlow( ref, opt, width, height, dstStride, shift, offset, limit, clpRng, bitDepth, g ) )
      {
        return false;
      }
    }
  }

  return true;
}

template<typename G>
static bool check_one_gradFilter( InterPredInterpolation* ref, InterPredInterpolation* opt, int width, int height,
                                  int srcStride, int gradStride, G input_generator, bool padding )
{
  std::ostringstream sstm;
  sstm << "gradFilter width=" << width << " height=" << height << " padding=" << padding;

  int widthG = width + 2;
  int heightG = height + 2;

  std::vector<Pel> src( srcStride * heightG );
  std::vector<Pel> gradX_ref( gradStride * heightG );
  std::vector<Pel> gradY_ref( gradStride * heightG );
  std::vector<Pel> gradX_opt( gradStride * heightG );
  std::vector<Pel> gradY_opt( gradStride * heightG );

  // Initialize source buffer.
  std::generate( src.begin(), src.end(), input_generator );

  const int bitDepth = 10; // Unused in gradFilter.

  if( padding )
  {
    ref->xFpBDOFGradFilter( src.data(), srcStride, widthG, heightG, gradStride, gradX_ref.data(), gradY_ref.data(),
                            bitDepth );
    opt->xFpBDOFGradFilter( src.data(), srcStride, widthG, heightG, gradStride, gradX_opt.data(), gradY_opt.data(),
                            bitDepth );
  }
  else
  {
    ref->xFpProfGradFilter( src.data(), srcStride, widthG, heightG, gradStride, gradX_ref.data(), gradY_ref.data(),
                            bitDepth );
    opt->xFpProfGradFilter( src.data(), srcStride, widthG, heightG, gradStride, gradX_opt.data(), gradY_opt.data(),
                            bitDepth );
  }

  bool res_gradX = compare_values_2d( sstm.str(), gradX_ref.data(), gradX_opt.data(), heightG, ( unsigned )gradStride );
  bool res_gradY = compare_values_2d( sstm.str(), gradY_ref.data(), gradY_opt.data(), heightG, ( unsigned )gradStride );

  return res_gradX && res_gradY;
}

static bool check_gradFilter( InterPredInterpolation* ref, InterPredInterpolation* opt, unsigned num_cases, int width,
                              int height )
{
  printf( "Testing InterPred::gradFilter w=%d h=%d\n", width, height );
  InputGenerator<Pel> g{ 14 }; // Signed 14 bit.
  DimensionGenerator rng;

  for( unsigned i = 0; i < num_cases; ++i )
  {
    unsigned srcStride = rng.get( width + 2, 128 );
    unsigned gradStride = rng.get( width + 2, 128 );

    for( auto padding : { true, false } )
    {
      if( !check_one_gradFilter( ref, opt, width, height, srcStride, gradStride, g, padding ) )
      {
        return false;
      }
    }
  }

  return true;
}

static bool test_InterPred()
{
  InterPredInterpolation ref;
  InterPredInterpolation opt;

  ref.init( /*enableOpt=*/false );
  opt.init( /*enableOpt=*/true );

  unsigned num_cases = NUM_CASES;
  bool passed = true;

  for( unsigned width = 8; width <= 16; width += 8 )
  {
    for( unsigned height = 8; height <= 16; height += 8 )
    {
      passed = check_biDirOptFlow( &ref, &opt, num_cases, width, height ) && passed;
    }
  }

  for( unsigned w : { 4, 8, 16, 32 } )
  {
    for( unsigned h : { 4, 8, 16, 32 } )
    {
      passed = check_gradFilter( &ref, &opt, num_cases, w, h ) && passed;
    }
  }

  return passed;
}
#endif

#if ENABLE_SIMD_OPT_DIST
static bool check_lumaWeightedSSE( RdCost* ref, RdCost* opt, unsigned num_cases, int width, int height )
{
  printf( "Testing RdCost::lumaWeightedSSE %dx%d\n", width, height );

  std::ostringstream sstm;
  sstm << "lumaWeightedSSE" << " w=" << width << " h=" << height;

  InputGenerator<Pel> g14{ 14 };
  InputGenerator<Pel> g10{ 10, /*is_signed=*/false }; // Index range : 0 - 1023.
  InputGenerator<uint32_t> g17{ 17, /*is_signed=*/false };
  DimensionGenerator rng;

  bool passed = true;
  for( unsigned i = 0; i < num_cases; i++ )
  {
    int org_stride  = rng.get( width, g_fastUnitTest ? 256 : 1024 );
    int cur_stride  = rng.get( width, g_fastUnitTest ? 256 : 1024 );
    int luma_stride = rng.get( width, g_fastUnitTest ? 256 : 1024 );
    std::vector<Pel> orgBuf( org_stride * height );
    std::vector<Pel> curBuf( cur_stride * height );
    std::vector<Pel> orgLumaBuf( luma_stride * height * 2 );
    std::vector<uint32_t> lumaWeights( 1024 );

    DistParam dtParam;
    dtParam.org.buf = orgBuf.data();
    dtParam.cur.buf = curBuf.data();
    dtParam.org.width = width;
    dtParam.org.height = height;
    dtParam.cur.stride = cur_stride;
    dtParam.org.stride = org_stride;
    CPelBuf pelBuf;
    pelBuf.buf = orgLumaBuf.data();
    pelBuf.stride = luma_stride;
    dtParam.orgLuma = &pelBuf;
    dtParam.bitDepth = 10;
    dtParam.compID = COMP_Y;

    std::generate( orgBuf.begin(), orgBuf.end(), g14 );
    std::generate( curBuf.begin(), curBuf.end(), g14 );
    std::generate( orgLumaBuf.begin(), orgLumaBuf.end(), g10 );
    std::generate( lumaWeights.begin(), lumaWeights.end(), g17 );

    for( unsigned csx = 0; csx < 2; csx++ )
    {
      Distortion sum_ref = ref->m_wtdPredPtr[csx]( dtParam, VVENC_CHROMA_420, lumaWeights.data() );
      Distortion sum_opt = opt->m_wtdPredPtr[csx]( dtParam, VVENC_CHROMA_420, lumaWeights.data() );
      passed = compare_value( sstm.str(), sum_ref, sum_opt ) && passed;
    }
  }

  return passed;
}

static bool check_fixWeightedSSE( RdCost* ref, RdCost* opt, unsigned num_cases, int width, int height )
{
  printf( "Testing RdCost::fixWeightedSSE %dx%d\n", width, height );

  std::ostringstream sstm;
  sstm << "fixWeightedSSE" << " w=" << width << " h=" << height;

  DimensionGenerator rng;
  InputGenerator<Pel> g14{ 14 };
  InputGenerator<uint32_t> g17{ 17, /*is_signed=*/false };

  bool passed = true;
  for( unsigned i = 0; i < num_cases; i++ )
  {
    int org_stride = rng.get( width, g_fastUnitTest ? 256 : 1024 );
    int cur_stride = rng.get( width, g_fastUnitTest ? 256 : 1024 );
    std::vector<Pel> orgBuf( org_stride * height );
    std::vector<Pel> curBuf( cur_stride * height );

    DistParam dtParam;
    dtParam.org.buf = orgBuf.data();
    dtParam.cur.buf = curBuf.data();
    dtParam.org.width = width;
    dtParam.org.height = height;
    dtParam.cur.stride = cur_stride;
    dtParam.org.stride = org_stride;
    dtParam.bitDepth = 10;

    std::generate( orgBuf.begin(), orgBuf.end(), g14 );
    std::generate( curBuf.begin(), curBuf.end(), g14 );
    uint32_t fixedPTweight = g17();

    Distortion sum_ref = ref->m_fxdWtdPredPtr( dtParam, fixedPTweight );
    Distortion sum_opt = opt->m_fxdWtdPredPtr( dtParam, fixedPTweight );
    passed = compare_value( sstm.str(), sum_ref, sum_opt ) && passed;
  }
  return passed;
}

static bool check_SAD( RdCost* ref, RdCost* opt, unsigned num_cases, int width, int height )
{
  std::ostringstream sstm;
  sstm << "RdCost::m_afpDistortFunc[0][DF_SAD" << width << "] "
       << " w=" << width << " h=" << height;
  printf( "Testing %s\n", sstm.str().c_str() );

  DimensionGenerator rng;
  InputGenerator<Pel> g10{ 10, /*is_signed=*/false };

  bool passed = true;
  for( unsigned i = 0; i < num_cases; i++ )
  {
    int org_stride = rng.get( width, g_fastUnitTest ? 256 : 1024 );
    int cur_stride = rng.get( width, g_fastUnitTest ? 256 : 1024 );
    std::vector<Pel> orgBuf( org_stride * height );
    std::vector<Pel> curBuf( cur_stride * height );

    DistParam dtParam;
    dtParam.org.buf = orgBuf.data();
    dtParam.org.stride = org_stride;
    dtParam.cur.buf = curBuf.data();
    dtParam.cur.stride = cur_stride;
    dtParam.org.width = width;
    dtParam.org.height = height;
    dtParam.bitDepth = 10;
    dtParam.subShift = rng.get( 0, 1 );
    dtParam.applyWeight = 0; // applyWeight appears to be always zero.

    std::generate( orgBuf.begin(), orgBuf.end(), g10 );
    std::generate( curBuf.begin(), curBuf.end(), g10 );

    const int index = DF_SAD + log2( width );
    Distortion sum_ref = ref->m_afpDistortFunc[0][index]( dtParam );
    Distortion sum_opt = opt->m_afpDistortFunc[0][index]( dtParam );
    passed = compare_value( sstm.str(), sum_ref, sum_opt ) && passed;
  }
  return passed;
}

static bool check_HADs( RdCost* ref, RdCost* opt, unsigned num_cases, int width, int height, bool fast )
{
  std::ostringstream sstm;
  const char* fast_str = fast ? "_fast" : "";
  sstm << "RdCost::m_afpDistortFunc[0][DF_HAD" << width << fast_str << "] "
       << " w=" << width << " h=" << height;
  printf( "Testing %s\n", sstm.str().c_str() );

  DimensionGenerator rng;
  InputGenerator<Pel> g10{ 10, /*is_signed=*/false };

  bool passed = true;
  for( unsigned i = 0; i < num_cases; i++ )
  {
    int org_stride = rng.get( width, g_fastUnitTest ? 256 : 1024 );
    int cur_stride = rng.get( width, g_fastUnitTest ? 256 : 1024 );
    std::vector<Pel> orgBuf( org_stride * height );
    std::vector<Pel> curBuf( cur_stride * height );

    DistParam dtParam;
    dtParam.org.buf = orgBuf.data();
    dtParam.org.stride = org_stride;
    dtParam.cur.buf = curBuf.data();
    dtParam.cur.stride = cur_stride;
    dtParam.org.width = width;
    dtParam.org.height = height;
    dtParam.bitDepth = 10;
    dtParam.applyWeight = 0; // applyWeight appears to be always zero.

    std::generate( orgBuf.begin(), orgBuf.end(), g10 );
    std::generate( curBuf.begin(), curBuf.end(), g10 );

    const int index = ( fast ? DF_HAD_fast : DF_HAD ) + log2( width );
    Distortion sum_ref = ref->m_afpDistortFunc[0][( DFunc )( index )]( dtParam );
    Distortion sum_opt = opt->m_afpDistortFunc[0][( DFunc )( index )]( dtParam );
    passed = compare_value( sstm.str(), sum_ref, sum_opt ) && passed;
  }
  return passed;
}

static bool check_SADwMask( RdCost* ref, RdCost* opt, unsigned num_cases, int width, int height )
{
  std::ostringstream sstm;
  sstm << "RdCost::m_afpDistortFunc[0][DF_SAD_WITH_MASK] " << " w=" << width << " h=" << height;
  printf( "Testing %s\n", sstm.str().c_str());

  DimensionGenerator rng;
  InputGenerator<Pel> g1{ 1, /*is_signed=*/false }; // Masks are either 0 or 1.
  InputGenerator<Pel> g10{ 10, /*is_signed=*/false };

  bool passed = true;
  for( unsigned i = 0; i < num_cases; i++ )
  {
    int splitDir = rng.get( 0, GEO_NUM_PARTITION_MODE - 1 );
    int maskStride = 0, maskStride2 = 0;
    int stepX = 1;
    Pel *sadMask;
    int16_t angle = g_GeoParams[splitDir][0];
    const int wIdx = floorLog2( width ) - GEO_MIN_CU_LOG2;
    const int hIdx = floorLog2( height ) - GEO_MIN_CU_LOG2;
    
    if( g_angle2mirror[angle] == 2 )
    {
      maskStride  = -GEO_WEIGHT_MASK_SIZE;
      maskStride2 = -width;
      sadMask     = &g_globalGeoEncSADmask[g_angle2mask[g_GeoParams[splitDir][0]]]
                      [( GEO_WEIGHT_MASK_SIZE - 1 - g_weightOffset[hIdx][wIdx][splitDir][1] ) * GEO_WEIGHT_MASK_SIZE
                                                  + g_weightOffset[hIdx][wIdx][splitDir][0]
                      ];
    }
    else if( g_angle2mirror[angle] == 1 )
    {
      stepX       = -1;
      maskStride2 = width;
      maskStride  = GEO_WEIGHT_MASK_SIZE;
      sadMask     = &g_globalGeoEncSADmask[g_angle2mask[g_GeoParams[splitDir][0]]]
                      [     GEO_WEIGHT_MASK_SIZE *     g_weightOffset[hIdx][wIdx][splitDir][1]
                        + ( GEO_WEIGHT_MASK_SIZE - 1 - g_weightOffset[hIdx][wIdx][splitDir][0] )
                      ];
    }
    else
    {
      maskStride  = GEO_WEIGHT_MASK_SIZE;
      maskStride2 = -width;
      sadMask     = &g_globalGeoEncSADmask[g_angle2mask[g_GeoParams[splitDir][0]]]
                      [   g_weightOffset[hIdx][wIdx][splitDir][1] * GEO_WEIGHT_MASK_SIZE
                        + g_weightOffset[hIdx][wIdx][splitDir][0]
                      ];
    }
    int org_stride  = rng.get( width, g_fastUnitTest ? 256 : 1024 );
    int cur_stride  = rng.get( width, g_fastUnitTest ? 256 : 1024 );
    std::vector<Pel> orgBuf( org_stride * height );
    std::vector<Pel> curBuf( cur_stride * height );

    DistParam dtParam;
    dtParam.org.buf = orgBuf.data();
    dtParam.org.stride = org_stride;
    dtParam.cur.buf = curBuf.data();
    dtParam.cur.stride = cur_stride;
    dtParam.mask = sadMask;
    dtParam.maskStride = maskStride;
    dtParam.maskStride2 = maskStride2;
    dtParam.org.width = width;
    dtParam.org.height = height;
    dtParam.bitDepth = 10;
    dtParam.subShift = 0; // always 0 for GEO
    dtParam.applyWeight = 0;  // applyWeight appears to be always zero.
    dtParam.stepX = stepX;

    std::generate( orgBuf.begin(), orgBuf.end(), g10 );
    std::generate( curBuf.begin(), curBuf.end(), g10 );

    Distortion sum_ref = ref->m_afpDistortFunc[0][DF_SAD_WITH_MASK]( dtParam );
    Distortion sum_opt = opt->m_afpDistortFunc[0][DF_SAD_WITH_MASK]( dtParam );
    passed = compare_value( sstm.str(), sum_ref, sum_opt ) && passed;
  }
  return passed;
}

static bool test_RdCost()
{
  RdCost ref;
  RdCost opt;
  ref.create( /*enableOpt=*/false );
  opt.create( /*enableOpt=*/true );

  unsigned num_cases = NUM_CASES;
  bool passed = true;
  std::array<int, 8> widths = { 1, 2, 4, 8, 16, 32, 64, 128 };
  std::array<int, 7> heights = { 2, 4, 8, 16, 32, 64, 128 };

  for( int h : heights )
  {
    for( int w : widths )
    {
      passed = check_lumaWeightedSSE( &ref, &opt, num_cases, w, h ) && passed;
      passed = check_fixWeightedSSE( &ref, &opt, num_cases, w, h ) && passed;
      passed = check_SAD( &ref, &opt, num_cases, w, h ) && passed;

      if( w >= 2 )
      {
        passed = check_HADs( &ref, &opt, num_cases, w, h, /*fast=*/true ) && passed;
        passed = check_HADs( &ref, &opt, num_cases, w, h, /*fast=*/false ) && passed;
      }

      if (w >= GEO_MIN_CU_SIZE && h >= GEO_MIN_CU_SIZE && w <= GEO_MAX_CU_SIZE && h <= GEO_MAX_CU_SIZE)
      {
        passed = check_SADwMask( &ref, &opt, num_cases, w, h ) && passed;
      }
    }
  }
  return passed;
}
#endif // ENABLE_SIMD_OPT_DIST

#if ENABLE_SIMD_OPT_AFFINE_ME
template<typename G>
static bool check_EqualCoeffComputer( AffineGradientSearch* ref, AffineGradientSearch* opt, unsigned num_cases,
                                      G inp_gen )
{
  DimensionGenerator dim;

  static constexpr size_t buf_size = MAX_CU_SIZE * MAX_CU_SIZE;
  std::vector<Pel> residue( buf_size );
  std::vector<Pel> derivate0( buf_size );
  std::vector<Pel> derivate1( buf_size );
  Pel* pDerivate[2] = { derivate0.data(), derivate1.data() };

  static constexpr size_t coeff_size = 7;
  int64_t i64EqualCoeff_ref[coeff_size][coeff_size];
  int64_t i64EqualCoeff_opt[coeff_size][coeff_size];

  bool passed = true;

  for( int b6Param : { 0, 1 } )
  {
    std::ostringstream sstm_test;
    sstm_test << "AffineGradientSearch::EqualCoeffComputer<" << std::boolalpha << static_cast<bool>( b6Param ) << ">"
              << " Input=" << inp_gen.input_type();
    std::cout << "Testing " << sstm_test.str() << std::endl;

    // Set height and width to powers of two >= 16.
    for( int height : { 16, 32, 64, 128 } )
    {
      for( int width : { 16, 32, 64, 128 } )
      {
        for( unsigned n = 0; n < num_cases; n++ )
        {
          // Set random strides >= width.
          const int residueStride = dim.get( width, MAX_CU_SIZE );
          const int derivateStride = dim.get( width, MAX_CU_SIZE );

          // Fill input buffers with signed 10-bit data from generator.
          std::generate( residue.begin(), residue.end(), inp_gen );
          std::generate( derivate0.begin(), derivate0.end(), inp_gen );
          std::generate( derivate1.begin(), derivate1.end(), inp_gen );

          // Clear output blocks.
          std::memset( i64EqualCoeff_ref, 0, sizeof( i64EqualCoeff_ref ) );
          std::memset( i64EqualCoeff_opt, 0, sizeof( i64EqualCoeff_opt ) );

          ref->m_EqualCoeffComputer[b6Param]( residue.data(), residueStride, pDerivate, derivateStride, width, height,
                                              i64EqualCoeff_ref );
          opt->m_EqualCoeffComputer[b6Param]( residue.data(), residueStride, pDerivate, derivateStride, width, height,
                                              i64EqualCoeff_opt );

          std::ostringstream sstm_subtest;
          sstm_subtest << sstm_test.str() << " residueStride=" << residueStride << " derivateStride=" << derivateStride
                       << " width=" << width << " height=" << height;

          passed = compare_values_2d( sstm_subtest.str(), &i64EqualCoeff_ref[0][0], &i64EqualCoeff_opt[0][0],
                                      coeff_size, coeff_size ) &&
                   passed;
        }
      }
    }
  }

  return passed;
}

static bool test_AffineGradientSearch()
{
  AffineGradientSearch ref{ /*enableOpt=*/false };
  AffineGradientSearch opt{ /*enableOpt=*/true };

  unsigned num_cases = NUM_CASES;
  bool passed = true;

  // Observed up to 13 bit inputs in a real encoding.
  static constexpr unsigned bd = 13;
  auto random_gen = InputGenerator<Pel>{ bd, /*is_signed=*/true };
  auto minmax_gen = MinMaxGenerator<Pel>{ bd, /*is_signed=*/true };

  passed = check_EqualCoeffComputer( &ref, &opt, num_cases, random_gen ) && passed;
  passed = check_EqualCoeffComputer( &ref, &opt, num_cases, minmax_gen ) && passed;

  return passed;
}
#endif // ENABLE_SIMD_OPT_AFFINE_ME

#ifdef ENABLE_SIMD_OPT_BUFFER
static bool check_addAvg( PelBufferOps* ref, PelBufferOps* opt, unsigned num_cases )
{
  static constexpr unsigned bd = 10;
  ClpRng clpRng{ bd };
  DimensionGenerator dim;
  InputGenerator<Pel> inp_gen{ bd, /*is_signed=*/false }; // unsigned 10-bit

  const unsigned shiftNum = std::max<int>( 2, ( IF_INTERNAL_PREC - bd ) ) + 1;
  const int      offset   = ( 1 << ( shiftNum - 1 ) ) + 2 * IF_INTERNAL_OFFS;

  static constexpr size_t buf_size = MAX_CU_SIZE * MAX_CU_SIZE;

  // Use xMalloc to create aligned buffers.
  Pel *src0     = ( Pel* ) xMalloc( Pel, buf_size );
  Pel *src1     = ( Pel* ) xMalloc( Pel, buf_size );
  Pel *dest_ref = ( Pel* ) xMalloc( Pel, buf_size );
  Pel *dest_opt = ( Pel* ) xMalloc( Pel, buf_size );

  bool passed = true;

  // Test addAvg with no strides.
  for( int size : { 4, 8, 16, 32, 48, 64, 128, 192 } )
  {
    std::ostringstream sstm_test;
    sstm_test << "PelBufferOps::addAvg" << " size=" << size;
    std::cout << "Testing " << sstm_test.str() << std::endl;

    for( unsigned n = 0; n < num_cases; n++ )
    {
      // Fill input buffers with unsigned 10-bit data from generator.
      std::generate( src0, src0 + buf_size, inp_gen );
      std::generate( src1, src1 + buf_size, inp_gen );

      // Clear output blocks.
      memset( dest_ref, 0, buf_size * sizeof( Pel ) );
      memset( dest_opt, 0, buf_size * sizeof( Pel ) );

      ref->addAvg( src0, src1, dest_ref, size, shiftNum, offset, clpRng );
      opt->addAvg( src0, src1, dest_opt, size, shiftNum, offset, clpRng );

      passed = compare_values_1d( sstm_test.str(), dest_ref, dest_opt, buf_size ) && passed;
    }
  }

  // Test addAvg with strides.
  for( int height : { 4, 8, 16, 24, 32, 64 } )
  {
    for( int width : { 4, 8, 12, 16, 20, 24, 32, 40, 48, 64 } )
    {
      std::ostringstream sstm_test;
      sstm_test << "PelBufferOps::addAvg(strided)" << " w=" << width << " h=" << height;
      std::cout << "Testing " << sstm_test.str() << std::endl;

      for( unsigned n = 0; n < num_cases; n++ )
      {
        // Set random strides >= width.
        const int src0Stride = dim.get( width, MAX_CU_SIZE );
        const int src1Stride = dim.get( width, MAX_CU_SIZE );
        const int destStride = dim.get( width, MAX_CU_SIZE );

        // Fill input buffers with unsigned 10-bit data from generator.
        std::generate( src0, src0 + buf_size, inp_gen );
        std::generate( src1, src1 + buf_size, inp_gen );

        // Clear output blocks.
        memset( dest_ref, 0, buf_size * sizeof( Pel ) );
        memset( dest_opt, 0, buf_size * sizeof( Pel ) );

        if( ( width & 15 ) == 0 )
        {
          ref->addAvg16( src0, src0Stride, src1, src1Stride, dest_ref, destStride,
                         width, height, shiftNum, offset, clpRng );
          opt->addAvg16( src0, src0Stride, src1, src1Stride, dest_opt, destStride,
                         width, height, shiftNum, offset, clpRng );
        }
        else if( ( width & 7 ) == 0 )
        {
          ref->addAvg8( src0, src0Stride, src1, src1Stride, dest_ref, destStride,
                        width, height, shiftNum, offset, clpRng );
          opt->addAvg8( src0, src0Stride, src1, src1Stride, dest_opt, destStride,
                        width, height, shiftNum, offset, clpRng );
        }
        else if( ( width & 3 ) == 0 )
        {
          ref->addAvg4( src0, src0Stride, src1, src1Stride, dest_ref, destStride,
                        width, height, shiftNum, offset, clpRng );
          opt->addAvg4( src0, src0Stride, src1, src1Stride, dest_opt, destStride,
                        width, height, shiftNum, offset, clpRng );
        }
        else // Shouldn't come here.
        {
          THROW( "Unsupported size" );
        }

        std::ostringstream sstm_subtest;
        sstm_subtest << sstm_test.str() << " src0Stride=" << src0Stride << " src1Stride=" << src1Stride
                     << " destStride=" << destStride;

        passed = compare_values_2d( sstm_subtest.str(), dest_ref, dest_opt, height, width, destStride ) && passed;
      }
    }
  }

  xFree( src0 );
  xFree( src1 );
  xFree( dest_ref );
  xFree( dest_opt );

  return passed;
}

static bool test_PelBufferOps()
{
  PelBufferOps ref;
  PelBufferOps opt;

#if defined( TARGET_SIMD_X86 )
  opt.initPelBufOpsX86();
#endif
#if defined( TARGET_SIMD_ARM )
  opt.initPelBufOpsARM();
#endif

  unsigned num_cases = NUM_CASES;
  bool passed = true;

  passed = check_addAvg( &ref, &opt, num_cases ) && passed;

  return passed;
}
#endif // ENABLE_SIMD_OPT_BUFFER

#if ENABLE_SIMD_OPT_MCIF
template<int N, bool isVertical, bool isFirst, bool isLast>
static bool check_filter( InterpolationFilter* ref, InterpolationFilter* opt, unsigned width, unsigned height )
{
  static_assert( N == 2 || N == 4 || N == 6 || N == 8, "Supported taps: 2/4/6/8" );

  std::string str_VerHor = isVertical ? "Ver" : "Hor";

  static constexpr unsigned bd = 10; // default bit-depth
  ClpRng clpRng{ ( int )bd };
  DimensionGenerator dim;
  InputGenerator<Pel> inp_gen{ bd, /*is_signed=*/false };

  // Max buffer size is ( height + 8 ) * srcStride.
  static constexpr unsigned BUF_SIZE = ( MAX_CU_SIZE + 8 ) * ( MAX_CU_SIZE + 8 );
  std::vector<Pel> src( BUF_SIZE );
  std::vector<Pel> dst_ref( BUF_SIZE );
  std::vector<Pel> dst_opt( BUF_SIZE );

  std::ostringstream sstm_test;
  sstm_test << "InterpolationFilter::filter" << str_VerHor << "_N" << N << "[" << isFirst << "][" << isLast << "]"
            << " width=" << width << " height=" << height;
  std::cout << "Testing " << sstm_test.str() << std::endl;

  unsigned srcStride = dim.get( width, MAX_CU_SIZE + 8 );
  unsigned dstStride = dim.get( width, MAX_CU_SIZE + 8 );

  // Fill input buffers with unsigned data.
  std::generate( src.begin(), src.end(), inp_gen );

  // Clear output blocks.
  std::fill( dst_ref.begin(), dst_ref.end(), 0 );
  std::fill( dst_opt.begin(), dst_opt.end(), 0 );

  int cStride = isVertical ? srcStride : 1;
  ptrdiff_t src_offset = ( N / 2 - 1 ) * cStride;

  unsigned frac, tapIdx;
  const TFilterCoeff* pCoeff;

  if( N == 8 )
  {
    tapIdx = 0;
    frac = dim.get( 0, LUMA_INTERPOLATION_FILTER_SUB_SAMPLE_POSITIONS );
    pCoeff = InterpolationFilter::m_lumaFilter[frac];
  }
  else if( N == 6 )
  {
    tapIdx = 3;
    frac = dim.get( 0, LUMA_INTERPOLATION_FILTER_SUB_SAMPLE_POSITIONS + 1 );
    pCoeff = frac == 17 ? InterpolationFilter::m_lumaAltHpelIFilter : InterpolationFilter::m_lumaFilter4x4[frac];
    src_offset += cStride; // some of the 6-tap filter impementations use 8-tap
  }
  else if( N == 4 )
  {
    tapIdx = 1;
    frac = dim.get( 0, LUMA_INTERPOLATION_FILTER_SUB_SAMPLE_POSITIONS );
    pCoeff = InterpolationFilter::m_chromaFilter[frac << 1];
  }
  else // N == 2
  {
    tapIdx = 2;
    frac = dim.get( 0, LUMA_INTERPOLATION_FILTER_SUB_SAMPLE_POSITIONS - 1 );
    pCoeff = InterpolationFilter::m_bilinearFilterPrec4[frac];
  }

  if( isVertical )
  {
    ref->m_filterVer[tapIdx][isFirst][isLast]( clpRng, src.data() + src_offset, ( int )srcStride, dst_ref.data(),
                                               ( int )dstStride, ( int )width, ( int )height, pCoeff );
    opt->m_filterVer[tapIdx][isFirst][isLast]( clpRng, src.data() + src_offset, ( int )srcStride, dst_opt.data(),
                                               ( int )dstStride, ( int )width, ( int )height, pCoeff );
  }
  else // Horizontal
  {
    ref->m_filterHor[tapIdx][isFirst][isLast]( clpRng, src.data() + src_offset, ( int )srcStride, dst_ref.data(),
                                               ( int )dstStride, ( int )width, ( int )height, pCoeff );
    opt->m_filterHor[tapIdx][isFirst][isLast]( clpRng, src.data() + src_offset, ( int )srcStride, dst_opt.data(),
                                               ( int )dstStride, ( int )width, ( int )height, pCoeff );
  }

  std::ostringstream sstm_subtest;
  sstm_subtest << sstm_test.str() << " srcStride=" << srcStride << " dstStride=" << dstStride << " frac=" << frac;

  return compare_values_2d( sstm_subtest.str(), dst_ref.data(), dst_opt.data(), height, dstStride );
}

template<bool isLast, unsigned width>
static bool check_filterWxH_N8( InterpolationFilter* ref, InterpolationFilter* opt, unsigned height, unsigned num_cases )
{
  static_assert( width == 4 || width == 8 || width == 16, "Width must be either 4, 8, or 16" );

  DimensionGenerator dim;

  // Max buffer size for src is ( height + 7 ) * srcStride.
  std::vector<Pel> src( ( MAX_CU_SIZE + 7 ) * ( MAX_CU_SIZE + 7 ) );
  std::vector<Pel> dst_ref( MAX_CU_SIZE * MAX_CU_SIZE );
  std::vector<Pel> dst_opt( MAX_CU_SIZE * MAX_CU_SIZE );

  bool passed = true;

  // Test 8-bit and 10-bit.
  for( unsigned bd : { 8, 10 } )
  {
    ClpRng clpRng{ ( int )bd };

    InputGenerator<Pel> inp_gen{ bd, /*is_signed=*/false };

    std::ostringstream sstm_test;
    sstm_test << "InterpolationFilter::filter" << width << "x" << height << "[0][" << isLast << "]"
              << " bitDepth=" << bd;
    std::cout << "Testing " << sstm_test.str() << std::endl;

    for( unsigned n = 0; n < num_cases; n++ )
    {
      unsigned srcStride = dim.get( width, MAX_CU_SIZE ) + 7; // srcStride >= width + 7
      unsigned dstStride = dim.get( width, MAX_CU_SIZE );

      const TFilterCoeff *pCoeffH, *pCoeffV;
      if( width == 4 )
      {
        unsigned hCoeff_idx = dim.get( 0, LUMA_INTERPOLATION_FILTER_SUB_SAMPLE_POSITIONS - 1 );
        unsigned vCoeff_idx = dim.get( 0, LUMA_INTERPOLATION_FILTER_SUB_SAMPLE_POSITIONS - 1 );
        pCoeffH = InterpolationFilter::m_lumaFilter4x4[hCoeff_idx];
        pCoeffV = InterpolationFilter::m_lumaFilter4x4[vCoeff_idx];
      }
      else // Include lumaAltHpelIFilter for other widths.
      {
        unsigned hCoeff_idx = dim.get( 0, LUMA_INTERPOLATION_FILTER_SUB_SAMPLE_POSITIONS );
        unsigned vCoeff_idx = dim.get( 0, LUMA_INTERPOLATION_FILTER_SUB_SAMPLE_POSITIONS );
        pCoeffH = hCoeff_idx == LUMA_INTERPOLATION_FILTER_SUB_SAMPLE_POSITIONS
                      ? InterpolationFilter::m_lumaAltHpelIFilter
                      : InterpolationFilter::m_lumaFilter[hCoeff_idx];
        pCoeffV = vCoeff_idx == LUMA_INTERPOLATION_FILTER_SUB_SAMPLE_POSITIONS
                      ? InterpolationFilter::m_lumaAltHpelIFilter
                      : InterpolationFilter::m_lumaFilter[vCoeff_idx];
      }

      // Fill input buffers with unsigned data.
      std::generate( src.begin(), src.end(), inp_gen );

      // Clear output blocks.
      std::fill( dst_ref.begin(), dst_ref.end(), 0 );
      std::fill( dst_opt.begin(), dst_opt.end(), 0 );

      ptrdiff_t src_offset = 3 * ( 1 + srcStride );

      if( width == 4 )
      {
        ref->m_filter4x4[0][isLast]( clpRng, src.data() + src_offset, ( int )srcStride, dst_ref.data(),
                                     ( int )dstStride, ( int )width, ( int )height, pCoeffH, pCoeffV );
        opt->m_filter4x4[0][isLast]( clpRng, src.data() + src_offset, ( int )srcStride, dst_opt.data(),
                                     ( int )dstStride, ( int )width, ( int )height, pCoeffH, pCoeffV );
      }
      else if( width == 8 )
      {
        ref->m_filter8xH[0][isLast]( clpRng, src.data() + src_offset, ( int )srcStride, dst_ref.data(),
                                     ( int )dstStride, ( int )width, ( int )height, pCoeffH, pCoeffV );
        opt->m_filter8xH[0][isLast]( clpRng, src.data() + src_offset, ( int )srcStride, dst_opt.data(),
                                     ( int )dstStride, ( int )width, ( int )height, pCoeffH, pCoeffV );
      }
      else // width == 16
      {
        ref->m_filter16xH[0][isLast]( clpRng, src.data() + src_offset, ( int )srcStride, dst_ref.data(),
                                       ( int )dstStride, ( int )width, ( int )height, pCoeffH, pCoeffV );
        opt->m_filter16xH[0][isLast]( clpRng, src.data() + src_offset, ( int )srcStride, dst_opt.data(),
                                       ( int )dstStride, ( int )width, ( int )height, pCoeffH, pCoeffV );
      }

      std::ostringstream sstm_subtest;
      sstm_subtest << sstm_test.str() << " srcStride=" << srcStride << " dstStride=" << dstStride;

      passed =
          compare_values_2d( sstm_subtest.str(), dst_ref.data(), dst_opt.data(), height, width, dstStride ) && passed;
    }
  }

  return passed;
}

template<bool isLast, unsigned width>
static bool check_filterWxH_N4( InterpolationFilter* ref, InterpolationFilter* opt, unsigned height, unsigned num_cases )
{
  static_assert( width == 4 || width == 8 || width == 16, "Width must be either 4, 8, or 16" );

  DimensionGenerator dim;

  std::vector<Pel> src( MAX_CU_SIZE * MAX_CU_SIZE );
  std::vector<Pel> dst_ref( MAX_CU_SIZE * MAX_CU_SIZE );
  std::vector<Pel> dst_opt( MAX_CU_SIZE * MAX_CU_SIZE );

  bool passed = true;

  // Test 8-bit and 10-bit.
  for( unsigned bd : { 8, 10 } )
  {
    ClpRng clpRng{ ( int )bd };

    InputGenerator<Pel> inp_gen{ bd, /*is_signed=*/false };

    std::ostringstream sstm_test;
    sstm_test << "InterpolationFilter::filter" << width << "x" << height << "[1][" << isLast << "]"
              << " bitDepth=" << bd;
    std::cout << "Testing " << sstm_test.str() << std::endl;

    for( unsigned n = 0; n < num_cases; n++ )
    {
      unsigned srcStride = dim.get( width + 3, MAX_CU_SIZE ); // srcStride >= width + 3
      unsigned dstStride = dim.get( width, MAX_CU_SIZE );

      unsigned hCoeff_idx = dim.get( 0, CHROMA_INTERPOLATION_FILTER_SUB_SAMPLE_POSITIONS );
      unsigned vCoeff_idx = dim.get( 0, CHROMA_INTERPOLATION_FILTER_SUB_SAMPLE_POSITIONS );
      const TFilterCoeff* pCoeffH = InterpolationFilter::m_chromaFilter[hCoeff_idx];
      const TFilterCoeff* pCoeffV = InterpolationFilter::m_chromaFilter[vCoeff_idx];

      // Fill input buffers with unsigned data.
      std::generate( src.begin(), src.end(), inp_gen );

      // Clear output blocks.
      std::fill( dst_ref.begin(), dst_ref.end(), 0 );
      std::fill( dst_opt.begin(), dst_opt.end(), 0 );

      ptrdiff_t src_offset = 1 + srcStride;

      if( width == 4 )
      {
        ref->m_filter4x4[1][isLast]( clpRng, src.data() + src_offset, ( int )srcStride, dst_ref.data(),
                                     ( int )dstStride, ( int )width, ( int )height, pCoeffH, pCoeffV );
        opt->m_filter4x4[1][isLast]( clpRng, src.data() + src_offset, ( int )srcStride, dst_opt.data(),
                                     ( int )dstStride, ( int )width, ( int )height, pCoeffH, pCoeffV );
      }
      else if( width == 8 )
      {
        ref->m_filter8xH[1][isLast]( clpRng, src.data() + src_offset, ( int )srcStride, dst_ref.data(),
                                     ( int )dstStride, ( int )width, ( int )height, pCoeffH, pCoeffV );
        opt->m_filter8xH[1][isLast]( clpRng, src.data() + src_offset, ( int )srcStride, dst_opt.data(),
                                     ( int )dstStride, ( int )width, ( int )height, pCoeffH, pCoeffV );
      }
      else // width == 16
      {
        ref->m_filter16xH[1][isLast]( clpRng, src.data() + src_offset, ( int )srcStride, dst_ref.data(),
                                       ( int )dstStride, ( int )width, ( int )height, pCoeffH, pCoeffV );
        opt->m_filter16xH[1][isLast]( clpRng, src.data() + src_offset, ( int )srcStride, dst_opt.data(),
                                       ( int )dstStride, ( int )width, ( int )height, pCoeffH, pCoeffV );
      }

      std::ostringstream sstm_subtest;
      sstm_subtest << sstm_test.str() << " srcStride=" << srcStride << " dstStride=" << dstStride;

      passed =
          compare_values_2d( sstm_subtest.str(), dst_ref.data(), dst_opt.data(), height, width, dstStride ) && passed;
    }
  }

  return passed;
}

template<unsigned isFirst, unsigned isLast>
static bool check_filterCopy( InterpolationFilter* ref, InterpolationFilter* opt, unsigned num_cases, bool biMCForDMVR )
{
  DimensionGenerator dim;

  static constexpr size_t buf_size = MAX_CU_SIZE * MAX_CU_SIZE;

  std::vector<Pel> src( buf_size );
  std::vector<Pel> dst_ref( buf_size );
  std::vector<Pel> dst_opt( buf_size );

  bool passed = true;

  // Test unsigned 8-bit and 10-bit.
  for( unsigned bd : { 8, 10 } )
  {
    ClpRng clpRng{ ( int )bd };
    InputGenerator<Pel> inp_gen{ bd, /*is_signed=*/false };

    std::ostringstream sstm_test;
    sstm_test << "InterpolationFilter::filterCopy[" << isFirst << "][" << isLast << "]"
              << " biMCForDMVR=" << std::boolalpha << biMCForDMVR << " bitDepth=" << bd;
    std::cout << "Testing " << sstm_test.str() << std::endl;

    for( unsigned n = 0; n < num_cases; n++ )
    {
      unsigned height    = dim.get( 1, MAX_CU_SIZE );
      unsigned width     = dim.get( 1, MAX_CU_SIZE );
      unsigned srcStride = dim.get( width, MAX_CU_SIZE );
      unsigned dstStride = dim.get( width, MAX_CU_SIZE );

      // Fill input buffers with unsigned data.
      std::generate( src.begin(), src.end(), inp_gen );

      // Clear output blocks.
      std::fill( dst_ref.begin(), dst_ref.end(), 0 );
      std::fill( dst_opt.begin(), dst_opt.end(), 0 );

      ref->m_filterCopy[isFirst][isLast]( clpRng, src.data(), ( int )srcStride, dst_ref.data(), ( int )dstStride,
                                          ( int )width, ( int )height, biMCForDMVR );
      opt->m_filterCopy[isFirst][isLast]( clpRng, src.data(), ( int )srcStride, dst_opt.data(), ( int )dstStride,
                                          ( int )width, ( int )height, biMCForDMVR );

      std::ostringstream sstm_subtest;
      sstm_subtest << sstm_test.str() << " srcStride=" << srcStride << " dstStride=" << dstStride << " w=" << width
                   << " h=" << height;

      passed =
          compare_values_2d( sstm_subtest.str(), dst_ref.data(), dst_opt.data(), height, width, dstStride ) && passed;
    }
  }

  return passed;
}

template<typename G>
static bool check_one_xWeightedGeoBlk( InterpolationFilter* ref, InterpolationFilter* opt, int src0Stride,
                                       int src1Stride, int dstStride, int width, int height, int bitDepth,
                                       G input_generator )
{
  CHECK( src0Stride < width, "Src0tride must be greater than or equal to width" );
  CHECK( src1Stride < width, "Src1tride must be greater than or equal to width" );
  CHECK( dstStride < width, "DstStride must be greater than or equal to width" );

  std::ostringstream sstm;
  sstm << "xWeightedGeoBlk src0Stride=" << src0Stride << "src1Stride=" << src1Stride << " dstStride=" << dstStride
       << " w=" << width << " h=" << height;

  std::vector<Pel> src0( src0Stride * height );
  std::vector<Pel> src1( src1Stride * height );
  std::vector<Pel> dst_ref( dstStride * height );
  std::vector<Pel> dst_opt( dstStride * height );

  // Initialize source buffers.
  std::generate( src0.begin(), src0.end(), input_generator );
  std::generate( src1.begin(), src1.end(), input_generator );

  DimensionGenerator rng;

  auto allChromaLuma = { VVENC_CHROMA_400, VVENC_CHROMA_420, VVENC_CHROMA_422, VVENC_CHROMA_444 };
  auto halfResolutionChroma = { VVENC_CHROMA_420, VVENC_CHROMA_422 };
  auto fullResolutionChromaLuma = { VVENC_CHROMA_400, VVENC_CHROMA_444 };

  // Smallest luma block used is 8 pixels and if width is 4, it will occur to chroma only.
  ChromaFormat chromaFormat =
      width == 4 ? rng.getOneOf<ChromaFormat>( halfResolutionChroma ) : rng.getOneOf<ChromaFormat>( allChromaLuma );
  // 422 or 444, height must be >= 8 (else out of bound access at geo offset table).
  chromaFormat = height == 4 ? VVENC_CHROMA_420 : chromaFormat;
  // Limit luma width/height in range 64 (else out of bound access at geo offset table).
  chromaFormat = width == 64 || height == 64 ? rng.getOneOf<ChromaFormat>( fullResolutionChromaLuma ) : chromaFormat;

  auto compLuma = { COMP_Y };
  auto compChroma = { COMP_Cb, COMP_Cr };
  // Only luma plane exists for 400.
  ComponentID compIdx = chromaFormat == VVENC_CHROMA_400 ? rng.getOneOf<ComponentID>( compLuma )
                                                         : rng.getOneOf<ComponentID>( compChroma );

  // VVENC_CHROMA_420 : Chroma at half resolution in both directions.
  // VVENC_CHROMA_422 : Chroma at half horizontal resolution, full vertical resolution.
  SizeType luma_width = chromaFormat == VVENC_CHROMA_420 || chromaFormat == VVENC_CHROMA_422 ? width * 2 : width;
  SizeType luma_height = chromaFormat == VVENC_CHROMA_420 ? height * 2 : height;

  CHECK( luma_width > 64, "luma_width out of bounds for geo weight offset tables" );
  CHECK( luma_height > 64, "luma_height out of bounds for geo weight offset tables" );
  CHECK( luma_width < 8, "luma_width out of bounds for geo weight offset tables" );
  CHECK( luma_height < 8, "luma_height out of bounds for geo weight offset tables" );

  const UnitArea localUnitArea{ chromaFormat, Area( 0, 0, luma_width, luma_height ) };
  const CodingUnit cu{ localUnitArea };

  const uint8_t splitDir = rng.get( 0, GEO_NUM_PARTITION_MODE - 1 );

  ClpRngs clpRng;
  clpRng.bd = bitDepth;

  Size sz{ luma_width, luma_height }; // Unused in weightedGeoBlk.
  AreaBuf<Pel> areaBufDst_ref{ dst_ref.data(), dstStride, sz };
  AreaBuf<Pel> areaBufDst_opt{ dst_opt.data(), dstStride, sz };
  AreaBuf<Pel> areaBufSrc0{ src0.data(), src0Stride, sz };
  AreaBuf<Pel> areaBufSrc1{ src1.data(), src1Stride, sz };

  // Give all three planes same buffer, as only one of them is active in weightedGeoBlk.
  PelUnitBuf dstUnitBuf_opt{
      chromaFormat,
      areaBufDst_opt, // COMP_Y
      areaBufDst_opt, // COMP_Cb
      areaBufDst_opt  // COMP_Cr
  };
  PelUnitBuf dstUnitBuf_ref{ chromaFormat, areaBufDst_ref, areaBufDst_ref, areaBufDst_ref };
  PelUnitBuf src0UnitBuf{ chromaFormat, areaBufSrc0, areaBufSrc0, areaBufSrc0 };
  PelUnitBuf src1UnitBuf{ chromaFormat, areaBufSrc1, areaBufSrc1, areaBufSrc1 };

  ref->m_weightedGeoBlk( clpRng, cu, width, height, compIdx, splitDir, dstUnitBuf_ref, src0UnitBuf, src1UnitBuf );
  opt->m_weightedGeoBlk( clpRng, cu, width, height, compIdx, splitDir, dstUnitBuf_opt, src0UnitBuf, src1UnitBuf );
  return compare_values_2d( sstm.str(), dst_ref.data(), dst_opt.data(), height, width, dstStride );
}

static bool check_xWeightedGeoBlk( InterpolationFilter* ref, InterpolationFilter* opt, unsigned num_cases, int width,
                                   int height )
{
  printf( "Testing InterpolationFilter::xWeightedGeoBlk w=%d h=%d\n", width, height );
  InputGenerator<TCoeff> g{ 14 };
  DimensionGenerator rng;

  for( unsigned i = 0; i < num_cases; ++i )
  {
    for( int bitDepth : { 8, 10 } )
    {
      unsigned src0Stride = rng.get( width, MAX_CU_SIZE );
      unsigned src1Stride = rng.get( width, MAX_CU_SIZE );
      unsigned dstStride  = rng.get( width, MAX_CU_SIZE );
      if( !check_one_xWeightedGeoBlk( ref, opt, src0Stride, src1Stride, dstStride, width, height, bitDepth, g ) )
      {
        return false;
      }
    }
  }

  return true;
}

static bool test_InterpolationFilter()
{
  InterpolationFilter ref;
  InterpolationFilter opt;

  ref.initInterpolationFilter( /*enable=*/false );
  opt.initInterpolationFilter( /*enable=*/true );

  unsigned num_cases = NUM_CASES;
  bool passed = true;

  for( int height : { 4, 8, 16, 32, 64 } )
  {
    for( int width : { 4, 8, 16, 32, 64 } )
    {
      // Skip invalid 4×64 and 64×4 cases.
      if( ( width == 4 && height == 64 ) || ( width == 64 && height == 4 ) )
      {
        continue;
      }
      passed = check_xWeightedGeoBlk( &ref, &opt, num_cases, width, height ) && passed;
    }
  }

  for( unsigned height : { 1, 4, 8, 16, 32, 64, 128 } )
  {
    for( unsigned width : { 1, 4, 8, 16, 32, 64, 128 } )
    {
      // Luma 6-tap
      passed = check_filter<6, 0, 1, 0>( &ref, &opt, width, height + 5 ) && passed; // height + N - 1
      passed = check_filter<6, 0, 1, 1>( &ref, &opt, width, height ) && passed;
      passed = check_filter<6, 1, 0, 0>( &ref, &opt, width, height ) && passed;
      passed = check_filter<6, 1, 0, 1>( &ref, &opt, width, height ) && passed;
      passed = check_filter<6, 1, 1, 0>( &ref, &opt, width, height ) && passed;
      passed = check_filter<6, 1, 1, 1>( &ref, &opt, width, height ) && passed;
      // Luma 8-tap
      passed = check_filter<8, 0, 1, 0>( &ref, &opt, width, height + 7 ) && passed; // height + N - 1
      passed = check_filter<8, 0, 1, 1>( &ref, &opt, width, height ) && passed;
      passed = check_filter<8, 1, 0, 0>( &ref, &opt, width, height ) && passed;
      passed = check_filter<8, 1, 0, 1>( &ref, &opt, width, height ) && passed;
      passed = check_filter<8, 1, 1, 0>( &ref, &opt, width, height ) && passed;
      passed = check_filter<8, 1, 1, 1>( &ref, &opt, width, height ) && passed;
    }
  }
  for( unsigned height : { 1, 2, 4, 8, 16, 32, 64, 128 } )
  {
    for( unsigned width : { 1, 2, 4, 8, 12, 16, 20, 24, 32, 64, 128 } )
    {
      // Chroma 4-tap
      passed = check_filter<4, 0, 1, 0>( &ref, &opt, width, height + 3 ) && passed; // height + N - 1
      passed = check_filter<4, 0, 1, 1>( &ref, &opt, width, height ) && passed;
      passed = check_filter<4, 1, 0, 0>( &ref, &opt, width, height ) && passed;
      passed = check_filter<4, 1, 0, 1>( &ref, &opt, width, height ) && passed;
      passed = check_filter<4, 1, 1, 0>( &ref, &opt, width, height ) && passed;
      passed = check_filter<4, 1, 1, 1>( &ref, &opt, width, height ) && passed;
    }
  }
  for( unsigned height : { 8, 16, 32, 64, 128 } )
  {
    for( unsigned width : { 8, 16, 32, 64, 128 } )
    {
      // Bilinear 2-tap: height and width are padded by four.
      passed = check_filter<2, 0, 1, 0>( &ref, &opt, width + 4, height + 4 ) && passed;
      passed = check_filter<2, 1, 1, 0>( &ref, &opt, width + 4, height + 4 ) && passed;
    }
  }

  // The width = 4 case is only called with height = 4.
  passed = check_filterWxH_N8<false, 4>( &ref, &opt, 4, num_cases ) && passed;
  passed = check_filterWxH_N8<true, 4>( &ref, &opt, 4, num_cases ) && passed;
  for( unsigned height : { 4, 8, 16, 32, 64, 128 } )
  {
    passed = check_filterWxH_N8<false, 8>( &ref, &opt, height, num_cases ) && passed;
    passed = check_filterWxH_N8<true, 8>( &ref, &opt, height, num_cases ) && passed;
    passed = check_filterWxH_N8<false, 16>( &ref, &opt, height, num_cases ) && passed;
    passed = check_filterWxH_N8<true, 16>( &ref, &opt, height, num_cases ) && passed;
  }

  // The width = 4 case is only called with height = 4.
  passed = check_filterWxH_N4<false, 4>( &ref, &opt, 4, num_cases ) && passed;
  passed = check_filterWxH_N4<true, 4>( &ref, &opt, 4, num_cases ) && passed;
  for( unsigned height : { 2, 4, 8, 16, 32 } )
  {
    passed = check_filterWxH_N4<false, 8>( &ref, &opt, height, num_cases ) && passed;
    passed = check_filterWxH_N4<true, 8>( &ref, &opt, height, num_cases ) && passed;
    passed = check_filterWxH_N4<false, 16>( &ref, &opt, height, num_cases ) && passed;
    passed = check_filterWxH_N4<true, 16>( &ref, &opt, height, num_cases ) && passed;
  }

  passed = check_filterCopy<0, 0>( &ref, &opt, num_cases, false ) && passed;
  passed = check_filterCopy<0, 1>( &ref, &opt, num_cases, false ) && passed;
  passed = check_filterCopy<1, 0>( &ref, &opt, num_cases, false ) && passed;
  passed = check_filterCopy<1, 1>( &ref, &opt, num_cases, false ) && passed;
  passed = check_filterCopy<1, 0>( &ref, &opt, num_cases, true ) && passed;

  return passed;
}
#endif // ENABLE_SIMD_OPT_MCIF

#if ENABLE_SIMD_OPT_ALF
#define MAX_NUM_ALF_TRANSPOSE_ID 4

template<typename G>
static bool check_one_filterBlk( AdaptiveLoopFilter* ref, AdaptiveLoopFilter* opt, int srcStride, int dstStride,
                                 unsigned int w, unsigned int h, G input_generator, unsigned int linearIndex )
{
  CHECK( srcStride < w, "SrcStride must be greater than or equal to width." );
  CHECK( dstStride < w, "DstStride must be greater than or equal to width." );

  std::ostringstream sstm;
  sstm << "filterBlk linearIndex=" << linearIndex << " srcStride=" << srcStride << " dstStride=" << dstStride
       << " w=" << w << " h=" << h;

  ClpRng clpRng{ 10 };

  DimensionGenerator rng;

  int dstX = rng.get( 0, MAX_CU_SIZE, 8 );
  int dstY = rng.get( 0, MAX_CU_SIZE, 8 );

  const Area blk{ 0, 0, w, h };
  const Area blkDst{ dstX, dstY, w, h };

  // Padding to src memory so that filterBlk can safely index [-3,+3] rows.
  constexpr int pad = 3;
  std::vector<Pel> src( ( blk.y + h + 2 * pad ) * srcStride + blk.x + 2 * pad );
  std::vector<Pel> dst_ref( ( blkDst.y + h ) * dstStride + blkDst.x );
  std::vector<Pel> dst_opt( ( blkDst.y + h ) * dstStride + blkDst.x );
  std::generate( src.begin(), src.end(), input_generator );

  Size sz{ w, h };
  AreaBuf<const Pel> areaBufSrc{ src.data() + pad * srcStride + pad, srcStride, sz };
  AreaBuf<Pel> areaBufDst_ref{ dst_ref.data(), dstStride, sz };
  AreaBuf<Pel> areaBufDst_opt{ dst_opt.data(), dstStride, sz };

  XUCache xuCache;
  std::mutex csMutex;
  CodingStructure cs{ xuCache, &csMutex }; // Dummy CodingStructure for filterBlk (unused).

  ComponentID compId = COMP_Y;
  ChromaFormat chromaFormat = CHROMA_400;

  // Give all three planes same buffer, as only one of them is active in filterBlk.
  PelUnitBuf dstUnitBuf_ref{
      chromaFormat,
      areaBufDst_ref, // COMP_Y
      areaBufDst_ref, // COMP_Cb
      areaBufDst_ref  // COMP_Cr
  };
  PelUnitBuf dstUnitBuf_opt{ chromaFormat, areaBufDst_opt, areaBufDst_opt, areaBufDst_opt };
  CPelUnitBuf srcUnitBuf{ chromaFormat, areaBufSrc, areaBufSrc, areaBufSrc };

  const int numClassBlocksInCTU = ( MAX_CU_SIZE * MAX_CU_SIZE ) >> 4;
  std::vector<AlfClassifier> classifier;
  for( unsigned i = 0; i < numClassBlocksInCTU; ++i )
  {
    const uint8_t classIdx = rng.get( 0, MAX_NUM_ALF_CLASSES - 1 );
    const uint8_t transposeIdx = rng.get( 0, MAX_NUM_ALF_TRANSPOSE_ID - 1 );
    classifier.emplace_back( classIdx, transposeIdx );
  }

  const int vbCTUHeight = rng.getOneOf<int>( { 32, 64, 128 } );
  const int vbPos = vbCTUHeight - ALF_VB_POS_ABOVE_CTUROW_LUMA;

  // Build coefficient and clip arrays for all classes and transpose variants.
  constexpr size_t LumaSz = MAX_NUM_ALF_TRANSPOSE_ID * MAX_NUM_ALF_CLASSES * MAX_NUM_ALF_LUMA_COEFF;
  std::vector<short> coeffLuma( LumaSz );
  std::vector<short> clipLuma( LumaSz );

  for( unsigned t = 0; t < MAX_NUM_ALF_TRANSPOSE_ID; ++t )
  {
    for( unsigned c = 0; c < MAX_NUM_ALF_CLASSES; ++c )
    {
      int offset = ( t * MAX_NUM_ALF_CLASSES + c ) * MAX_NUM_ALF_LUMA_COEFF;
      auto coeff_idx = rng.get( 0, ALF_FIXED_FILTER_NUM - 1 );
      for( unsigned i = 0; i < MAX_NUM_ALF_LUMA_COEFF; ++i )
      {
        coeffLuma[offset + i] = static_cast<short>( AdaptiveLoopFilter::m_fixedFilterSetCoeff[coeff_idx][i] );
        // In optimized versions, clipping is used only when isNonLinear is true.
        if( linearIndex == 1 )
        {
          clipLuma[offset + i] = rng.getOneOf<short>( { 1024, 128, 64, 32 } );
        }
        else
        {
          clipLuma[offset + i] = INT16_MAX;
        }
      }
    }
  }

  ref->m_filter7x7Blk[linearIndex]( classifier.data(), dstUnitBuf_ref, srcUnitBuf, blkDst, blk, compId,
                                    coeffLuma.data(), clipLuma.data(), clpRng, cs, vbCTUHeight, vbPos );
  opt->m_filter7x7Blk[linearIndex]( classifier.data(), dstUnitBuf_opt, srcUnitBuf, blkDst, blk, compId,
                                    coeffLuma.data(), clipLuma.data(), clpRng, cs, vbCTUHeight, vbPos );

  return compare_values_2d( sstm.str(), dst_ref.data(), dst_opt.data(), h + blkDst.y, w, dstStride );
}

static bool check_filterBlk( AdaptiveLoopFilter* ref, AdaptiveLoopFilter* opt, unsigned num_cases, int w, int h )
{
  printf( "Testing AdaptiveLoopFilter::filterBlk w=%d h=%d\n", w, h );

  DimensionGenerator rng;

  for( unsigned linearIndex : { 0, 1 } )
  {
    InputGenerator<TCoeff> g{ 10, /*is_signed=*/false };
    for( unsigned i = 0; i < num_cases; ++i )
    {
      // Stride is often the width of a video frame, so use the width of 8K as an upper bound.
      unsigned srcStride = rng.get( w, g_fastUnitTest ? 512 : 8192 );
      unsigned dstStride = rng.get( w, g_fastUnitTest ? 512 : 8192 );

      if( !check_one_filterBlk( ref, opt, srcStride, dstStride, w, h, g, linearIndex ) )
      {
        return false;
      }
    }
  }
  return true;
}

static bool test_AdaptiveLoopFilter()
{
  AdaptiveLoopFilter ref{ /*enableOpt=*/false };
  AdaptiveLoopFilter opt{ /*enableOpt=*/true };

  unsigned num_cases = NUM_CASES;
  bool passed = true;

  for( unsigned w : { 8, 16, 32, 48, 64, 128 } )
  {
    for( unsigned h : { 8, 16, 24, 32, 64, 112, 128 } )
    {
      passed = check_filterBlk( &ref, &opt, num_cases, w, h ) && passed;
    }
  }

  return passed;
}
#endif // ENABLE_SIMD_OPT_ALF

struct UnitTestEntry
{
  std::string name;
  bool ( *fn )();
};

static const UnitTestEntry test_suites[] = {
#if ENABLE_SIMD_OPT_INTRAPRED
    { "IntraPred", test_IntraPred },
#endif
#if ENABLE_SIMD_TRAFO
    { "TCoeffOps", test_TCoeffOps },
#endif
#if ENABLE_SIMD_OPT_MCTF
    { "MCTF", test_MCTF },
#endif
#if ENABLE_SIMD_OPT_BDOF
    { "InterPred", test_InterPred },
#endif
#if ENABLE_SIMD_OPT_DIST
    { "RdCost", test_RdCost },
#endif
#if ENABLE_SIMD_OPT_AFFINE_ME
    { "AffineGradientSearch", test_AffineGradientSearch },
#endif
#if ENABLE_SIMD_OPT_BUFFER
    { "PelBufferOps", test_PelBufferOps },
#endif
#if ENABLE_SIMD_OPT_MCIF
    { "InterpolationFilter", test_InterpolationFilter },
#endif
#if ENABLE_SIMD_OPT_ALF
    { "ALF", test_AdaptiveLoopFilter },
#endif
};

struct UnitTestArgs
{
  bool isFast = false;
  bool show_help = false;
  int seed;
  std::string testcase;
};

static inline std::string get_testcase_help_text()
{
  std::ostringstream sstm;
  sstm << "Run a single test suite. One of: ";
  bool first = true;
  for( const auto& entry : test_suites )
  {
    if( !first )
    { 
      sstm << ", ";
    }
    first = false;
    sstm << entry.name;
  }
  return sstm.str();
}

UnitTestArgs parse_args( int argc, char* argv[] )
{
  UnitTestArgs args;
  args.seed = ( unsigned )time( NULL );

  po::Options opts;
  opts.addOptions()
    ( "help,h", args.show_help, "Show help", true )
    ( "seed", args.seed, "Set random seed for running tests" )
    ( "testcase,t", args.testcase, get_testcase_help_text(), false )
    ( "fast", args.isFast, "Run a fast but less real-world accurate version of the tests", false );

  po::SilentReporter err;
  po::scanArgv( opts, argc, ( const char** )argv, err );

  g_fastUnitTest = args.isFast;

  if( args.show_help )
  {
    std::ostringstream help_sstm;
    po::doHelp( help_sstm, opts );
    std::cout << help_sstm.str() << "\n";
    exit( EXIT_SUCCESS );
  }

  return args;
}

int main( int argc, char* argv[] )
{
  UnitTestArgs args = parse_args( argc, argv );

  srand( args.seed );
  std::cout << "Running unit tests with seed=" << args.seed << ".\n\n";

  bool passed = true;

  for( const auto& entry : test_suites )
  {
    if( args.testcase == "" || args.testcase == entry.name )
    {
      std::cout << "Running test suite: " << entry.name << "\n";
      passed = entry.fn() && passed;
    }
  }

  if( !passed )
  {
    printf( "\nerror: some tests failed for seed=%u!\n\n", args.seed );
    exit( EXIT_FAILURE );
  }
  printf( "\nsuccess: all tests passed!\n\n" );
}
