/* The copyright in this software is being made available under the BSD
 * License, included below. This software may be subject to other third party
 * and contributor rights, including patent rights, and no such rights are
 * granted under this license.
 *
 * Copyright (c) 2010-2023, ITU/ISO/IEC
 * 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 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 (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE.
 */

/** \file     InterSearch.h
    \brief    inter search class (header)
 */

#ifndef __INTERSEARCH__
#define __INTERSEARCH__

// Include files
#include "CABACWriter.h"
#include "EncCfg.h"

#include "CommonLib/MotionInfo.h"
#include "CommonLib/InterPrediction.h"
#include "CommonLib/TrQuant.h"
#include "CommonLib/Unit.h"
#include "CommonLib/UnitPartitioner.h"
#include "CommonLib/RdCost.h"
#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
#include "CommonLib/BilateralFilter.h"
#endif

#include "CommonLib/AffineGradientSearch.h"
#include "CommonLib/IbcHashMap.h"
#include "CommonLib/Hash.h"
#include <unordered_map>
#include <vector>
#include "EncReshape.h"
//! \ingroup EncoderLib
//! \{

// ====================================================================================================================
// Class definition
// ====================================================================================================================

static const uint32_t MAX_NUM_REF_LIST_ADAPT_SR = 2;
static const uint32_t MAX_IDX_ADAPT_SR          = 33;
static const uint32_t NUM_MV_PREDICTORS         = 3;
struct BlkRecord
{
  std::unordered_map<Mv, Distortion> bvRecord;
};
class EncModeCtrl;

struct AffineMVInfo
{
  Mv  affMVs[2][33][3];
  int x, y, w, h;
};

struct BlkUniMvInfo
{
  Mv uniMvs[2][33];
  int x, y, w, h;
};

typedef struct
{
  Mv acMvAffine4Para[2][3];
  Mv acMvAffine6Para[2][3];
  int16_t affine4ParaRefIdx[2];
  int16_t affine6ParaRefIdx[2];
  Distortion hevcCost[3];
  Distortion affineCost[3];
  bool affine4ParaAvail;
  bool affine6ParaAvail;
} EncAffineMotion;

#if MERGE_ENC_OPT
struct ModeInfo
{
  uint32_t mergeCand;
  bool     isRegularMerge;
  bool     isMMVD;
  bool     isCIIP;
#if CIIP_PDPC
  bool     isCiipPDPC;
#endif
#if JVET_AG0135_AFFINE_CIIP
  bool     isCiipAffine;
#endif
#if JVET_X0141_CIIP_TIMD_TM && JVET_W0123_TIMD_FUSION
  int      intraMode;
#endif
  bool     isAffine;
#if AFFINE_MMVD
  bool     isAffineMmvd;
#endif
#if TM_MRG
  bool     isTMMrg;
#endif
#if JVET_AG0276_LIC_FLAG_SIGNALING
  bool     isTMMrgOppositeLic;
  bool     isOppositeLic;
  bool     isAffOppositeLic;
#endif
#if JVET_X0049_ADAPT_DMVR
  bool     isBMMrg;
  uint8_t  bmDir;
#endif
#if JVET_AD0182_AFFINE_DMVR_PLUS_EXTENSIONS
  bool isAffBMMrg;
  uint8_t affBMDir;
#endif
#if JVET_AA0070_RRIBC
  int rribcFlipType;
#endif
  bool     isGeo;
  uint8_t     geoSplitDir;
  uint8_t     geoMergeIdx0;
  uint8_t     geoMergeIdx1;
#if ENABLE_OBMC
  bool      isOBMC;
#endif
  ModeInfo() : mergeCand(0), isRegularMerge(false), isMMVD(false)
    , isCIIP(false)
#if CIIP_PDPC
    , isCiipPDPC(false)
#endif
#if JVET_AG0135_AFFINE_CIIP
    , isCiipAffine(false)
#endif
#if JVET_X0141_CIIP_TIMD_TM && JVET_W0123_TIMD_FUSION
    , intraMode(0)
#endif
    , isAffine(false)
#if AFFINE_MMVD
    , isAffineMmvd(false)
#endif
#if TM_MRG
    , isTMMrg(false)
#endif
#if JVET_AG0276_LIC_FLAG_SIGNALING
    , isTMMrgOppositeLic(false)
    , isOppositeLic(false)
    , isAffOppositeLic(false)
#endif
#if JVET_X0049_ADAPT_DMVR
    , isBMMrg(false)
    , bmDir(0)
#endif
#if JVET_AD0182_AFFINE_DMVR_PLUS_EXTENSIONS
    , isAffBMMrg(false)
    , affBMDir(0)
#endif
#if JVET_AA0070_RRIBC
    , rribcFlipType(0)
#endif
  , isGeo(false), geoSplitDir(0), geoMergeIdx0(0), geoMergeIdx1(0)
#if ENABLE_OBMC
    , isOBMC(false)
#endif
  {}
  ModeInfo(const uint32_t mergeCand, const bool isRegularMerge, const bool isMMVD, const bool isCIIP
#if CIIP_PDPC
    , const bool isCiipPDPC
#endif
#if JVET_AG0135_AFFINE_CIIP
    , const bool isCiipAffine
#endif
#if JVET_X0141_CIIP_TIMD_TM && JVET_W0123_TIMD_FUSION
    , const int intraMode
#endif
    , const bool isAffine
#if ENABLE_OBMC
    , const bool isOBMC = false
#endif
#if AFFINE_MMVD
    , const bool isAffineMmvd = false
#endif
#if TM_MRG
    , const bool isTMMrg = false
#endif
#if JVET_AG0276_LIC_FLAG_SIGNALING
    , const bool isTMMrgOppositeLic = false
    , const bool isOppositeLic = false
    , const bool isAffOppositeLic = false
#endif
  ) :
    mergeCand(mergeCand), isRegularMerge(isRegularMerge), isMMVD(isMMVD), isCIIP(isCIIP)
#if CIIP_PDPC
    , isCiipPDPC(isCiipPDPC)
#endif
#if JVET_AG0135_AFFINE_CIIP
    , isCiipAffine(isCiipAffine)
#endif
#if JVET_X0141_CIIP_TIMD_TM && JVET_W0123_TIMD_FUSION
    , intraMode(intraMode)
#endif
    , isAffine(isAffine)
#if AFFINE_MMVD
    , isAffineMmvd(isAffineMmvd)
#endif
#if TM_MRG
    , isTMMrg(isTMMrg)
#endif
#if JVET_AG0276_LIC_FLAG_SIGNALING
    , isTMMrgOppositeLic(isTMMrgOppositeLic)
    , isOppositeLic(isOppositeLic)
    , isAffOppositeLic(isAffOppositeLic)
#endif
#if JVET_X0049_ADAPT_DMVR
    , isBMMrg( false )
    , bmDir( 0 )
#endif
#if JVET_AD0182_AFFINE_DMVR_PLUS_EXTENSIONS
    , isAffBMMrg(false)
    , affBMDir(0)
#endif
    , isGeo(false), geoSplitDir(0), geoMergeIdx0(0), geoMergeIdx1(0)
#if ENABLE_OBMC
    , isOBMC(false)
#endif
  {}
  ModeInfo(const CodingUnit cu, const PredictionUnit pu)
  {
#if AFFINE_MMVD
#if JVET_Y0067_ENHANCED_MMVD_MVD_SIGN_PRED
    mergeCand = pu.afMmvdFlag ?  pu.afMmvdMergeIdx : pu.mergeIdx;
#if JVET_AA0132_CONFIGURABLE_TM_TOOLS
    mergeCand = !pu.cs->sps->getUseTMMMVD() && pu.afMmvdFlag ? pu.afMmvdBaseIdx * ECM3_AF_MMVD_MAX_REFINE_NUM + pu.afMmvdStep * ECM3_AF_MMVD_OFFSET_DIR + pu.afMmvdDir : mergeCand;
#endif
#else
    mergeCand = pu.afMmvdFlag ? pu.afMmvdBaseIdx * AF_MMVD_MAX_REFINE_NUM + pu.afMmvdStep * AF_MMVD_OFFSET_DIR + pu.afMmvdDir : pu.mergeIdx;
#endif
#else
    mergeCand = pu.mergeIdx;
#endif
    isRegularMerge = pu.regularMergeFlag;
    isMMVD = pu.mmvdMergeFlag || cu.mmvdSkip;
    isCIIP = pu.ciipFlag;
#if CIIP_PDPC
    isCiipPDPC = pu.ciipPDPC;
#endif
#if JVET_AG0135_AFFINE_CIIP
    isCiipAffine = pu.ciipAffine;
#endif
#if JVET_X0141_CIIP_TIMD_TM && JVET_W0123_TIMD_FUSION
    intraMode = pu.intraDir[0];
#endif
    isAffine = cu.affine;
#if AFFINE_MMVD
    isAffineMmvd = pu.afMmvdFlag;
#endif
#if TM_MRG
    isTMMrg = pu.tmMergeFlag;
#endif
#if JVET_AG0276_LIC_FLAG_SIGNALING
    isTMMrgOppositeLic = pu.tmMergeFlagOppositeLic;
    isOppositeLic = pu.mergeOppositeLic;
    isAffOppositeLic = pu.affineOppositeLic;
#endif
#if JVET_X0049_ADAPT_DMVR
    isBMMrg = pu.bmMergeFlag;
    bmDir = pu.bmDir;
#endif
#if JVET_AD0182_AFFINE_DMVR_PLUS_EXTENSIONS
    isAffBMMrg = pu.affBMMergeFlag;
    affBMDir = pu.affBMDir;
#endif
#if JVET_AA0070_RRIBC
    rribcFlipType = cu.rribcFlipType;
#endif
    isGeo = cu.geoFlag;
    geoSplitDir = pu.geoSplitDir;
    geoMergeIdx0 = pu.geoMergeIdx0;
    geoMergeIdx1 = pu.geoMergeIdx1;
#if ENABLE_OBMC
    isOBMC = cu.obmcFlag;
#endif
#if JVET_AG0112_REGRESSION_BASED_GPM_BLENDING
    CHECK(cu.geoBlendFlag && (pu.geoMergeIdx0 != pu.mergeIdx || pu.geoMergeIdx0 != mergeCand),"ModeInfo() failed.");
#endif
  }
};
#endif

#if JVET_AC0112_IBC_CIIP
struct ModeIbcInfo
{
#if JVET_AC0112_IBC_GPM
  uint32_t mergeCand;
  bool     isCIIP;
  int      dirIdx;
  bool     isIbcGpm;
  int      mergeIdx0;
  int      mergeIdx1;
  int      splitDir;
  int      bldIdx;
  int      combIdx;
#if JVET_AE0169_BIPREDICTIVE_IBC
  ModeIbcInfo() : mergeCand(0), isCIIP(false), dirIdx(0), isIbcGpm(false), mergeIdx0(MAX_UCHAR), mergeIdx1(MAX_UCHAR), splitDir(0), bldIdx(0), combIdx(0)
#else
  ModeIbcInfo() : mergeCand(0), isCIIP(false), dirIdx(0), isIbcGpm(false), mergeIdx0(0), mergeIdx1(0), splitDir(0), bldIdx(0), combIdx(0)
#endif
  {}
  ModeIbcInfo(const uint32_t mergeCand, const bool isCIIP, const int dirIdx, const bool isIbcGpm, const int mergeIdx0, const int mergeIdx1, const int splitDir, const int bldIdx, const int combIdx
  ) :
    mergeCand(mergeCand), isCIIP(isCIIP), dirIdx(dirIdx), isIbcGpm(isIbcGpm), mergeIdx0(mergeIdx0), mergeIdx1(mergeIdx1), splitDir(splitDir), bldIdx(bldIdx), combIdx(combIdx)
  {}
  ModeIbcInfo(const CodingUnit cu, const PredictionUnit pu)
  {
    mergeCand = pu.mergeIdx;
    isCIIP = pu.ibcCiipFlag;
    dirIdx = pu.ibcCiipIntraIdx;
    isIbcGpm = pu.ibcGpmFlag;
    mergeIdx0 = pu.ibcGpmMergeIdx0;
    mergeIdx1 = pu.ibcGpmMergeIdx1;
    splitDir = pu.ibcGpmSplitDir;
    bldIdx = pu.ibcGpmBldIdx;
  }
#else
  uint32_t mergeCand;
  bool     isCIIP;
  int      dirIdx;
  ModeIbcInfo() : mergeCand(0), isCIIP(false), dirIdx(0)
  {}
  ModeIbcInfo(const uint32_t mergeCand, const bool isCIIP, const int dirIdx
  ) :
    mergeCand(mergeCand), isCIIP(isCIIP), dirIdx(dirIdx)
  {}
  ModeIbcInfo(const CodingUnit cu, const PredictionUnit pu)
  {
    mergeCand = pu.mergeIdx;
    isCIIP = pu.ibcCiipFlag;
    dirIdx = pu.ibcCiipIntraIdx;
  }
#endif
};
#endif

#if JVET_AC0112_IBC_GPM && !JVET_AC0112_IBC_CIIP
struct ModeIbcInfo
{
  uint32_t mergeCand;
  bool     isIbcGpm;
  int      mergeIdx0;
  int      mergeIdx1;
  int      splitDir;
  int      bldIdx;
  int      combIdx;
  ModeIbcInfo() : mergeCand(0), isIbcGpm(false), mergeIdx0(0), mergeIdx1(0), splitDir(0), bldIdx(0), combIdx(0)
  {}
  ModeIbcInfo(const uint32_t mergeCand, const bool isIbcGpm, const int mergeIdx0, const int mergeIdx1, const int splitDir, const int bldIdx, const int combIdx
  ) :
    mergeCand(mergeCand), isIbcGpm(isIbcGpm), mergeIdx0(mergeIdx0), mergeIdx1(mergeIdx1), splitDir(splitDir), bldIdx(bldIdx), combIdx(combIdx)
  {}
  ModeIbcInfo(const CodingUnit cu, const PredictionUnit pu)
  {
    mergeCand = pu.mergeIdx;
    isIbcGpm = pu.ibcGpmFlag;
    mergeIdx0 = pu.ibcGpmMergeIdx0;
    mergeIdx1 = pu.ibcGpmMergeIdx1;
    splitDir = pu.ibcGpmSplitDir;
    bldIdx = pu.ibcGpmBldIdx;
  }
};
#endif

#if INTER_LIC
class EncFastLICCtrl
{
  double m_amvpRdBeforeLIC[NUM_IMV_MODES];

public:
  EncFastLICCtrl() { init(); }

  void init()
  {
    m_amvpRdBeforeLIC[IMV_OFF ] = std::numeric_limits<double>::max();
    m_amvpRdBeforeLIC[IMV_FPEL] = std::numeric_limits<double>::max();
    m_amvpRdBeforeLIC[IMV_4PEL] = std::numeric_limits<double>::max();
    m_amvpRdBeforeLIC[IMV_HPEL] = std::numeric_limits<double>::max();
  }

  bool skipRDCheckForLIC( bool isLIC
                         , int imv, double curBestRd
                         , uint32_t cuNumPel
  )
  {
    bool skipLIC = false;
    if (isLIC)
    {
      skipLIC |= skipLicBasedOnBestAmvpRDBeforeLIC(imv, curBestRd);
      skipLIC |= (cuNumPel < LIC_MIN_CU_PIXELS);
    }
    return skipLIC;
  }

public:
  void setBestAmvpRDBeforeLIC(const CodingUnit& cu, double curCuRdCost)
  {
    m_amvpRdBeforeLIC[cu.imv] = !cu.firstPU->mergeFlag && cu.predMode != MODE_IBC && !cu.licFlag ? std::min(curCuRdCost, m_amvpRdBeforeLIC[cu.imv]) : m_amvpRdBeforeLIC[cu.imv];
  }
private:
  bool skipLicBasedOnBestAmvpRDBeforeLIC(uint8_t curCuimvIdx, double curBestRdCost)
  {
    return m_amvpRdBeforeLIC[curCuimvIdx] != std::numeric_limits<double>::max()
        && m_amvpRdBeforeLIC[curCuimvIdx] > curBestRdCost * LIC_AMVP_SKIP_TH;
  }
};
#endif

#if JVET_AD0208_IBC_ADAPT_FOR_CAM_CAPTURED_CONTENTS
template <int N>
struct SrchCostBv
{
  static const int maxSize  = N;
  static const int capacity = (N) + 1;

  uint32_t   cnt;
  bool       enableFracIBC;         // Just to make sure cfg-off results and macro-off results will match
  bool       enableMultiCandSrch;   // True: search best fracBv among best N integer BVs; False: search best fracBv among best integer BVs and best fracBv of previous round
  Distortion costList  [capacity];
  Mv         mvList    [capacity];
#if JVET_AE0169_BIPREDICTIVE_IBC
  int        mergeIdxList[capacity];
#endif
  uint8_t    imvList   [capacity];
  uint8_t    mvpIdxList[capacity];
#if JVET_AA0070_RRIBC || JVET_AC0060_IBC_BVP_CLUSTER_RRIBC_BVD_SIGN_DERIV
  int        bvTypeList[capacity];
#endif
#if JVET_AA0070_RRIBC && JVET_AC0060_IBC_BVP_CLUSTER_RRIBC_BVD_SIGN_DERIV
  bool       bvFlipList[capacity];
#endif
#if JVET_AE0159_FIBC
  bool       bvFilter[capacity];
#endif
#if JVET_AE0078_IBC_LIC_EXTENSION
  bool       skipLicSrch[capacity];
  int        bvLicIdx[capacity];
#endif

  SrchCostBv()
  : cnt                 (0)
  , enableFracIBC       (false)
  , enableMultiCandSrch (false)
  {
    mvList[maxSize].setZero();
  }

  void init(bool resetHistoryMv = false, bool _enableFracIBC = false)
  {
    cnt = 0;
    enableFracIBC       = _enableFracIBC;
    enableMultiCandSrch = _enableFracIBC;
    if (resetHistoryMv)
    {
      mvList[maxSize].setZero();
#if JVET_AE0159_FIBC
      bvFilter[maxSize] = false;
#endif
#if JVET_AE0078_IBC_LIC_EXTENSION
      bvLicIdx[maxSize] = 0;
      skipLicSrch[maxSize] = false;
#endif
    }
  }

  void cutoff(double ratio)
  {
    if (N >= 2)
    {
      if (cnt >= 2)
      {
        double th = ratio * (double)costList[0];

        for (int i = 1; i < cnt; ++i)
        {
          if ((double)costList[i] > th)
          {
            cnt = i;
            return;
          }
        }
      }
    }
  }

  int find(int mvx, int mvy
#if JVET_AE0169_BIPREDICTIVE_IBC
         , int mergeIdx
#endif
#if JVET_AA0070_RRIBC || JVET_AC0060_IBC_BVP_CLUSTER_RRIBC_BVD_SIGN_DERIV
         , int bvType
#endif
#if JVET_AC0060_IBC_BVP_CLUSTER_RRIBC_BVD_SIGN_DERIV && JVET_AA0070_RRIBC
         , bool bvFlip
#endif
#if JVET_AE0078_IBC_LIC_EXTENSION
         , int bvLic
#endif
  )
  {
    for (int i = 0; i < (int)cnt; ++i)
    {
      if (mvList[i].getHor() == mvx && mvList[i].getVer() == mvy
#if JVET_AE0169_BIPREDICTIVE_IBC
        && mergeIdxList[i] == mergeIdx
#endif
#if JVET_AA0070_RRIBC || JVET_AC0060_IBC_BVP_CLUSTER_RRIBC_BVD_SIGN_DERIV
        && bvTypeList[i] == bvType
#endif
#if JVET_AC0060_IBC_BVP_CLUSTER_RRIBC_BVD_SIGN_DERIV && JVET_AA0070_RRIBC
        && bvFlipList[i] == bvFlip
#endif
#if JVET_AE0078_IBC_LIC_EXTENSION
        && bvLicIdx[i] == bvLic
#endif
        )
      {
        return i;
      }
    }
    return NOT_VALID;
  }

  void  replaceAt(uint32_t idxSrc, uint32_t idxDst)
  {
    if (idxSrc != idxDst)
    {
      costList  [idxDst] = costList  [idxSrc];
      mvList    [idxDst] = mvList    [idxSrc];
#if JVET_AE0169_BIPREDICTIVE_IBC
      mergeIdxList[idxDst] = mergeIdxList[idxSrc];
#endif
      imvList   [idxDst] = imvList   [idxSrc];
      mvpIdxList[idxDst] = mvpIdxList[idxSrc];
#if JVET_AA0070_RRIBC || JVET_AC0060_IBC_BVP_CLUSTER_RRIBC_BVD_SIGN_DERIV
      bvTypeList[idxDst] = bvTypeList[idxSrc];
#endif
#if JVET_AC0060_IBC_BVP_CLUSTER_RRIBC_BVD_SIGN_DERIV && JVET_AA0070_RRIBC
      bvFlipList[idxDst] = bvFlipList[idxSrc];
#endif
#if JVET_AE0078_IBC_LIC_EXTENSION
      bvLicIdx[idxDst] = bvLicIdx[idxSrc];
      skipLicSrch[idxDst] = skipLicSrch[idxSrc];
#endif
#if JVET_AE0159_FIBC
      bvFilter[idxDst] = bvFilter[idxSrc];
#endif
    }
  }

  int  insert(Distortion cost, int mvx, int mvy
#if JVET_AE0169_BIPREDICTIVE_IBC
            , int mergeIdx
#endif
#if JVET_AA0070_RRIBC || JVET_AC0060_IBC_BVP_CLUSTER_RRIBC_BVD_SIGN_DERIV
            , int bvType = 0
#endif
#if JVET_AC0060_IBC_BVP_CLUSTER_RRIBC_BVD_SIGN_DERIV && JVET_AA0070_RRIBC
            , bool bvFlip = true
#endif
#if JVET_AE0078_IBC_LIC_EXTENSION
            , int bvLic = 0
#endif
  )
  {
#if JVET_AA0070_RRIBC || JVET_AC0060_IBC_BVP_CLUSTER_RRIBC_BVD_SIGN_DERIV
    // Ignore 1-D BV's
    if (bvType != 0)
    {
      return NOT_VALID;
    }
#endif
#if JVET_AC0060_IBC_BVP_CLUSTER_RRIBC_BVD_SIGN_DERIV && JVET_AA0070_RRIBC
    bvFlip = bvType == 0 ? false : bvFlip;
#endif

    // Find insertion index
    int insertIdx = NOT_VALID;
    for (int i = 0; i < (int)cnt; ++i)
    {
      if (cost <= costList[i])
      {
        if (cost == costList[i])
        {
          if (mvList[i].getHor() == mvx && mvList[i].getVer() == mvy
#if JVET_AE0169_BIPREDICTIVE_IBC
            && mergeIdxList[i] == mergeIdx
#endif
#if JVET_AA0070_RRIBC || JVET_AC0060_IBC_BVP_CLUSTER_RRIBC_BVD_SIGN_DERIV
            && bvTypeList[i] == bvType
#endif
#if JVET_AC0060_IBC_BVP_CLUSTER_RRIBC_BVD_SIGN_DERIV && JVET_AA0070_RRIBC
            && bvFlipList[i] == bvFlip
#endif
#if JVET_AE0078_IBC_LIC_EXTENSION
            && bvLicIdx[i] == bvLic
#endif
            )
          {
            return NOT_VALID;
          }
        }
        else
        {
          insertIdx = i;
          break;
        }
      }
    }

    // Do insertion
    auto setAt = [&](uint32_t idx)
    {
      costList[idx] = cost;
      mvList[idx].set(mvx, mvy);
#if JVET_AE0169_BIPREDICTIVE_IBC
      mergeIdxList[idx] = mergeIdx;
#endif
#if JVET_AA0070_RRIBC || JVET_AC0060_IBC_BVP_CLUSTER_RRIBC_BVD_SIGN_DERIV
      bvTypeList[idx] = bvType;
#endif
#if JVET_AC0060_IBC_BVP_CLUSTER_RRIBC_BVD_SIGN_DERIV && JVET_AA0070_RRIBC
      bvFlipList[idx] = bvFlip;
#endif
#if JVET_AE0078_IBC_LIC_EXTENSION
      bvLicIdx[idx] = bvLic;
#endif
#if JVET_AE0159_FIBC
      bvFilter[idx] = false;
#endif
    };

    auto replaceNext = [&](uint32_t idx)
    {
      costList[idx + 1] = costList[idx];
      mvList[idx + 1].set(mvList[idx].getHor(), mvList[idx].getVer());
#if JVET_AE0169_BIPREDICTIVE_IBC
      mergeIdxList[idx + 1] = mergeIdxList[idx];
#endif
#if JVET_AA0070_RRIBC || JVET_AC0060_IBC_BVP_CLUSTER_RRIBC_BVD_SIGN_DERIV
      bvTypeList[idx + 1] = bvTypeList[idx];
#endif
#if JVET_AC0060_IBC_BVP_CLUSTER_RRIBC_BVD_SIGN_DERIV && JVET_AA0070_RRIBC
      bvFlipList[idx + 1] = bvFlipList[idx];
#endif
#if JVET_AE0078_IBC_LIC_EXTENSION
      bvLicIdx[idx + 1] = bvLicIdx[idx];
#endif
#if JVET_AE0159_FIBC
      bvFilter[idx + 1] = bvFilter[idx];
#endif
    };

    if (insertIdx != NOT_VALID)
    {
      for (int i = (cnt < N ? cnt - 1 : N - 2); i >= insertIdx; --i)
      {
        replaceNext(i);
      }
      setAt(insertIdx);

      if (cnt < N)
      {
        ++cnt;
      }
      return insertIdx;
    }
    else if (cnt < N)
    {
      insertIdx = cnt;
      setAt(insertIdx);

      ++cnt;
      return insertIdx;
    }
    else
    {
      return NOT_VALID;
    }
  }
};

typedef SrchCostBv<4> SrchCostIntBv;
#endif

/// encoder search class
class InterSearch : public InterPrediction, AffineGradientSearch
{
private:
  EncModeCtrl     *m_modeCtrl;

  PelStorage      m_tmpPredStorage              [NUM_REF_PIC_LIST_01];
  PelStorage      m_tmpStorageLCU;
#if JVET_AD0208_IBC_ADAPT_FOR_CAM_CAPTURED_CONTENTS
  PelStorage      m_tmpStorageCUflipH;
  PelStorage      m_tmpStorageCUflipV;
public:
  SrchCostIntBv   m_bestSrchCostIntBv;
#if JVET_AE0159_FIBC || JVET_AE0078_IBC_LIC_EXTENSION
  SrchCostIntBv   m_bestSrchCostIbcFilter;
#endif
private:
#endif
  PelStorage      m_tmpAffiStorage;
  Pel*            m_tmpAffiError;
#if AFFINE_ENC_OPT
  Pel*            m_tmpAffiDeri[2];
#else
  int*            m_tmpAffiDeri[2];
#endif
#if JVET_AC0112_IBC_CIIP
  PelStorage      m_ibcCiipBuffer;
#endif
#if JVET_AC0112_IBC_CIIP || JVET_AC0112_IBC_LIC
#if JVET_AA0070_RRIBC
  AMVPInfo        m_amvpInfo[3];
  AMVPInfo        m_amvpInfo4Pel[3];
#if JVET_AD0208_IBC_ADAPT_FOR_CAM_CAPTURED_CONTENTS
  AMVPInfo        m_amvpInfoHPel[3];
  AMVPInfo        m_amvpInfoQPel[3];
#endif
#else
  AMVPInfo        m_amvpInfo;
  AMVPInfo        m_amvpInfo4Pel;
#if JVET_AD0208_IBC_ADAPT_FOR_CAM_CAPTURED_CONTENTS
  AMVPInfo        m_amvpInfoHPel;
  AMVPInfo        m_amvpInfoQPel;
#endif
#endif
#if JVET_AE0169_BIPREDICTIVE_IBC
  Pel**           m_amvpMergeBuffer;
  MergeCtx        m_amvpMergeCtx;
#endif
#endif

#if MULTI_HYP_PRED
  MergeCtx        m_geoMrgCtx;
#if JVET_AD0213_LIC_IMP
  int             m_mhpMrgTempBufSet;
  PelUnitBuf      m_mhpMrgTempBuf[GEO_MAX_NUM_UNI_CANDS];
  PelUnitBuf      m_mhpMrgTempBufLic[GEO_MAX_NUM_UNI_CANDS];
#else
  bool            m_mhpMrgTempBufSet;
  PelUnitBuf      m_mhpMrgTempBuf[GEO_MAX_NUM_UNI_CANDS];
#endif
  PelUnitBuf      m_mhpTempBuf[GEO_MAX_TRY_WEIGHTED_SAD];
  int             m_mhpTempBufCounter;
#endif

  CodingStructure ****m_pSplitCS;
  CodingStructure ****m_pFullCS;

  CodingStructure **m_pSaveCS;

  ClpRng          m_lumaClpRng;
  uint32_t        m_estWeightIdxBits[BCW_NUM];
  BcwMotionParam  m_uniMotions;
  bool            m_affineModeSelected;
#if JVET_AD0213_LIC_IMP
  bool            m_doAffineLic;
#if TM_AMVP
  bool            updateMvNeeded;
  bool            updateL1ZeroFlagMvNeeded;
  bool            updateSMVDMvNeeded;
  bool            isBDOFNotNeeded;
  AMVPInfo        *amvpCand0;
  AMVPInfo        *amvpCand1;
  AMVPInfo        *amvpCand0Lic;
  AMVPInfo        *amvpCand1Lic;
  int             mvpIdx0;
  int             mvpIdx1;
#endif
#endif
  std::unordered_map< Position, std::unordered_map< Size, BlkRecord> > m_ctuRecord;
#if JVET_AA0070_RRIBC
  Distortion      minCostProj;
#endif
  AffineMVInfo       *m_affMVList;
  int             m_affMVListIdx;
  int             m_affMVListSize;
  int             m_affMVListMaxSize;
  BlkUniMvInfo*   m_uniMvList;
  int             m_uniMvListIdx;
  int             m_uniMvListSize;
  int             m_uniMvListMaxSize;
#if INTER_LIC
  BlkUniMvInfo*   m_uniMvListLIC;
  int             m_uniMvListIdxLIC;
  int             m_uniMvListSizeLIC;
#endif
#if JVET_AG0098_AMVP_WITH_SBTMVP
  bool*           m_amvpSbTmvpBufValid;
  MotionInfo*     m_amvpSbTmvpMotionBuf;
  Position        m_amvpSbTmvpBufTLPos;
#endif
  Distortion      m_hevcCost;
  EncAffineMotion m_affineMotion;
  PatentBvCand    m_defaultCachedBvs;
#if JVET_AE0059_INTER_CCCM
  Pel             **m_interCccmStorage;
#endif
#if JVET_AF0073_INTER_CCP_MERGE
  Pel             **m_interCcpMergeStorage;
#endif
#if JVET_AE0169_BIPREDICTIVE_IBC
  Distortion      m_bestBvpSADHADCost;
#endif
protected:
  // interface to option
  EncCfg*         m_pcEncCfg;

  // interface to classes
#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
  BilateralFilter* m_bilateralFilter;
#endif
  TrQuant*        m_pcTrQuant;
#if JVET_AG0276_NLIC
public:
  EncReshape*     m_pcReshape;
protected:
#else
  EncReshape*     m_pcReshape;
#endif

  // ME parameters
  int             m_iSearchRange;
  int             m_bipredSearchRange; // Search range for bi-prediction
  MESearchMethod  m_motionEstimationSearchMethod;
  int             m_aaiAdaptSR                  [MAX_NUM_REF_LIST_ADAPT_SR][MAX_IDX_ADAPT_SR];

  // RD computation
  CABACWriter*    m_CABACEstimator;
  CtxCache*       m_ctxCache;
  DistParam       m_cDistParam;
#if JVET_AA0133_INTER_MTS_OPT
  double          m_globalBestLumaCost;
  double          m_bestDCT2PassLumaCost;
#endif
  RefPicList      m_currRefPicList;
  int             m_currRefPicIndex;
  bool            m_skipFracME;
  int             m_numHashMVStoreds[NUM_REF_PIC_LIST_01][MAX_NUM_REF];
  Mv              m_hashMVStoreds[NUM_REF_PIC_LIST_01][MAX_NUM_REF][5];

  // Misc.
  Pel            *m_pTempPel;

  // AMVP cost computation
#if JVET_Y0129_MVD_SIGNAL_AMVP_MERGE_MODE
  uint32_t            m_auiMVPIdxCost               [AMVP_MAX_NUM_CANDS+1][AMVP_MAX_NUM_CANDS+1+1]; //th array bounds
#else
  uint32_t            m_auiMVPIdxCost               [AMVP_MAX_NUM_CANDS+1][AMVP_MAX_NUM_CANDS+1]; //th array bounds
#endif

  Mv              m_integerMv2Nx2N              [NUM_REF_PIC_LIST_01][MAX_NUM_REF];

  bool            m_isInitialized;

  Mv              m_acBVs[2 * IBC_NUM_CANDIDATES];
  unsigned int    m_numBVs;
  bool            m_useCompositeRef;
  Distortion      m_estMinDistSbt[NUMBER_SBT_MODE + 1]; // estimated minimum SSE value of the PU if using a SBT mode
  uint8_t         m_sbtRdoOrder[NUMBER_SBT_MODE];       // order of SBT mode in RDO
  bool            m_skipSbtAll;                         // to skip all SBT modes for the current PU
  uint8_t         m_histBestSbt;                        // historical best SBT mode for PU of certain SSE values
  uint8_t         m_histBestMtsIdx;                     // historical best MTS idx  for PU of certain SSE values
  bool            m_clipMvInSubPic;

#if INTER_LIC
public:
  EncFastLICCtrl  m_fastLicCtrl;
#endif
#if JVET_AF0073_INTER_CCP_MERGE
  bool m_isInterCcpModelReady;
  int  m_validNum;
  CCPModelCandidate m_interCcpMergeList[MAX_CCP_CAND_LIST_SIZE];
#endif

#if JVET_X0083_BM_AMVP_MERGE_MODE
public:
  Distortion      m_amvpOnlyCost;
#endif

public:
#if MULTI_HYP_PRED
#if JVET_AD0213_LIC_IMP
  void             initMHPTmpBuffer(PelStorage* mergeTmpBuffer, PelStorage* mergeTmpBuffer2, int maxNumMergeCandidates,
#else
  void             initMHPTmpBuffer(PelStorage* mergeTmpBuffer, int maxNumMergeCandidates,
#endif
    PelStorage* mhpTmpBuffer, int maxNumStoredMhpCandidates,
    const UnitArea localUnitArea)
  {
#if JVET_AD0213_LIC_IMP
    m_mhpMrgTempBufSet = 0;
#else
    m_mhpMrgTempBufSet = false;
#endif
    for (uint8_t mergeCand = 0; mergeCand < maxNumMergeCandidates; mergeCand++)
    {
      m_mhpMrgTempBuf[mergeCand] = mergeTmpBuffer[mergeCand].getBuf(localUnitArea);
#if JVET_AD0213_LIC_IMP
      m_mhpMrgTempBufLic[mergeCand] = mergeTmpBuffer2[mergeCand].getBuf(localUnitArea);
#endif
    }
    for (uint8_t i = 0; i < maxNumStoredMhpCandidates; i++)
    {
      m_mhpTempBuf[i] = mhpTmpBuffer[i].getBuf(localUnitArea);
    }
    m_mhpTempBufCounter = 0;
  }
#if JVET_AD0213_LIC_IMP
  void             setGeoTmpBuffer(int setId)
  {
    m_mhpMrgTempBufSet = setId;
  }
  void             setGeoTmpBuffer(MergeCtx geoMrgCtx, int setId)
  {
    m_mhpMrgTempBufSet = setId;
    m_geoMrgCtx = geoMrgCtx;
  }
#else
  void             setGeoTmpBuffer()
  {
    m_mhpMrgTempBufSet = true;
  }
  void             setGeoTmpBuffer(MergeCtx geoMrgCtx)
  {
    m_mhpMrgTempBufSet = true;
    m_geoMrgCtx = geoMrgCtx;
  }
#endif
#endif

#if JVET_AI0183_MVP_EXTENSION
  MotionInfo      m_subPuMiBuf[SUB_BUFFER_SIZE][(MAX_CU_SIZE * MAX_CU_SIZE) >> (MIN_CU_LOG2 << 1)];
#endif
  InterSearch();
  virtual ~InterSearch();

  void init                         ( EncCfg*        pcEncCfg,
#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
                                     BilateralFilter* bilateralFilter,
#endif
                                      TrQuant*       pcTrQuant,
                                      int            iSearchRange,
                                      int            bipredSearchRange,
                                      MESearchMethod motionEstimationSearchMethod,
                                      bool           useCompositeRef,
                                      const uint32_t     maxCUWidth,
                                      const uint32_t     maxCUHeight,
                                      const uint32_t     maxTotalCUDepth,
                                      RdCost*        pcRdCost,
                                      CABACWriter*   CABACEstimator,
                                      CtxCache*      ctxCache
                                     , EncReshape*   m_pcReshape
#if JVET_Z0153_IBC_EXT_REF
                                    , const uint32_t curPicWidthY
#if JVET_AJ0172_IBC_ITMP_ALIGN_REF_AREA
                                    , const uint32_t curPicHeightY 
#endif
#endif
                                    );

  void destroy                      ();

  void       calcMinDistSbt         ( CodingStructure &cs, const CodingUnit& cu, const uint8_t sbtAllowed );
  uint8_t    skipSbtByRDCost        ( int width, int height, int mtDepth, uint8_t sbtIdx, uint8_t sbtPos, double bestCost, Distortion distSbtOff, double costSbtOff, bool rootCbfSbtOff );
  bool       getSkipSbtAll          ()                 { return m_skipSbtAll; }
  void       setSkipSbtAll          ( bool skipAll )   { m_skipSbtAll = skipAll; }
  uint8_t    getSbtRdoOrder         ( uint8_t idx )    { assert( m_sbtRdoOrder[idx] < NUMBER_SBT_MODE ); assert( (uint32_t)( m_estMinDistSbt[m_sbtRdoOrder[idx]] >> 2 ) < ( MAX_UINT >> 1 ) ); return m_sbtRdoOrder[idx]; }
  Distortion getEstDistSbt          ( uint8_t sbtMode) { return m_estMinDistSbt[sbtMode]; }
  void       initTuAnalyzer         ()                 { m_estMinDistSbt[NUMBER_SBT_MODE] = std::numeric_limits<uint64_t>::max(); m_skipSbtAll = false; }
  void       setHistBestTrs         ( uint8_t sbtInfo, uint8_t mtsIdx ) { m_histBestSbt = sbtInfo; m_histBestMtsIdx = mtsIdx; }
  void       initSbtRdoOrder        ( uint8_t sbtMode ) { m_sbtRdoOrder[0] = sbtMode; m_estMinDistSbt[0] = m_estMinDistSbt[sbtMode]; }

  void setTempBuffers               (CodingStructure ****pSlitCS, CodingStructure ****pFullCS, CodingStructure **pSaveCS );
  void resetCtuRecord               ()             { m_ctuRecord.clear(); }
#if ENABLE_SPLIT_PARALLELISM
  void copyState                    ( const InterSearch& other );
#endif
#if JVET_AA0133_INTER_MTS_OPT
  void setBestCost(double cost) { m_globalBestLumaCost = cost; }
#endif
  void setAffineModeSelected        ( bool flag) { m_affineModeSelected = flag; }
#if JVET_AD0213_LIC_IMP
  void setDoAffineLic               ( bool flag) { m_doAffineLic = flag; }
#if TM_AMVP
  void resetLicEncCtrlPara          ();
  void setEncCtrlParaLicOff         ( CodingUnit& cu );
  void setEncCtrlParaLicOn          ( CodingUnit& cu );
  void checkEncLicOff               ( CodingUnit& cu, MergeCtx& mergeCtx );
  void checkEncLicOn                ( CodingUnit& cu, MergeCtx& mergeCtx );
#endif
#endif
  void resetAffineMVList() { m_affMVListIdx = 0; m_affMVListSize = 0; }
  void savePrevAffMVInfo(int idx, AffineMVInfo &tmpMVInfo, bool& isSaved)
  {
    if (m_affMVListSize > idx)
    {
      tmpMVInfo = m_affMVList[(m_affMVListIdx - 1 - idx + m_affMVListMaxSize) % m_affMVListMaxSize];
      isSaved = true;
    }
    else
      isSaved = false;
  }
  void addAffMVInfo(AffineMVInfo &tmpMVInfo)
  {
    int j = 0;
    AffineMVInfo *prevInfo = nullptr;
    for (; j < m_affMVListSize; j++)
    {
      prevInfo = m_affMVList + ((m_affMVListIdx - j - 1 + m_affMVListMaxSize) % (m_affMVListMaxSize));
      if ((tmpMVInfo.x == prevInfo->x) && (tmpMVInfo.y == prevInfo->y) && (tmpMVInfo.w == prevInfo->w) && (tmpMVInfo.h == prevInfo->h))
      {
        break;
      }
    }
    if (j < m_affMVListSize)
      *prevInfo = tmpMVInfo;
    else
    {
      m_affMVList[m_affMVListIdx] = tmpMVInfo;
      m_affMVListIdx = (m_affMVListIdx + 1) % m_affMVListMaxSize;
      m_affMVListSize = std::min(m_affMVListSize + 1, m_affMVListMaxSize);
    }
  }
#if INTER_LIC
  void swapUniMvBuffer() // simply swap the MvInfo buffer in order to not over-change the functions ME, insertUniMvCands, savePrevUniMvInfo and addUniMvInfo.
  {
    BlkUniMvInfo*   tempMvInfo;
    int             tempInt;

    tempMvInfo     = m_uniMvList;
    m_uniMvList    = m_uniMvListLIC;
    m_uniMvListLIC = tempMvInfo;

    tempInt           = m_uniMvListIdx;
    m_uniMvListIdx    = m_uniMvListIdxLIC;
    m_uniMvListIdxLIC = tempInt;

    tempInt            = m_uniMvListSize;
    m_uniMvListSize    = m_uniMvListSizeLIC;
    m_uniMvListSizeLIC = tempInt;
  }
#endif
  void resetUniMvList() { m_uniMvListIdx = 0; m_uniMvListSize = 0; 
#if INTER_LIC
                          m_uniMvListIdxLIC = 0; m_uniMvListSizeLIC = 0; 
#endif
  }
  void insertUniMvCands(CompArea blkArea, Mv cMvTemp[2][33])
  {
    BlkUniMvInfo* curMvInfo = m_uniMvList + m_uniMvListIdx;
    int j = 0;
    for (; j < m_uniMvListSize; j++)
    {
      BlkUniMvInfo* prevMvInfo = m_uniMvList + ((m_uniMvListIdx - 1 - j + m_uniMvListMaxSize) % (m_uniMvListMaxSize));
      if ((blkArea.x == prevMvInfo->x) && (blkArea.y == prevMvInfo->y) && (blkArea.width == prevMvInfo->w) && (blkArea.height == prevMvInfo->h))
      {
        break;
      }
    }

    if (j < m_uniMvListSize)
    {
      curMvInfo = m_uniMvList + ((m_uniMvListIdx - 1 - j + m_uniMvListMaxSize) % (m_uniMvListMaxSize));
    }

    ::memcpy(curMvInfo->uniMvs, cMvTemp, 2 * 33 * sizeof(Mv));
    if (j == m_uniMvListSize)  // new element
    {
      curMvInfo->x = blkArea.x;
      curMvInfo->y = blkArea.y;
      curMvInfo->w = blkArea.width;
      curMvInfo->h = blkArea.height;
      m_uniMvListSize = std::min(m_uniMvListSize + 1, m_uniMvListMaxSize);
      m_uniMvListIdx = (m_uniMvListIdx + 1) % (m_uniMvListMaxSize);
    }
  }
  void savePrevUniMvInfo(CompArea blkArea, BlkUniMvInfo &tmpUniMvInfo, bool& isUniMvInfoSaved)
  {
    int j = 0;
    BlkUniMvInfo* curUniMvInfo = nullptr;
    for (; j < m_uniMvListSize; j++)
    {
      curUniMvInfo = m_uniMvList + ((m_uniMvListIdx - 1 - j + m_uniMvListMaxSize) % (m_uniMvListMaxSize));
      if ((blkArea.x == curUniMvInfo->x) && (blkArea.y == curUniMvInfo->y) && (blkArea.width == curUniMvInfo->w) && (blkArea.height == curUniMvInfo->h))
      {
        break;
      }
    }

    if (j < m_uniMvListSize)
    {
      isUniMvInfoSaved = true;
      tmpUniMvInfo = *curUniMvInfo;
    }
  }
  void addUniMvInfo(BlkUniMvInfo &tmpUniMVInfo)
  {
    int j = 0;
    BlkUniMvInfo* prevUniMvInfo = nullptr;
    for (; j < m_uniMvListSize; j++)
    {
      prevUniMvInfo = m_uniMvList + ((m_uniMvListIdx - 1 - j + m_uniMvListMaxSize) % (m_uniMvListMaxSize));
      if ((tmpUniMVInfo.x == prevUniMvInfo->x) && (tmpUniMVInfo.y == prevUniMvInfo->y) && (tmpUniMVInfo.w == prevUniMvInfo->w) && (tmpUniMVInfo.h == prevUniMvInfo->h))
      {
        break;
      }
    }
    if (j < m_uniMvListSize)
    {
      *prevUniMvInfo = tmpUniMVInfo;
    }
    else
    {
      m_uniMvList[m_uniMvListIdx] = tmpUniMVInfo;
      m_uniMvListIdx = (m_uniMvListIdx + 1) % m_uniMvListMaxSize;
      m_uniMvListSize = std::min(m_uniMvListSize + 1, m_uniMvListMaxSize);
    }
  }
  void resetSavedAffineMotion();
  void storeAffineMotion( Mv acAffineMv[2][3], int8_t affineRefIdx[2], EAffineModel affineType, int bcwIdx );
#if !JVET_Z0084_IBC_TM
  bool searchBv(PredictionUnit& pu, int xPos, int yPos, int width, int height, int picWidth, int picHeight, int xBv, int yBv, int ctuSize);
#endif
  void setClipMvInSubPic(bool flag) { m_clipMvInSubPic = flag; }
#if JVET_AE0169_BIPREDICTIVE_IBC
  int getAmvpMergeValidCand() { return m_amvpMergeCtx.numValidMergeCand; }
#endif
protected:

  /// sub-function for motion vector refinement used in fractional-pel accuracy
  Distortion  xPatternRefinement    ( const CPelBuf* pcPatternKey, Mv baseRefMv, int iFrac, Mv& rcMvFrac, bool bAllowUseOfHadamard );

#if JVET_Z0131_IBC_BVD_BINARIZATION
  void xEstBvdBitCosts(EstBvdBitsStruct *p
#if JVET_AE0169_BIPREDICTIVE_IBC
                     , bool bi
#endif
#if JVET_AD0208_IBC_ADAPT_FOR_CAM_CAPTURED_CONTENTS
                     , unsigned useIBCFrac = 0
#if JVET_AA0070_RRIBC
                     , int ctxIdRrIBC = NOT_VALID
#endif
#if JVET_AC0060_IBC_BVP_CLUSTER_RRIBC_BVD_SIGN_DERIV
                     , int ctxIdOneComp = NOT_VALID
#endif
#endif
#if JVET_AC0060_IBC_BVP_CLUSTER_RRIBC_BVD_SIGN_DERIV
                     , const bool useBvpCluster = true
#endif
  );
#endif

   typedef struct
   {
     int left;
     int right;
     int top;
     int bottom;
   }SearchRange;

  typedef struct
  {
    SearchRange searchRange;
    const CPelBuf* pcPatternKey;
    const Pel*  piRefY;
    int         iRefStride;
    int         iBestX;
    int         iBestY;
    uint32_t        uiBestRound;
    uint32_t        uiBestDistance;
    Distortion  uiBestSad;
    uint8_t       ucPointNr;
    int         subShiftMode;
    unsigned    imvShift;
    bool        useAltHpelIf;
    bool        inCtuSearch;
    bool        zeroMV;
  } IntTZSearchStruct;

  // sub-functions for ME
  inline void xTZSearchHelp         ( IntTZSearchStruct& rcStruct, const int iSearchX, const int iSearchY, const uint8_t ucPointNr, const uint32_t uiDistance );
  inline void xTZ2PointSearch       ( IntTZSearchStruct& rcStruct );
  inline void xTZ8PointSquareSearch ( IntTZSearchStruct& rcStruct, const int iStartX, const int iStartY, const int iDist );
  inline void xTZ8PointDiamondSearch( IntTZSearchStruct& rcStruct, const int iStartX, const int iStartY, const int iDist, const bool bCheckCornersAtDist1 );

  Distortion xGetInterPredictionError( PredictionUnit& pu, PelUnitBuf& origBuf, const RefPicList &eRefPicList = REF_PIC_LIST_X );

public:
  /// encoder estimation - inter prediction (non-skip)

  void setModeCtrl( EncModeCtrl *modeCtrl ) { m_modeCtrl = modeCtrl;}

#if JVET_X0083_BM_AMVP_MERGE_MODE
  void predInterSearch(CodingUnit& cu, Partitioner& partitioner, bool& amvpMergeModeNotValid,
#if JVET_AD0213_LIC_IMP
      MvField* mvFieldAmListCommon = nullptr, bool* licAmListCommon = nullptr, Mv* mvBufEncAmBDmvrL0 = nullptr, Mv* mvBufEncAmBDmvrL1 = nullptr);
#else
      MvField* mvFieldAmListCommon = nullptr, Mv* mvBufEncAmBDmvrL0 = nullptr, Mv* mvBufEncAmBDmvrL1 = nullptr);
#endif
#else
  void predInterSearch(CodingUnit& cu, Partitioner& partitioner );
#endif

  /// set ME search range
  void setAdaptiveSearchRange       ( int iDir, int iRefIdx, int iSearchRange) { CHECK(iDir >= MAX_NUM_REF_LIST_ADAPT_SR || iRefIdx>=int(MAX_IDX_ADAPT_SR), "Invalid index"); m_aaiAdaptSR[iDir][iRefIdx] = iSearchRange; }
#if JVET_AD0208_IBC_ADAPT_FOR_CAM_CAPTURED_CONTENTS
  bool  predIBCSearch               ( CodingUnit& cu, Partitioner& partitioner, const int localSearchRangeX, const int localSearchRangeY, IbcHashMap& ibcHashMap
#if JVET_AC0112_IBC_LIC || JVET_AC0112_IBC_CIIP
                                    , Distortion* bvSearchCost = nullptr
#endif
#if JVET_AC0112_IBC_CIIP
                                    , PelBuf* ciipIbcBuff = nullptr
#endif
#if JVET_AA0070_RRIBC
                                    , bool isSecondPass = false
#endif
#if JVET_AC0112_IBC_LIC || JVET_AC0112_IBC_CIIP
                                    , bool* searchedByHash = nullptr
#endif
#if JVET_AD0208_IBC_ADAPT_FOR_CAM_CAPTURED_CONTENTS && JVET_AC0112_IBC_CIIP
                                    , bool isCiipFirstPass = false
#endif
#if JVET_AD0208_IBC_ADAPT_FOR_CAM_CAPTURED_CONTENTS && (JVET_AC0112_IBC_CIIP || JVET_AC0112_IBC_LIC)
                                    , double costTh = std::numeric_limits<double>::max()
#endif
  );
  void  xIntraPatternSearch         ( PredictionUnit& pu, IntTZSearchStruct&  cStruct, Mv& rcMv, Distortion&  ruiCost, Mv* cMvSrchRngLT, Mv* cMvSrchRngRB, Mv* pcMvPred
#if JVET_AA0070_RRIBC
                                    , int rribcFlipType
#endif
  );
#else
#if JVET_AA0070_RRIBC
#if JVET_AC0112_IBC_CIIP
  bool  predIBCSearch           ( CodingUnit& cu, Partitioner& partitioner, const int localSearchRangeX, const int localSearchRangeY, IbcHashMap& ibcHashMap, Distortion* bvSearchCost = NULL, PelBuf* ciipIbcBuff = NULL, bool isSecondPass = false, bool* searchedByHash = NULL );
#else
#if JVET_AC0112_IBC_LIC
  bool  predIBCSearch           ( CodingUnit& cu, Partitioner& partitioner, const int localSearchRangeX, const int localSearchRangeY, IbcHashMap& ibcHashMap, Distortion* bvSearchCost = NULL, bool isSecondPass = false, bool* searchedByHash = NULL );
#else
  bool  predIBCSearch           ( CodingUnit& cu, Partitioner& partitioner, const int localSearchRangeX, const int localSearchRangeY, IbcHashMap& ibcHashMap, bool isSecondPass = false );
#endif
#endif
  void  xIntraPatternSearch          ( PredictionUnit& pu, IntTZSearchStruct&  cStruct, Mv& rcMv, Distortion&  ruiCost, Mv* cMvSrchRngLT, Mv* cMvSrchRngRB, Mv* pcMvPred, int rribcFlipType );
#else
#if JVET_AC0112_IBC_CIIP
  bool  predIBCSearch           ( CodingUnit& cu, Partitioner& partitioner, const int localSearchRangeX, const int localSearchRangeY, IbcHashMap& ibcHashMap, Distortion* bvSearchCost = NULL, PelBuf* ciipIbcBuff = NULL, bool* searchedByHash = NULL);
#else
#if JVET_AC0112_IBC_LIC
  bool  predIBCSearch           ( CodingUnit& cu, Partitioner& partitioner, const int localSearchRangeX, const int localSearchRangeY, IbcHashMap& ibcHashMap, Distortion* bvSearchCost = NULL, bool* searchedByHash = NULL);
#else
  bool  predIBCSearch           ( CodingUnit& cu, Partitioner& partitioner, const int localSearchRangeX, const int localSearchRangeY, IbcHashMap& ibcHashMap);
#endif
#endif
  void  xIntraPatternSearch         ( PredictionUnit& pu, IntTZSearchStruct&  cStruct, Mv& rcMv, Distortion&  ruiCost, Mv* cMvSrchRngLT, Mv* cMvSrchRngRB, Mv* pcMvPred );
#endif
#endif
#if JVET_AD0208_IBC_ADAPT_FOR_CAM_CAPTURED_CONTENTS
  void  xIntraPatternSearchOpt(PredictionUnit& pu, IntTZSearchStruct&  cStruct, Mv& rcMv, Distortion&  ruiCost, Mv* cMvSrchRngLT, Mv* cMvSrchRngRB, Mv* pcMvPred
#if JVET_AA0070_RRIBC
                             , int rribcFlipType
#endif
  );
#endif
#if JVET_AE0169_BIPREDICTIVE_IBC
  void getBvpMergeOrgBuf(const PredictionUnit& pu, int mergeIdx, CPelBuf* pcPatternKey, const CPelBuf& refBuf, const PelBuf& ibcCiipIntraBuf, PelUnitBuf& orgBufForAmvpMerge);
#endif
#if JVET_AD0208_IBC_ADAPT_FOR_CAM_CAPTURED_CONTENTS
  template <int N>
  Distortion xPredIBCFracPelSearch  ( PredictionUnit&              pu 
                                    , SrchCostBv<N>&               intBvList
                                    , AMVPInfo*                    amvpInfoQPel
                                    , AMVPInfo*                    amvpInfoHPel
                                    , AMVPInfo*                    amvpInfoFPel
                                    , AMVPInfo*                    amvpInfo4Pel
#if JVET_AA0070_RRIBC || JVET_AC0060_IBC_BVP_CLUSTER_RRIBC_BVD_SIGN_DERIV
                                    , int                          numBvTypes = 1
#endif
#if JVET_AA0070_RRIBC && JVET_AC0060_IBC_BVP_CLUSTER_RRIBC_BVD_SIGN_DERIV
                                    , bool                         testRrIBC = true
#endif
  );
#endif
  void  xSetIntraSearchRange        ( PredictionUnit& pu, int iRoiWidth, int iRoiHeight, const int localSearchRangeX, const int localSearchRangeY, Mv& rcMvSrchRngLT, Mv& rcMvSrchRngRB);
  void  resetIbcSearch()
  {
    for (int i = 0; i < IBC_NUM_CANDIDATES; i++)
    {
      m_defaultCachedBvs.m_bvCands[i].setZero();
    }
    m_defaultCachedBvs.currCnt = 0;
  }
#if JVET_AA0070_RRIBC
#if JVET_AC0112_IBC_CIIP
  void xIBCEstimation(PredictionUnit &pu, PelUnitBuf &origBuf, PelBuf &ciipIbcIntraBuff, Mv pcMvPred[3][2], Mv &rcMv, Distortion &ruiCost, const int localSearchRangeX, const int localSearchRangeY, int numRribcType);
#else
  void xIBCEstimation(PredictionUnit &pu, PelUnitBuf &origBuf, Mv pcMvPred[3][2], Mv &rcMv, Distortion &ruiCost, const int localSearchRangeX, const int localSearchRangeY, int numRribcType);
#endif
  void xIBCSearchMVCandUpdate( Distortion uiSad, int x, int y, Distortion *uiSadBestCand, Mv *cMVCand);
  int  xIBCSearchMVChromaRefine( PredictionUnit &pu, int iRoiWidth, int iRoiHeight, int cuPelX, int cuPelY, Distortion *uiSadBestCand, Mv *cMVCand, int rribcFlipType );
#else
#if JVET_AC0112_IBC_CIIP
  void  xIBCEstimation   ( PredictionUnit& pu, PelUnitBuf& origBuf, PelBuf &ciipIbcIntraBuff, Mv     *pcMvPred, Mv     &rcMv, Distortion &ruiCost, const int localSearchRangeX, const int localSearchRangeY);
#else
  void  xIBCEstimation   ( PredictionUnit& pu, PelUnitBuf& origBuf, Mv     *pcMvPred, Mv     &rcMv, Distortion &ruiCost, const int localSearchRangeX, const int localSearchRangeY);
#endif
  void  xIBCSearchMVCandUpdate  ( Distortion  uiSad, int x, int y, Distortion* uiSadBestCand, Mv* cMVCand);
  int   xIBCSearchMVChromaRefine( PredictionUnit& pu, int iRoiWidth, int iRoiHeight, int cuPelX, int cuPelY, Distortion* uiSadBestCand, Mv*     cMVCand );
#endif
  void addToSortList(std::list<BlockHash>& listBlockHash, std::list<int>& listCost, int cost, const BlockHash& blockHash);
  bool predInterHashSearch(CodingUnit& cu, Partitioner& partitioner, bool& isPerfectMatch);
  bool xHashInterEstimation(PredictionUnit& pu, RefPicList& bestRefPicList, int& bestRefIndex, Mv& bestMv, Mv& bestMvd, int& bestMVPIndex, bool& isPerfectMatch);
  bool xRectHashInterEstimation(PredictionUnit& pu, RefPicList& bestRefPicList, int& bestRefIndex, Mv& bestMv, Mv& bestMvd, int& bestMVPIndex, bool& isPerfectMatch);
  void selectRectangleMatchesInter(const MapIterator& itBegin, int count, std::list<BlockHash>& listBlockHash, const BlockHash& currBlockHash, int width, int height, int idxNonSimple, unsigned int* &hashValues, int baseNum, int picWidth, int picHeight, bool isHorizontal, uint16_t* curHashPic);
  void selectMatchesInter(const MapIterator& itBegin, int count, std::list<BlockHash>& vecBlockHash, const BlockHash& currBlockHash);
#if MULTI_HYP_PRED
  void predInterSearchAdditionalHypothesis(PredictionUnit& pu, const MEResult& x, MEResultVec& out);
  inline static unsigned getAdditionalHypothesisInitialBits(const MultiHypPredictionData& mhData, const int iNumWeights, const int iNumMHRefPics);
#endif

#if JVET_Z0056_GPM_SPLIT_MODE_REORDERING
  // -------------------------------------------------------------------------------------------------------------------
  // Inter GPM model selection
  // -------------------------------------------------------------------------------------------------------------------
protected:
#if JVET_AG0164_AFFINE_GPM
  uint32_t m_gpmacsSplitModeTmSelAvail [GEO_ENC_MMVD_MAX_REFINE_NUM_ADJ][GEO_ENC_MMVD_MAX_REFINE_NUM_ADJ][GEO_MAX_ALL_INTER_UNI_CANDS]; // Note: sizeof(uint16_t) should not be less than GEO_MAX_NUM_UNI_CANDS
  uint8_t  m_gpmacsSplitModeTmSel      [GEO_ENC_MMVD_MAX_REFINE_NUM_ADJ][GEO_ENC_MMVD_MAX_REFINE_NUM_ADJ][GEO_MAX_ALL_INTER_UNI_CANDS][GEO_MAX_ALL_INTER_UNI_CANDS][GEO_NUM_PARTITION_MODE];
  uint32_t m_gpmPartTplCost            [GEO_ENC_MMVD_MAX_REFINE_NUM_ADJ][GEO_MAX_ALL_INTER_UNI_CANDS][2][GEO_NUM_PARTITION_MODE]; // [][][0][]: partition 0, [][][1][]: partition 1
#else
  uint16_t m_gpmacsSplitModeTmSelAvail [GEO_ENC_MMVD_MAX_REFINE_NUM_ADJ][GEO_ENC_MMVD_MAX_REFINE_NUM_ADJ][GEO_MAX_NUM_UNI_CANDS]; // Note: sizeof(uint16_t) should not be less than GEO_MAX_NUM_UNI_CANDS
  uint8_t  m_gpmacsSplitModeTmSel      [GEO_ENC_MMVD_MAX_REFINE_NUM_ADJ][GEO_ENC_MMVD_MAX_REFINE_NUM_ADJ][GEO_MAX_NUM_UNI_CANDS][GEO_MAX_NUM_UNI_CANDS][GEO_NUM_PARTITION_MODE];
  uint32_t m_gpmPartTplCost            [GEO_ENC_MMVD_MAX_REFINE_NUM_ADJ][GEO_MAX_NUM_UNI_CANDS][2][GEO_NUM_PARTITION_MODE]; // [][][0][]: partition 0, [][][1][]: partition 1
#endif
public:
  void initGeoAngleSelection(PredictionUnit& pu
#if JVET_Y0065_GPM_INTRA
                           , IntraPrediction* pcIntraPred, const uint8_t (&mpm)[GEO_NUM_PARTITION_MODE][2][GEO_MAX_NUM_INTRA_CANDS]
#endif
  );
  void setGeoSplitModeToSyntaxTable(PredictionUnit& pu, MergeCtx& mergeCtx0, int mergeCand0, MergeCtx& mergeCtx1, int mergeCand1
#if JVET_AG0164_AFFINE_GPM
                                  , const AffineMergeCtx &affMergeCtx
#endif
#if JVET_Y0065_GPM_INTRA
                                  , IntraPrediction* pcIntraPred
#endif
#if JVET_AI0082_GPM_WITH_INTER_IBC
                                  , Mv* geoBvList
#endif
                                  , int mmvdCand0 = -1, int mmvdCand1 = -1); // mmvdCandX = -1: regular, 0~GPM_EXT_MMVD_MAX_REFINE_NUM-1: MMVD, >=GPM_EXT_MMVD_MAX_REFINE_NUM: TM
#if JVET_W0097_GPM_MMVD_TM && TM_MRG
#if JVET_AJ0274_GPM_AFFINE_TM
  void setGeoTMSplitModeToSyntaxTable(PredictionUnit& pu, MergeCtx (&mergeCtx)[GEO_NUM_TM_MV_CAND], const AffineMergeCtx &affMergeCtx, int mergeCand0, int mergeCand1, int mmvdCand0 = -1, int mmvdCand1 = -1); // mmvdCandX = -1: regular, 0~GPM_EXT_MMVD_MAX_REFINE_NUM-1: MMVD, >=GPM_EXT_MMVD_MAX_REFINE_NUM: TM
#else
  void setGeoTMSplitModeToSyntaxTable(PredictionUnit& pu, MergeCtx (&mergeCtx)[GEO_NUM_TM_MV_CAND], int mergeCand0, int mergeCand1, int mmvdCand0 = -1, int mmvdCand1 = -1); // mmvdCandX = -1: regular, 0~GPM_EXT_MMVD_MAX_REFINE_NUM-1: MMVD, >=GPM_EXT_MMVD_MAX_REFINE_NUM: TM
#endif
#endif
  int  convertGeoSplitModeToSyntax(int splitDir, int mergeCand0, int mergeCand1, int mmvdCand0 = -1, int mmvdCand1 = -1); // mmvdCandX = -1: regular, 0~GPM_EXT_MMVD_MAX_REFINE_NUM-1: MMVD, >=GPM_EXT_MMVD_MAX_REFINE_NUM: TM
#if JVET_AE0169_BIPREDICTIVE_IBC
  void resetBestBvpSADHADCost()     { m_bestBvpSADHADCost = std::numeric_limits<Distortion>::max(); }
#if JVET_AD0208_IBC_ADAPT_FOR_CAM_CAPTURED_CONTENTS
  void setBestBvpSADHADCost(Distortion cost) { m_bestBvpSADHADCost = std::min(m_bestBvpSADHADCost, cost); }
#endif
  Distortion getBestBvpSADHADCost() { return m_bestBvpSADHADCost; }
#endif

protected:
#if JVET_Y0065_GPM_INTRA
#if JVET_AI0082_GPM_WITH_INTER_IBC
  inline void xRemapMrgIndexAndMmvdIdx(int& mergeCand0, int& mergeCand1, int& mmvdCand0, int& mmvdCand1, bool &isIntra0, bool &isIntra1, bool &isIbc0, bool &isIbc1)
#else
  inline void xRemapMrgIndexAndMmvdIdx(int& mergeCand0, int& mergeCand1, int& mmvdCand0, int& mmvdCand1, bool &isIntra0, bool &isIntra1)
#endif
  {
#if JVET_AI0082_GPM_WITH_INTER_IBC
#if JVET_AG0164_AFFINE_GPM
    bool isIbc = (mergeCand0 >= GEO_MAX_ALL_INTER_UNI_CANDS + GEO_MAX_NUM_INTRA_CANDS) || (mergeCand1 >= GEO_MAX_ALL_INTER_UNI_CANDS + GEO_MAX_NUM_INTRA_CANDS);
#else
    bool isIbc = (mergeCand0 >= GEO_MAX_NUM_UNI_CANDS + GEO_MAX_NUM_INTRA_CANDS) || (mergeCand1 >= GEO_MAX_NUM_UNI_CANDS + GEO_MAX_NUM_INTRA_CANDS);
#endif
#endif
#if JVET_W0097_GPM_MMVD_TM
#if JVET_AI0082_GPM_WITH_INTER_IBC
    const int intraMmvdBufIdx = (GPM_EXT_MMVD_MAX_REFINE_NUM + 1) + 1 + (isIbc ? 1 : 0);
#else
    static const int intraMmvdBufIdx = (GPM_EXT_MMVD_MAX_REFINE_NUM + 1) + 1;
#endif
#else
#if JVET_AI0082_GPM_WITH_INTER_IBC
    const int intraMmvdBufIdx = 1 + (isIbc ? 1 : 0);
#else
    static const int intraMmvdBufIdx = 1;
#endif
#endif

    isIntra0 = false;
    isIntra1 = false;
#if JVET_AI0082_GPM_WITH_INTER_IBC
    isIbc0 = false;
    isIbc1 = false;
#endif
#if JVET_AG0164_AFFINE_GPM
    if (mergeCand0 >= GEO_MAX_ALL_INTER_UNI_CANDS)
    {
      isIntra0    = true;
      mergeCand0 -= GEO_MAX_ALL_INTER_UNI_CANDS;
#if JVET_AI0082_GPM_WITH_INTER_IBC
      if (isIntra0 && mergeCand0 >= GEO_MAX_NUM_INTRA_CANDS)
      {
        isIbc0 = true;
        mergeCand0 -= GEO_MAX_NUM_INTRA_CANDS;
      }
#endif
      mmvdCand0   = intraMmvdBufIdx - 1;
    }

    if (mergeCand1 >= GEO_MAX_ALL_INTER_UNI_CANDS)
    {
      isIntra1    = true;
      mergeCand1 -= GEO_MAX_ALL_INTER_UNI_CANDS;
#if JVET_AI0082_GPM_WITH_INTER_IBC
      if (isIntra1 && mergeCand1 >= GEO_MAX_NUM_INTRA_CANDS)
      {
        isIbc1 = true;
        mergeCand1 -= GEO_MAX_NUM_INTRA_CANDS;
      }
#endif
      mmvdCand1   = intraMmvdBufIdx - 1;
    }
#else
    if (mergeCand0 >= GEO_MAX_NUM_UNI_CANDS)
    {
      isIntra0    = true;
      mergeCand0 -= GEO_MAX_NUM_UNI_CANDS;
#if JVET_AI0082_GPM_WITH_INTER_IBC
      if (isIntra0 && mergeCand0 >= GEO_MAX_NUM_INTRA_CANDS)
      {
        isIbc0 = true;
        mergeCand0 -= GEO_MAX_NUM_INTRA_CANDS;
      }
#endif
      mmvdCand0   = intraMmvdBufIdx - 1;
    }

    if (mergeCand1 >= GEO_MAX_NUM_UNI_CANDS)
    {
      isIntra1    = true;
      mergeCand1 -= GEO_MAX_NUM_UNI_CANDS;
#if JVET_AI0082_GPM_WITH_INTER_IBC
      if (isIntra1 && mergeCand1 >= GEO_MAX_NUM_INTRA_CANDS)
      {
        isIbc1 = true;
        mergeCand1 -= GEO_MAX_NUM_INTRA_CANDS;
      }
#endif
      mmvdCand1   = intraMmvdBufIdx - 1;
    }
#endif
  }
#endif

  inline void xSetGpmModeToSyntaxModeTable(uint8_t numValidInList, uint8_t(&modeListSrc)[GEO_NUM_SIG_PARTMODE], uint8_t(&modeListDst)[GEO_NUM_PARTITION_MODE])
  {
    memset(modeListDst, -1, sizeof(modeListDst));
    for (int i = 0; i < (int)numValidInList; ++i)
    {
      modeListDst[modeListSrc[i]] = i;
    }
  }

#if JVET_Y0065_GPM_INTRA
  template <uint8_t partIdx>
  void xCollectIntraGeoPartCost(PredictionUnit &pu, IntraPrediction* pcIntraPred, int mergeCand, uint32_t(&gpmTplCost)[GEO_NUM_PARTITION_MODE]);
#endif

  bool selectGeoSplitModes (PredictionUnit &pu, 
#if JVET_AG0164_AFFINE_GPM
                            const AffineMergeCtx& affMergeCtx, 
#endif
#if JVET_Y0065_GPM_INTRA
                            IntraPrediction* pcIntraPred,
#endif
#if JVET_AI0082_GPM_WITH_INTER_IBC
                            Mv* geoBvList,
#endif
                            uint32_t (&gpmTplCostPart0)[2][GEO_NUM_PARTITION_MODE],
                            uint32_t (&gpmTplCostPart1)[2][GEO_NUM_PARTITION_MODE],
                            MergeCtx& mergeCtx0, int mergeCand0, MergeCtx& mergeCtx1, int mergeCand1, uint8_t& numValidInList, uint8_t (&modeList)[GEO_NUM_SIG_PARTMODE], int mmvdCand0 = -1, int mmvdCand1 = -1);
  void getBestGeoModeListEncoder (PredictionUnit &pu, uint8_t& numValidInList,
                                  uint8_t (&modeList)[GEO_NUM_SIG_PARTMODE],
                                  Pel* pRefTopPart0, Pel* pRefLeftPart0,
                                  Pel* pRefTopPart1, Pel* pRefLeftPart1,
                                  uint32_t (&gpmTplCostPart0)[2][GEO_NUM_PARTITION_MODE],
                                  uint32_t (&gpmTplCostPart1)[2][GEO_NUM_PARTITION_MODE]);
#if JVET_W0097_GPM_MMVD_TM && TM_MRG
#if JVET_AJ0274_GPM_AFFINE_TM
  bool selectGeoTMSplitModes (PredictionUnit &pu,
                              uint32_t (&gpmTplCostPart0)[2][GEO_NUM_PARTITION_MODE],
                              uint32_t (&gpmTplCostPart1)[2][GEO_NUM_PARTITION_MODE],
                              MergeCtx (&mergeCtx)[GEO_NUM_TM_MV_CAND], const AffineMergeCtx& affMergeCtx, int mergeCand0, int mergeCand1, uint8_t& numValidInList, uint8_t (&modeList)[GEO_NUM_SIG_PARTMODE]);
#else
  bool selectGeoTMSplitModes (PredictionUnit &pu,
                              uint32_t (&gpmTplCostPart0)[2][GEO_NUM_PARTITION_MODE],
                              uint32_t (&gpmTplCostPart1)[2][GEO_NUM_PARTITION_MODE],
                              MergeCtx (&mergeCtx)[GEO_NUM_TM_MV_CAND], int mergeCand0, int mergeCand1, uint8_t& numValidInList, uint8_t (&modeList)[GEO_NUM_SIG_PARTMODE]);
#endif
  void getBestGeoTMModeListEncoder (PredictionUnit &pu, uint8_t& numValidInList,
                                    uint8_t (&modeList)[GEO_NUM_SIG_PARTMODE],
                                    Pel* (&pRefTopPart0)[GEO_NUM_TM_MV_CAND], Pel* (&pRefLeftPart0)[GEO_NUM_TM_MV_CAND],
                                    Pel* (&pRefTopPart1)[GEO_NUM_TM_MV_CAND], Pel* (&pRefLeftPart1)[GEO_NUM_TM_MV_CAND],
                                    uint32_t (&gpmTplCostPart0)[2][GEO_NUM_PARTITION_MODE],
                                    uint32_t (&gpmTplCostPart1)[2][GEO_NUM_PARTITION_MODE]);
#endif
#endif

protected:

  // -------------------------------------------------------------------------------------------------------------------
  // Inter search (AMP)
  // -------------------------------------------------------------------------------------------------------------------

  void xEstimateMvPredAMVP        ( PredictionUnit&       pu,
                                    PelUnitBuf&           origBuf,
                                    RefPicList            eRefPicList,
                                    int                   iRefIdx,
                                    Mv&                   rcMvPred,
                                    AMVPInfo&             amvpInfo,
                                    bool                  bFilled = false,
                                    Distortion*           puiDistBiP = NULL
#if JVET_X0083_BM_AMVP_MERGE_MODE
                                  , MvField*              mvFieldAmListCommon = NULL
#endif
                                  );

  void xCheckBestMVP              ( RefPicList  eRefPicList,
                                    Mv          cMv,
                                    Mv&         rcMvPred,
                                    int&        riMVPIdx,
                                    AMVPInfo&   amvpInfo,
                                    uint32_t&       ruiBits,
                                    Distortion& ruiCost
                                    ,
                                    const uint8_t  imv
#if JVET_AG0098_AMVP_WITH_SBTMVP
                                  , const bool amvpSbTmvp = false
                                  , const int amvpSbTmvpMvdIdx = -1
                                  , const int numAmvpSbTmvpOffset = -1
#endif
                                  );

  Distortion xGetTemplateCost     ( const PredictionUnit& pu,
                                    PelUnitBuf&           origBuf,
                                    PelUnitBuf&           predBuf,
                                    Mv                    cMvCand,
                                    int                   iMVPIdx,
                                    int                   iMVPNum,
                                    RefPicList            eRefPicList,
                                    int                   iRefIdx
                                  );
  uint32_t xCalcAffineMVBits      ( PredictionUnit& pu, Mv mvCand[3], Mv mvPred[3] );

  void xCopyAMVPInfo              ( AMVPInfo*   pSrc, AMVPInfo* pDst );
  uint32_t xGetMvpIdxBits             ( int iIdx, int iNum );
  void xGetBlkBits                ( bool bPSlice, int iPartIdx,  uint32_t uiLastMode, uint32_t uiBlkBit[3]);



  // -------------------------------------------------------------------------------------------------------------------
  // motion estimation
  // -------------------------------------------------------------------------------------------------------------------

  void xMotionEstimation          ( PredictionUnit&       pu,
                                    PelUnitBuf&           origBuf,
                                    RefPicList            eRefPicList,
                                    Mv&                   rcMvPred,
                                    int                   iRefIdxPred,
                                    Mv&                   rcMv,
                                    int&                  riMVPIdx,
                                    uint32_t&                 ruiBits,
                                    Distortion&           ruiCost,
                                    const AMVPInfo&       amvpInfo,
                                    bool                  bBi = false
#if MULTI_HYP_PRED
                                  , const int             weight = 0
#endif
                                  );

#if JVET_AG0098_AMVP_WITH_SBTMVP
  void xAmvpSbTmvpMotionEstimation( PredictionUnit&       pu,
                                    PelUnitBuf&           origBuf,
                                    RefPicList            eRefPicList,
                                    Mv&                   rcMvPred,
                                    int                   iRefIdxPred,
                                    Mv&                   rcMv,
                                    int&                  amvpSbTmvpIdx,
                                    bool                  useAmvpSbTmvpBuf,
                                    MergeCtx&             mrgCtx,
                                    int&                  riMVPIdx,
                                    uint32_t&             ruiBits,
                                    Distortion&           ruiCost,
                                    const Distortion      normalCost
                                  );
#endif

  void xTZSearch                  ( const PredictionUnit& pu,
                                    RefPicList            eRefPicList,
                                    int                   iRefIdxPred,
                                    IntTZSearchStruct&    cStruct,
                                    Mv&                   rcMv,
                                    Distortion&           ruiSAD,
                                    const Mv* const       pIntegerMv2Nx2NPred,
                                    const bool            bExtendedSettings,
                                    const bool            bFastSettings = false
                                  );

  void xTZSearchSelective         ( const PredictionUnit& pu,
                                    RefPicList            eRefPicList,
                                    int                   iRefIdxPred,
                                    IntTZSearchStruct&    cStruct,
                                    Mv&                   rcMv,
                                    Distortion&           ruiSAD,
                                    const Mv* const       pIntegerMv2Nx2NPred
                                  );

  void xSetSearchRange            ( const PredictionUnit& pu,
                                    const Mv&             cMvPred,
                                    const int             iSrchRng,
                                    SearchRange&          sr
                                  , IntTZSearchStruct &  cStruct
                                  );

  void xPatternSearchFast         ( const PredictionUnit& pu,
                                    RefPicList            eRefPicList,
                                    int                   iRefIdxPred,
                                    IntTZSearchStruct&    cStruct,
                                    Mv&                   rcMv,
                                    Distortion&           ruiSAD,
                                    const Mv* const       pIntegerMv2Nx2NPred
                                  );

  void xPatternSearch             ( IntTZSearchStruct&    cStruct,
                                    Mv&                   rcMv,
                                    Distortion&           ruiSAD
                                  );

  void xPatternSearchIntRefine    ( PredictionUnit&     pu,
                                    IntTZSearchStruct&  cStruct,
                                    Mv&                 rcMv,
                                    Mv&                 rcMvPred,
                                    int&                riMVPIdx,
                                    uint32_t&               ruiBits,
                                    Distortion&         ruiCost,
                                    const AMVPInfo&     amvpInfo,
                                    double              fWeight
                                  );

  void xPatternSearchFracDIF      ( const PredictionUnit& pu,
                                    RefPicList            eRefPicList,
                                    int                   iRefIdx,
                                    IntTZSearchStruct&    cStruct,
                                    const Mv&             rcMvInt,
                                    Mv&                   rcMvHalf,
                                    Mv&                   rcMvQter,
                                    Distortion&           ruiCost                             
                                  );

  void xPredAffineInterSearch     ( PredictionUnit&       pu,
                                    PelUnitBuf&           origBuf,
                                    int                   puIdx,
                                    uint32_t&                 lastMode,
                                    Distortion&           affineCost,
                                    Mv                    hevcMv[2][33]
                                  , Mv                    mvAffine4Para[2][33][3]
                                  , int                   refIdx4Para[2]
                                  , uint8_t               bcwIdx = BCW_DEFAULT
                                  , bool                  enforceBcwPred = false
                                  , uint32_t              bcwIdxBits = 0
                                  );

  void xAffineMotionEstimation    ( PredictionUnit& pu,
                                    PelUnitBuf&     origBuf,
                                    RefPicList      eRefPicList,
                                    Mv              acMvPred[3],
                                    int             iRefIdxPred,
                                    Mv              acMv[3],
                                    uint32_t&           ruiBits,
                                    Distortion&     ruiCost,
                                    int&            mvpIdx,
                                    const AffineAMVPInfo& aamvpi,
                                    bool            bBi = false
                                  );

  void xEstimateAffineAMVP        ( PredictionUnit&  pu,
                                    AffineAMVPInfo&  affineAMVPInfo,
                                    PelUnitBuf&      origBuf,
                                    RefPicList       eRefPicList,
                                    int              iRefIdx,
                                    Mv               acMvPred[3],
                                    Distortion*      puiDistBiP
                                  );

  Distortion xGetAffineTemplateCost( PredictionUnit& pu, PelUnitBuf& origBuf, PelUnitBuf& predBuf, Mv acMvCand[3], int iMVPIdx, int iMVPNum, RefPicList eRefPicList, int iRefIdx );

  void xCopyAffineAMVPInfo        ( AffineAMVPInfo& src, AffineAMVPInfo& dst );
  void xCheckBestAffineMVP        ( PredictionUnit &pu, AffineAMVPInfo &affineAMVPInfo, RefPicList eRefPicList, Mv acMv[3], Mv acMvPred[3], int& riMVPIdx, uint32_t& ruiBits, Distortion& ruiCost );

  Distortion xGetSymmetricCost( PredictionUnit& pu, PelUnitBuf& origBuf, RefPicList eCurRefPicList, const MvField& cCurMvField, MvField& cTarMvField , int bcwIdx );

  Distortion xSymmeticRefineMvSearch( PredictionUnit& pu, PelUnitBuf& origBuf, Mv& rcMvCurPred, Mv& rcMvTarPred
    , RefPicList eRefPicList, MvField& rCurMvField, MvField& rTarMvField, Distortion uiMinCost, int searchPattern, int nSearchStepShift, uint32_t uiMaxSearchRounds , int bcwIdx );

  void xSymmetricMotionEstimation( PredictionUnit& pu, PelUnitBuf& origBuf, Mv& rcMvCurPred, Mv& rcMvTarPred, RefPicList eRefPicList, MvField& rCurMvField, MvField& rTarMvField, Distortion& ruiCost, int bcwIdx );

  bool xReadBufferedAffineUniMv   ( PredictionUnit& pu, RefPicList eRefPicList, int32_t iRefIdx, Mv acMvPred[3], Mv acMv[3], uint32_t& ruiBits, Distortion& ruiCost
                                    , int& mvpIdx, const AffineAMVPInfo& aamvpi
  );
  double xGetMEDistortionWeight   ( uint8_t bcwIdx, RefPicList eRefPicList);
  bool xReadBufferedUniMv         ( PredictionUnit& pu, RefPicList eRefPicList, int32_t iRefIdx, Mv& pcMvPred, Mv& rcMv, uint32_t& ruiBits, Distortion& ruiCost);

  void xClipMv                    ( Mv& rcMv, const struct Position& pos, const struct Size& size, const class SPS& sps, const class PPS& pps );

public:
  void resetBufferedUniMotions    () { m_uniMotions.reset(); }
  uint32_t getWeightIdxBits       ( uint8_t bcwIdx ) { return m_estWeightIdxBits[bcwIdx]; }
  void initWeightIdxBits          ();
  void symmvdCheckBestMvp(
    PredictionUnit& pu,
    PelUnitBuf& origBuf,
    Mv curMv,
    RefPicList curRefList,
    AMVPInfo amvpInfo[2][33],
    int32_t bcwIdx,
    Mv cMvPredSym[2],
    int32_t mvpIdxSym[2],
    Distortion& bestCost,
    bool skip = false
    );
protected:

  void xExtDIFUpSamplingH(CPelBuf* pcPattern, bool useAltHpelIf);
  void xExtDIFUpSamplingQ         ( CPelBuf* pcPatternKey, Mv halfPelRef);
  uint32_t xDetermineBestMvp      ( PredictionUnit& pu, Mv acMvTemp[3], int& mvpIdx, const AffineAMVPInfo& aamvpi );
  // -------------------------------------------------------------------------------------------------------------------
  // compute symbol bits
  // -------------------------------------------------------------------------------------------------------------------

  void  setWpScalingDistParam     ( int iRefIdx, RefPicList eRefPicListCur, Slice *slice );
private:
#if JVET_AA0070_RRIBC
  void xxIBCHashSearch(PredictionUnit &pu, Mv mvPred[3][2], int numMvPred, Mv &mv, int &idxMvPred, IbcHashMap &ibcHashMap, AMVPInfo amvpInfo4Pel[3], int numRribcType);
#else
  void  xxIBCHashSearch(PredictionUnit& pu, Mv* mvPred, int numMvPred, Mv &mv, int& idxMvPred, IbcHashMap& ibcHashMap);
#endif
#if JVET_AC0060_IBC_BVP_CLUSTER_RRIBC_BVD_SIGN_DERIV
  inline void getBestBvpBvOneZeroComp(PredictionUnit &pu, Mv cMv, Distortion initCost, int *bvpIdxBest,
                                                   AMVPInfo *amvp1Pel = NULL, AMVPInfo *amvp4Pel = NULL);
#endif
public:
#if JVET_AA0133_INTER_MTS_OPT
  bool encodeResAndCalcRdInterCU(CodingStructure &cs, Partitioner &partitioner, const bool &skipResidual
    , const bool luma = true, const bool chroma = true
  );
#else
  void encodeResAndCalcRdInterCU  (CodingStructure &cs, Partitioner &partitioner, const bool &skipResidual
    , const bool luma = true, const bool chroma = true
  );
#endif
  void xEncodeInterResidualQT     (CodingStructure &cs, Partitioner &partitioner, const ComponentID &compID);
#if JVET_AA0133_INTER_MTS_OPT
  bool xEstimateInterResidualQT(CodingStructure &cs, Partitioner &partitioner, Distortion *puiZeroDist = NULL
    , const bool luma = true, const bool chroma = true
    , PelUnitBuf* orgResi = NULL
  );
#else
  void xEstimateInterResidualQT   (CodingStructure &cs, Partitioner &partitioner, Distortion *puiZeroDist = NULL
    , const bool luma = true, const bool chroma = true
    , PelUnitBuf* orgResi = NULL
  );
#endif
  uint64_t xGetSymbolFracBitsInter  (CodingStructure &cs, Partitioner &partitioner);
  uint64_t xCalcPuMeBits            (PredictionUnit& pu);
#if JVET_AD0213_LIC_IMP
  uint64_t xCalcExpPuBits           (PredictionUnit& pu);
#endif
};// END CLASS DEFINITION EncSearch

//! \}

#endif // __ENCSEARCH__