/* 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-2021, 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     CodingStructure.h
 *  \brief    A class managing the coding information for a specific image part
 */

#include "CodingStructure.h"

#include "Unit.h"
#include "Slice.h"
#include "Picture.h"
#include "UnitTools.h"
#include "UnitPartitioner.h"


XUCache g_globalUnitCache = XUCache();

const UnitScale UnitScaleArray[NUM_CHROMA_FORMAT][MAX_NUM_COMPONENT] =
{
  { {2,2}, {0,0}, {0,0} },  // 4:0:0
  { {2,2}, {1,1}, {1,1} },  // 4:2:0
  { {2,2}, {1,2}, {1,2} },  // 4:2:2
  { {2,2}, {2,2}, {2,2} }   // 4:4:4
};

// ---------------------------------------------------------------------------
// coding structure method definitions
// ---------------------------------------------------------------------------

CodingStructure::CodingStructure(CUCache& cuCache, PUCache& puCache, TUCache& tuCache)
  : area      ()
  , picture   ( nullptr )
  , parent    ( nullptr )
  , bestCS    ( nullptr )
  , m_isTuEnc ( false )
  , m_cuCache ( cuCache )
  , m_puCache ( puCache )
  , m_tuCache ( tuCache )
  , bestParent ( nullptr )
  , tmpColorSpaceCost(MAX_DOUBLE)
  , firstColorSpaceSelected(true)
  , resetIBCBuffer (false)
{
  for( uint32_t i = 0; i < MAX_NUM_COMPONENT; i++ )
  {
    m_coeffs[ i ] = nullptr;
#if SIGN_PREDICTION
    m_coeff_signs[ i ] = nullptr;
#endif
#if !REMOVE_PCM
    m_pcmbuf[ i ] = nullptr;
#endif
    m_offsets[ i ] = 0;
  }

  for (uint32_t i = 0; i < MAX_NUM_CHANNEL_TYPE; i++)
  {
#if REMOVE_PCM
    m_pltIdx[i] = nullptr;
#endif
    m_runType[i] = nullptr;
  }

  for( uint32_t i = 0; i < MAX_NUM_CHANNEL_TYPE; i++ )
  {
    m_cuIdx   [ i ] = nullptr;
    m_puIdx   [ i ] = nullptr;
    m_tuIdx   [ i ] = nullptr;
    m_isDecomp[ i ] = nullptr;
  }

#if GDR_ENABLED
  m_motionBuf0    = nullptr;
  m_motionBuf1    = nullptr;
  picHeader       = nullptr;
#else
  m_motionBuf     = nullptr;
#endif

#if JVET_W0123_TIMD_FUSION
#if GDR_ENABLED
  m_ipmBuf0 = nullptr;
  m_ipmBuf1 = nullptr;  
#else
  m_ipmBuf = nullptr;
#endif  
#endif
  features.resize( NUM_ENC_FEATURES );
#if !INTRA_RM_SMALL_BLOCK_SIZE_CONSTRAINTS
  treeType = TREE_D;
  modeType = MODE_TYPE_ALL;
#endif
  tmpColorSpaceIntraCost[0] = MAX_DOUBLE;
  tmpColorSpaceIntraCost[1] = MAX_DOUBLE;
  firstColorSpaceTestOnly = false;

#if GDR_ENABLED
  picHeader = nullptr;
#endif
}

void CodingStructure::destroy()
{
  picture   = nullptr;
  parent    = nullptr;

  m_pred.destroy();
  m_resi.destroy();
#if GDR_ENABLED
  m_reco0.destroy();
  m_reco1.destroy();
#else
  m_reco.destroy();
#endif
  m_orgr.destroy();

  destroyCoeffs();

  for( uint32_t i = 0; i < MAX_NUM_CHANNEL_TYPE; i++ )
  {
    delete[] m_isDecomp[ i ];
    m_isDecomp[ i ] = nullptr;

    delete[] m_cuIdx[ i ];
    m_cuIdx[ i ] = nullptr;

    delete[] m_puIdx[ i ];
    m_puIdx[ i ] = nullptr;

    delete[] m_tuIdx[ i ];
    m_tuIdx[ i ] = nullptr;
  }

#if GDR_ENABLED
  delete[] m_motionBuf0;
  delete[] m_motionBuf1;
  m_motionBuf0 = nullptr;
  m_motionBuf1 = nullptr;

  if (picHeader) {
    delete picHeader;
    picHeader = nullptr;
  }
#else
  delete[] m_motionBuf;
  m_motionBuf = nullptr;
#endif

#if JVET_W0123_TIMD_FUSION
#if GDR_ENABLED
  delete[] m_ipmBuf0;
  delete[] m_ipmBuf1;
  m_ipmBuf0 = nullptr;
  m_ipmBuf1 = nullptr;
#else
  delete[] m_ipmBuf;
  m_ipmBuf = nullptr;
#endif
#endif


  m_tuCache.cache( tus );
  m_puCache.cache( pus );
  m_cuCache.cache( cus );
}

void CodingStructure::releaseIntermediateData()
{
  clearTUs();
  clearPUs();
  clearCUs();
}

#if GDR_ENABLED
bool CodingStructure::containRefresh(int begX, int endX) const
{
  if (begX == endX)
  {
    return false;
  }

  const Area csArea      = area.Y();
  const Area refreshArea = Area(begX, area.ly(), endX - begX, std::min(slice->getPPS()->getPicHeightInLumaSamples(), area.lheight()));

  if (csArea.contains(refreshArea))
  {
    return true;
  }

  return false;
}

bool CodingStructure::overlapRefresh(int begX, int endX) const
{
  if (begX == endX)
  {
    return false;
  }

  const Area csArea = area.Y();
  const Area refreshArea = Area(begX, area.ly(), endX - begX, area.lheight());

  if (csArea.overlap(refreshArea))
  {
    return true;
  }

  return false;
}

bool CodingStructure::overlapRefresh() const
{
  const int  csX     = area.lx();
  const int  csWidth = area.lwidth();

  bool ret = overlapRefresh(csX, csX + csWidth);

  return ret;
}

bool CodingStructure::withinRefresh(int begX, int endX) const
{
  if (begX == endX)
  {
    return false;
  }

  const Area csArea = area.Y();
  const Area refreshArea = Area(begX, area.ly(), endX - begX, area.lheight());

  if (refreshArea.contains(csArea))
  {
    return true;
  }

  return false;
}

bool CodingStructure::refreshCrossTTV(int begX, int endX) const
{
  const int  csX = area.lx();
  const int  csY = area.ly();
  const int  csWidth  = area.lwidth();
  const int  csHeight = area.lheight();

  const Area refreshArea = Area(begX, csY, endX - begX, csHeight);

  const Area csArea0 = Area(csX,                                   csY, csWidth >> 2, csHeight);
  const Area csArea1 = Area(csX + (csWidth >> 2),                  csY, csWidth >> 1, csHeight);
  const Area csArea2 = Area(csX + (csWidth >> 2) + (csWidth >> 1), csY, csWidth >> 2, csHeight);

  bool overlap0 = csArea0.overlap(refreshArea);
  bool overlap1 = csArea1.overlap(refreshArea);
  bool overlap2 = csArea2.overlap(refreshArea);

  int sum = (overlap0 ? 1 : 0) + (overlap1 ? 1 : 0) + (overlap2 ? 1 : 0);

  if (0 < sum)
  {
    return true;
  }

  return false;
}

bool CodingStructure::refreshCrossBTV(int begX, int endX) const
{
  const int  csX = area.lx();
  const int  csY = area.ly();
  const int  csWidth = area.lwidth();
  const int  csHeight = area.lheight();

  const Area refreshArea = Area(begX, csY, endX - begX, csHeight);

  const Area csArea0 = Area(csX,                  csY, (csWidth >> 1), csHeight);
  const Area csArea1 = Area(csX + (csWidth >> 1), csY, (csWidth >> 1), csHeight);

  bool overlap0 = csArea0.overlap(refreshArea);
  bool overlap1 = csArea1.overlap(refreshArea);

  int sum = (overlap0 ? 1 : 0) + (overlap1 ? 1 : 0);

  if (0 < sum)
  {
    return true;
  }

  return false;
}

bool CodingStructure::overlapDirty() const
{
  const Position topLeft  = area.Y().topLeft();
  const Position topRight = area.Y().topRight();

  bool insideLeft  = isClean(topLeft, CHANNEL_TYPE_LUMA);
  bool insideRight = isClean(topRight, CHANNEL_TYPE_LUMA);

  if (insideLeft != insideRight)
  {
    return true;
  }

  return false;
}

bool CodingStructure::dirtyCrossTTV() const
{
  const int  csX = area.lx();
  const int  csY = area.ly();
  const int  csWidth = area.lwidth();
  const int  csHeight = area.lheight();

  const Area csArea0 = Area(csX, csY, csWidth >> 2, csHeight);
  const Area csArea1 = Area(csX + (csWidth >> 2), csY, csWidth >> 1, csHeight);
  const Area csArea2 = Area(csX + (csWidth >> 2) + (csWidth >> 1), csY, csWidth >> 2, csHeight);

  bool clean0 = isClean(csArea0, CHANNEL_TYPE_LUMA);
  bool clean1 = isClean(csArea1, CHANNEL_TYPE_LUMA);
  bool clean2 = isClean(csArea2, CHANNEL_TYPE_LUMA);

  bool allclean = clean0 && clean1 && clean2;

  if (allclean)
  {
    return false;
  }

  return true;
}

bool CodingStructure::dirtyCrossBTV() const
{
  const int  csX = area.lx();
  const int  csY = area.ly();
  const int  csWidth = area.lwidth();
  const int  csHeight = area.lheight();

  const Area csArea0 = Area(csX,                  csY, (csWidth >> 1), csHeight);
  const Area csArea1 = Area(csX + (csWidth >> 1), csY, (csWidth >> 1), csHeight);

  bool clean0 = isClean(csArea0, CHANNEL_TYPE_LUMA);
  bool clean1 = isClean(csArea1, CHANNEL_TYPE_LUMA);

  bool allclean = clean0 && clean1;

  if (allclean)
  {
    return false;
  }

  return true;
}

Area CodingStructure::findOverlappedArea(const Area &a1, const Area &a2) const
{
  Area intersectArea = Area(Position(0, 0), Size(0, 0));

  if (
    ((a1.x <= a2.x && a2.x <= a1.topRight().x)    || (a2.x <= a1.x && a1.x <= a2.topRight().x)) &&
    ((a1.y <= a2.y && a2.y <= a1.bottomRight().y) || (a2.y <= a1.y && a1.y <= a2.bottomRight().y))
    ) 
  {
    int xArray[4] = { a1.x, a1.topRight().x,    a2.x, a2.topRight().x };
    int yArray[4] = { a1.y, a1.bottomRight().y, a2.y, a2.bottomRight().y };
    std::sort(xArray, xArray + 4);
    std::sort(yArray, yArray + 4);

    intersectArea = Area(Position(xArray[1], yArray[1]), Size(xArray[2] - xArray[1] + 1, yArray[2] - yArray[1] + 1));
  }
  
  return intersectArea;
}
#endif



#if GDR_ENABLED
bool CodingStructure::isInGdrInvervalOrRecoveryPoc() const
{
  PicHeader     *curPh = picHeader;
  bool isCurGdrIntervalPic = curPh->getInGdrInterval();
  bool isCurGdrRecoveryPocPic = curPh->getIsGdrRecoveryPocPic();

  if (isCurGdrIntervalPic || isCurGdrRecoveryPocPic)
  {
    return true;
  }

  return false;
}

bool CodingStructure::isClean(const ChannelType effChType) const
{
  bool ret = isClean(area.Y(), effChType);

  return ret;
}

bool CodingStructure::isClean(const Position &IntPos, RefPicList e, int refIdx) const
{
  /*
    1. non gdr picture --> false;
    2. gdr picture
         pos in clean area -> true
         pos in dirty area -> false
  */
  const Picture* const refPic = slice->getRefPic(e, refIdx);

  if (!refPic || refIdx < 0)
  {
    return false;
  }

  PicHeader     *refPh = refPic->cs->picHeader;
  bool isRefGdrIntervalPic = refPh->getInGdrInterval();
  bool isRefGdrRecoveryPocPic = refPh->getIsGdrRecoveryPocPic();

  if (isInGdrInvervalOrRecoveryPoc())
  {
    int virboundaryEndx = 0;

    if (isRefGdrIntervalPic)
      virboundaryEndx = refPh->getVirtualBoundariesPosX(0);

    if (isRefGdrRecoveryPocPic)
      virboundaryEndx = slice->getPPS()->getPicWidthInLumaSamples();

    if (IntPos.x < virboundaryEndx)
    {
      return true;
    }
    else
    {
      return false;
    }
  }
  else
  {
    // refPic is normal picture
    bool isCurGdrPicture = (slice->getPicHeader()->getNumVerVirtualBoundaries() > 0);

    if (isCurGdrPicture)
    {
      return false;
    }
    else
    {
      return true;
    }
  }
}

bool CodingStructure::isClean(const Position &IntPos, const Picture* const refPic) const
{
  if (!refPic)
  {
    return false;
  }

  PicHeader     *refPh = refPic->cs->picHeader;
  bool isRefGdrIntervalPic    = refPh->getInGdrInterval();
  bool isRefGdrRecoveryPocPic = refPh->getIsGdrRecoveryPocPic();

  if (isInGdrInvervalOrRecoveryPoc())
  {
    int virboundaryEndx = 0;

    if (isRefGdrIntervalPic)
      virboundaryEndx = refPh->getVirtualBoundariesPosX(0);

    if (isRefGdrRecoveryPocPic)
      virboundaryEndx = slice->getPPS()->getPicWidthInLumaSamples();

    if (IntPos.x < virboundaryEndx)
    {
      return true;
    }
    else
    {
      return false;
    }
  }
  else
  {
    // refPic is normal picture
    bool isCurGdrPicture = (slice->getPicHeader()->getNumVerVirtualBoundaries() > 0);

    if (isCurGdrPicture)
    {
      return false;
    }
    else
    {
      return true;
    }
  }
}

bool CodingStructure::isClean(const int Intx, const int Inty, const ChannelType effChType) const
{
  /*
    1. non gdr picture --> false;
    2. gdr picture
         pos in clean area -> true
         pos in dirty area -> false
  */
  PicHeader     *curPh = picHeader;
  bool isCurGdrIntervalPic    = curPh->getInGdrInterval();
  bool isCurGdrRecoveryPocPic = curPh->getIsGdrRecoveryPocPic();
  
  if (isInGdrInvervalOrRecoveryPoc())
  {
    int virboundaryEndx = 0;
    
    if (isCurGdrIntervalPic)
      virboundaryEndx = curPh->getVirtualBoundariesPosX(0);

    if (isCurGdrRecoveryPocPic)
      virboundaryEndx = slice->getPPS()->getPicWidthInLumaSamples();

    virboundaryEndx = virboundaryEndx >> effChType;
    if (Intx < virboundaryEndx)
    {
      return true;
    }
    else
    {
      return false;
    }
  }

  return true;
}

bool CodingStructure::isClean(const Position &IntPos, const ChannelType effChType) const
{
  bool ret = isClean(IntPos.x, IntPos.y, effChType);

  return ret;
}

bool CodingStructure::isClean(const Area &area, const ChannelType effChType) const
{
  Position pTopLeft  = area.topLeft();
  Position pTopRight = area.topRight();
  Position pBotLeft  = area.bottomLeft();
  Position pBotRight = area.bottomRight();

  bool bTopLeft  = isClean(pTopLeft,  effChType);
  bool bTopRight = isClean(pTopRight, effChType);
  bool bBotLeft  = isClean(pBotLeft,  effChType);
  bool bBotRight = isClean(pBotRight, effChType);

  return bTopLeft && bTopRight && bBotLeft && bBotRight;
}

bool CodingStructure::isClean(const CodingUnit &cu) const
{
  bool ret = cu.Y().valid() ? isClean(cu.Y().bottomRight(), CHANNEL_TYPE_LUMA) : isClean(cu.Cb().bottomRight(), CHANNEL_TYPE_CHROMA);

  return ret;
}

bool CodingStructure::isClean(const PredictionUnit &pu) const
{
  bool ret = pu.Y().valid() ? isClean(pu.Y().bottomRight(), CHANNEL_TYPE_LUMA) : isClean(pu.Cb().bottomRight(), CHANNEL_TYPE_CHROMA);

  return ret;
}

bool CodingStructure::isClean(const TransformUnit &tu) const
{
  bool ret = tu.Y().valid() ? isClean(tu.Y().bottomRight(), CHANNEL_TYPE_LUMA) : isClean(tu.Cb().bottomRight(), CHANNEL_TYPE_CHROMA);

  return ret;
}
#endif

#if GDR_ENABLED
void CodingStructure::updateReconMotIPM(const UnitArea &uarea) const
{
  updateReconMotIPM(uarea, getRecoBuf(uarea));
}

void CodingStructure::updateReconMotIPM(const CompArea &carea) const
{
  updateReconMotIPM(carea, getRecoBuf(carea));
}

void CodingStructure::updateReconMotIPM(const UnitArea &uarea, const CPelUnitBuf &pbuf) const
{
  for (int i = 0; i < MAX_NUM_COMPONENT; i++)
  {
    ComponentID compID = (ComponentID)i;
    if (uarea.block(compID).valid())
    {
      updateReconMotIPM(uarea.block(compID), pbuf.get(compID));
    }
  }
}

void CodingStructure::updateReconMotIPM(const CompArea &carea, const CPelBuf &pbuf) const
{
  const ComponentID compID = carea.compID;

  if (!isInGdrInvervalOrRecoveryPoc())
  {
    picture->getRecoBuf(carea).copyFrom(pbuf);
    if (compID == COMPONENT_Y)
    {
#if JVET_W0123_TIMD_FUSION
      picture->cs->getIpmBuf(carea).copyFrom(getIpmBuf(carea));
#endif
      picture->cs->getMotionBuf(carea).copyFrom(getMotionBuf(carea));
    }

    return;
  }


  picture->getBuf(carea, PIC_RECONSTRUCTION_0).copyFrom(pbuf);
  if (compID == COMPONENT_Y)
  {
#if JVET_W0123_TIMD_FUSION
    picture->cs->getIpmBuf(carea, PIC_RECONSTRUCTION_0).copyFrom(getIpmBuf(carea));
#endif
    picture->cs->getMotionBuf(carea, PIC_RECONSTRUCTION_0).copyFrom(getMotionBuf(carea));
  }

  ChromaFormat chromaFormat = sps->getChromaFormatIdc();
  int gdrEndX = picHeader->getGdrEndX()          >> getComponentScaleX((ComponentID)compID, chromaFormat);
  int gdrEndY = pps->getPicHeightInLumaSamples() >> getComponentScaleY((ComponentID)compID, chromaFormat);;

  CompArea cleanArea      = CompArea((ComponentID)compID, chromaFormat, Area(Position(0, 0), Size(gdrEndX, gdrEndY)));
  CompArea overlappedArea = CompArea((ComponentID)compID, chromaFormat, findOverlappedArea(cleanArea, carea));
  CompArea subArea        = CompArea((ComponentID)compID, chromaFormat, Area(overlappedArea.pos() - carea.pos(), overlappedArea.size()));

  if (subArea.area() > 0)
  {  
    picture->getBuf(overlappedArea, PIC_RECONSTRUCTION_1).copyFrom(pbuf.subBuf(subArea.pos(), subArea.size()));
    if (compID == COMPONENT_Y)
    {
#if JVET_W0123_TIMD_FUSION
      picture->cs->getIpmBuf(overlappedArea, PIC_RECONSTRUCTION_1).copyFrom(getIpmBuf(overlappedArea));
#endif
      picture->cs->getMotionBuf(overlappedArea, PIC_RECONSTRUCTION_1).copyFrom(getMotionBuf(overlappedArea));
    }
  }
}
#endif
    
#if GDR_ENABLED

void CodingStructure::rspSignalPicture(const UnitArea &uarea, std::vector<Pel>& pLUT, bool usePred) const
{
  for (int i = 0; i < MAX_NUM_COMPONENT; i++)
  {
    ComponentID compID = (ComponentID) i;
    if (uarea.block(compID).valid())
    {
      CompArea carea = uarea.block(compID);
      rspSignalPicture(carea, pLUT, usePred);
    }
  }
}

void CodingStructure::rspSignalPicture(const CompArea &carea, std::vector<Pel>& pLUT, bool usePred) const
{
  ComponentID compID = carea.compID;
    
  // 1. normal picture
  if (!isInGdrInvervalOrRecoveryPoc())
  {   
    PelBuf picRecoBuff = picture->getRecoBuf(carea);
    CPelBuf predBuf = usePred ? getPredBuf(carea) : picture->getRecoBuf(carea);

    picRecoBuff.rspSignal(predBuf, pLUT);

    return;
  }

  // 2.1. gdr interval dirty picture
  PelBuf picRecoBuff0 = picture->getBuf(carea, PIC_RECONSTRUCTION_0);
  CPelBuf predBuf0 = usePred ? getPredBuf(carea) : picture->getBuf(carea, PIC_RECONSTRUCTION_0);
  
  picRecoBuff0.rspSignal(predBuf0, pLUT);

  // 2.2. gdr interval clean picture
  ChromaFormat chromaFormat = sps->getChromaFormatIdc();
  int gdrEndX = picHeader->getGdrEndX()          >> getComponentScaleX((ComponentID)compID, chromaFormat);
  int gdrEndY = pps->getPicHeightInLumaSamples() >> getComponentScaleY((ComponentID)compID, chromaFormat);;

  CompArea cleanArea      = CompArea((ComponentID)compID, chromaFormat, Area(Position(0, 0), Size(gdrEndX, gdrEndY)));
  CompArea overlappedArea = CompArea((ComponentID)compID, chromaFormat, findOverlappedArea(cleanArea, carea));

  if (overlappedArea.area() > 0)
  {
    PelBuf picRecoBuff1 = picture->getBuf(overlappedArea, PIC_RECONSTRUCTION_1);    
    CPelBuf predBuf1 = usePred ? getPredBuf(overlappedArea) : picture->getBuf(overlappedArea, PIC_RECONSTRUCTION_1);

    picRecoBuff1.rspSignal(predBuf1, pLUT);
  }
}


void CodingStructure::reconstructPicture(const CompArea &carea, std::vector<Pel>& pLUT, CodingStructure *resiCS, bool lmcsEnable) const
{  
  ComponentID compID = carea.compID;
  
  // 1. normal picture
  if (!isInGdrInvervalOrRecoveryPoc())  
  {
    PelBuf picRecoBuff = picture->getRecoBuf(carea);

    if (lmcsEnable)
    {
      picRecoBuff.rspSignal(getPredBuf(carea), pLUT);
      picRecoBuff.reconstruct(picRecoBuff, resiCS->getResiBuf(carea), slice->clpRng(compID));
    }
    else
    {
      picRecoBuff.reconstruct(getPredBuf(carea), resiCS->getResiBuf(carea), slice->clpRng(compID));
    }

    return;
  }
  
  // 2.1. gdr interval dirty picture
  PelBuf picRecoBuff0 = picture->getBuf(carea, PIC_RECONSTRUCTION_0);

  if (lmcsEnable)
  {
    picRecoBuff0.rspSignal(getPredBuf(carea), pLUT);
    picRecoBuff0.reconstruct(picRecoBuff0, resiCS->getResiBuf(carea), slice->clpRng(compID));
  }
  else
  {
    picRecoBuff0.reconstruct(getPredBuf(carea), resiCS->getResiBuf(carea), slice->clpRng(compID));
  }

  // 2.2. gdr interval clean picture
  ChromaFormat chromaFormat = sps->getChromaFormatIdc();
  int gdrEndX = picHeader->getGdrEndX()          >> getComponentScaleX((ComponentID)compID, chromaFormat);
  int gdrEndY = pps->getPicHeightInLumaSamples() >> getComponentScaleY((ComponentID)compID, chromaFormat);;

  CompArea cleanArea      = CompArea((ComponentID)compID, chromaFormat, Area(Position(0, 0), Size(gdrEndX, gdrEndY)));
  CompArea overlappedArea = CompArea((ComponentID)compID, chromaFormat, findOverlappedArea(cleanArea, carea));  

  if (overlappedArea.area() > 0)
  {
    PelBuf picRecoBuff1 = picture->getBuf(overlappedArea, PIC_RECONSTRUCTION_1);

    if (lmcsEnable)
    {
      picRecoBuff1.rspSignal(getPredBuf(overlappedArea), pLUT);
      picRecoBuff1.reconstruct(picRecoBuff1, resiCS->getResiBuf(overlappedArea), slice->clpRng(compID));
    }
    else
    {
      picRecoBuff1.reconstruct(getPredBuf(overlappedArea), resiCS->getResiBuf(overlappedArea), slice->clpRng(compID));
    }
  }
}
#endif


bool CodingStructure::isDecomp( const Position &pos, const ChannelType effChType )
{
  if( area.blocks[effChType].contains( pos ) )
  {
    return m_isDecomp[effChType][rsAddr( pos, area.blocks[effChType], area.blocks[effChType].width, unitScale[effChType] )];
  }
  else if( parent )
  {
    return parent->isDecomp( pos, effChType );
  }
  else
  {
    return false;
  }
}

bool CodingStructure::isDecomp( const Position &pos, const ChannelType effChType ) const
{
  if( area.blocks[effChType].contains( pos ) )
  {
    return m_isDecomp[effChType][rsAddr( pos, area.blocks[effChType], area.blocks[effChType].width, unitScale[effChType] )];
  }
  else if( parent )
  {
    return parent->isDecomp( pos, effChType );
  }
  else
  {
    return false;
  }
}

void CodingStructure::setDecomp(const CompArea &_area, const bool _isCoded /*= true*/)
{
  const UnitScale& scale = unitScale[_area.compID];

  AreaBuf<bool> isCodedBlk( m_isDecomp[toChannelType( _area.compID )] + rsAddr( _area, area.blocks[_area.compID].pos(), area.blocks[_area.compID].width, scale ),
                            area.blocks[_area.compID].width >> scale.posx,
                            _area.width                     >> scale.posx,
                            _area.height                    >> scale.posy);
  isCodedBlk.fill( _isCoded );
}

void CodingStructure::setDecomp(const UnitArea &_area, const bool _isCoded /*= true*/)
{
  for( uint32_t i = 0; i < _area.blocks.size(); i++ )
  {
    if (_area.blocks[i].valid())
    {
      setDecomp(_area.blocks[i], _isCoded);
    }
  }
}
#if !INTRA_RM_SMALL_BLOCK_SIZE_CONSTRAINTS
const int CodingStructure::signalModeCons( const PartSplit split, Partitioner &partitioner, const ModeType modeTypeParent ) const
{
  if (CS::isDualITree(*this) || modeTypeParent != MODE_TYPE_ALL || partitioner.currArea().chromaFormat == CHROMA_444 || partitioner.currArea().chromaFormat == CHROMA_400 )
  {
    return LDT_MODE_TYPE_INHERIT;
  }
  int minLumaArea = partitioner.currArea().lumaSize().area();
  if (split == CU_QUAD_SPLIT || split == CU_TRIH_SPLIT || split == CU_TRIV_SPLIT) // the area is split into 3 or 4 parts
  {
    minLumaArea = minLumaArea >> 2;
  }
  else if (split == CU_VERT_SPLIT || split == CU_HORZ_SPLIT) // the area is split into 2 parts
  {
    minLumaArea = minLumaArea >> 1;
  }
  int minChromaBlock = minLumaArea >> (getChannelTypeScaleX(CHANNEL_TYPE_CHROMA, partitioner.currArea().chromaFormat) + getChannelTypeScaleY(CHANNEL_TYPE_CHROMA, partitioner.currArea().chromaFormat));

  bool is2xNChroma = (partitioner.currArea().chromaSize().width == 4 && split == CU_VERT_SPLIT) || (partitioner.currArea().chromaSize().width == 8 && split == CU_TRIV_SPLIT);
  return minChromaBlock >= 16 && !is2xNChroma ? LDT_MODE_TYPE_INHERIT : ((minLumaArea < 32) || slice->isIntra()) ? LDT_MODE_TYPE_INFER : LDT_MODE_TYPE_SIGNAL;
}

void CodingStructure::clearCuPuTuIdxMap(const UnitArea &_area, uint32_t numCu, uint32_t numPu, uint32_t numTu, uint32_t* pOffset )
{
  UnitArea clippedArea = clipArea( _area, *picture );
  uint32_t numCh = ::getNumberValidChannels( _area.chromaFormat );
  for( uint32_t i = 0; i < numCh; i++ )
  {
    const CompArea &_selfBlk = area.blocks[i];
    const CompArea     &_blk = clippedArea.blocks[i];

    const UnitScale& scale = unitScale[_blk.compID];
    const Area scaledSelf = scale.scale( _selfBlk );
    const Area scaledBlk = scale.scale( _blk );
    const size_t offset = rsAddr( scaledBlk.pos(), scaledSelf.pos(), scaledSelf.width );
    unsigned *idxPtrCU = m_cuIdx[i] + offset;
    AreaBuf<uint32_t>( idxPtrCU, scaledSelf.width, scaledBlk.size() ).fill( 0 );

    unsigned *idxPtrPU = m_puIdx[i] + offset;
    AreaBuf<uint32_t>( idxPtrPU, scaledSelf.width, scaledBlk.size() ).fill( 0 );

    unsigned *idxPtrTU = m_tuIdx[i] + offset;
    AreaBuf<uint32_t>( idxPtrTU, scaledSelf.width, scaledBlk.size() ).fill( 0 );
  }

  //pop cu/pu/tus
  for( int i = m_numTUs; i > numTu; i-- )
  {
    m_tuCache.cache( tus.back() );
    tus.pop_back();
    m_numTUs--;
  }
  for( int i = m_numPUs; i > numPu; i-- )
  {
    m_puCache.cache( pus.back() );
    pus.pop_back();
    m_numPUs--;
  }
  for( int i = m_numCUs; i > numCu; i-- )
  {
    m_cuCache.cache( cus.back() );
    cus.pop_back();
    m_numCUs--;
  }
  for( int i = 0; i < 3; i++ )
  {
    m_offsets[i] = pOffset[i];
  }
}

CodingUnit* CodingStructure::getLumaCU( const Position &pos )
{
  const ChannelType effChType = CHANNEL_TYPE_LUMA;
  const CompArea &_blk = area.blocks[effChType];
  CHECK( !_blk.contains( pos ), "must contain the pos" );

  const unsigned idx = m_cuIdx[effChType][rsAddr( pos, _blk.pos(), _blk.width, unitScale[effChType] )];

  if (idx != 0)
  {
    return cus[idx - 1];
  }
  else
  {
    return nullptr;
  }
}
#endif
CodingUnit* CodingStructure::getCU( const Position &pos, const ChannelType effChType )
{
  const CompArea &_blk = area.blocks[effChType];
#if INTRA_RM_SMALL_BLOCK_SIZE_CONSTRAINTS
  if (!_blk.contains(pos))
#else
  if( !_blk.contains( pos ) || (treeType == TREE_C && effChType == CHANNEL_TYPE_LUMA) )
#endif
  {
#if !INTRA_RM_SMALL_BLOCK_SIZE_CONSTRAINTS
    //keep this check, which is helpful to identify bugs
    if( treeType == TREE_C && effChType == CHANNEL_TYPE_LUMA )
    {
      CHECK( parent == nullptr, "parent shall be valid; consider using function getLumaCU()" );
      CHECK( parent->treeType != TREE_D, "wrong parent treeType " );
    }
#endif
    if (parent)
    {
      return parent->getCU(pos, effChType);
    }
    else
    {
      return nullptr;
    }
  }
  else
  {
    const unsigned idx = m_cuIdx[effChType][rsAddr( pos, _blk.pos(), _blk.width, unitScale[effChType] )];

    if (idx != 0)
    {
      return cus[idx - 1];
    }
    else
    {
      return nullptr;
    }
  }
}

const CodingUnit* CodingStructure::getCU( const Position &pos, const ChannelType effChType ) const
{
  const CompArea &_blk = area.blocks[effChType];

#if GDR_ENABLED    
    Size lumaSize = slice->getPic()->Y().lumaSize();
    if (area.lumaSize() != lumaSize)
    {
      const Position posRB = (effChType == CHANNEL_TYPE_LUMA) ? area.Y().bottomRight() : area.Cb().bottomRight();
      const Position posTL = (effChType == CHANNEL_TYPE_LUMA) ? area.Y().topLeft() : area.Cb().topLeft();

      bool isTLClean = isClean(posTL, effChType);
      bool isRBClean = isClean(posRB, effChType);
      bool isSrcClean = isTLClean || isRBClean;
      bool isTarClean = isClean(pos, effChType);
      if (isSrcClean && !isTarClean) return nullptr;
    }    
#endif  

#if INTRA_RM_SMALL_BLOCK_SIZE_CONSTRAINTS
  if (!_blk.contains(pos))
#else
  if( !_blk.contains( pos ) || (treeType == TREE_C && effChType == CHANNEL_TYPE_LUMA) )
#endif
  {
#if !INTRA_RM_SMALL_BLOCK_SIZE_CONSTRAINTS
    if( treeType == TREE_C && effChType == CHANNEL_TYPE_LUMA )
    {
      CHECK( parent == nullptr, "parent shall be valid; consider using function getLumaCU()" );
      CHECK( parent->treeType != TREE_D, "wrong parent treeType" );
    }
#endif
    if (parent)
    {
      return parent->getCU(pos, effChType);
    }
    else
    {
      return nullptr;
    }
  }
  else
  {
    const unsigned idx = m_cuIdx[effChType][rsAddr( pos, _blk.pos(), _blk.width, unitScale[effChType] )];

    if (idx != 0)
    {
      return cus[idx - 1];
    }
    else
    {
      return nullptr;
    }
  }
}

PredictionUnit* CodingStructure::getPU( const Position &pos, const ChannelType effChType )
{
  const CompArea &_blk = area.blocks[effChType];

  if( !_blk.contains( pos ) )
  {
    if (parent)
    {
      return parent->getPU(pos, effChType);
    }
    else
    {
      return nullptr;
    }
  }
  else
  {
    const unsigned idx = m_puIdx[effChType][rsAddr( pos, _blk.pos(), _blk.width, unitScale[effChType] )];

    if (idx != 0)
    {
      return pus[idx - 1];
    }
    else
    {
      return nullptr;
    }
  }
}

const PredictionUnit * CodingStructure::getPU( const Position &pos, const ChannelType effChType ) const
{
  const CompArea &_blk = area.blocks[effChType];

#if GDR_ENABLED    
  Size lumaSize = slice->getPic()->Y().lumaSize();
  if (area.lumaSize() != lumaSize)
  {
    const Position posRB = (effChType == CHANNEL_TYPE_LUMA) ? area.Y().bottomRight() : area.Cb().bottomRight();
    const Position posTL = (effChType == CHANNEL_TYPE_LUMA) ? area.Y().topLeft() : area.Cb().topLeft();

    bool isTLClean = isClean(posTL, effChType);
    bool isRBClean = isClean(posRB, effChType);
    bool isSrcClean = isTLClean || isRBClean;
    bool isTarClean = isClean(pos, effChType);
    if (isSrcClean && !isTarClean) return nullptr;
}
#endif  


  if( !_blk.contains( pos ) )
  {
    if (parent)
    {
      return parent->getPU(pos, effChType);
    }
    else
    {
      return nullptr;
    }
  }
  else
  {
    const unsigned idx = m_puIdx[effChType][rsAddr( pos, _blk.pos(), _blk.width, unitScale[effChType] )];

    if (idx != 0)
    {
      return pus[idx - 1];
    }
    else
    {
      return nullptr;
    }
  }
}

TransformUnit* CodingStructure::getTU( const Position &pos, const ChannelType effChType, const int subTuIdx )
{
  const CompArea &_blk = area.blocks[effChType];

  if( !_blk.contains( pos ) )
  {
    if (parent)
    {
      return parent->getTU(pos, effChType);
    }
    else
    {
      return nullptr;
    }
  }
  else
  {
    const unsigned idx = m_tuIdx[effChType][rsAddr( pos, _blk.pos(), _blk.width, unitScale[effChType] )];

    if( idx != 0 )
    {
      unsigned extraIdx = 0;
      if( isLuma( effChType ) )
      {
        const TransformUnit& tu = *tus[idx - 1];

        if( tu.cu->ispMode ) // Intra SubPartitions mode
        {
          //we obtain the offset to index the corresponding sub-partition
          if( subTuIdx != -1 )
          {
            extraIdx = subTuIdx;
          }
          else
          {
            while( !tus[idx - 1 + extraIdx]->blocks[getFirstComponentOfChannel( effChType )].contains( pos ) )
            {
              extraIdx++;
#if !INTRA_RM_SMALL_BLOCK_SIZE_CONSTRAINTS
              CHECK( tus[idx - 1 + extraIdx]->cu->treeType == TREE_C, "tu searched by position points to a chroma tree CU" );
              CHECK( extraIdx > 3, "extraIdx > 3" );
#endif
            }
          }
        }
      }
      return tus[idx - 1 + extraIdx];
    }
    else if (m_isTuEnc)
    {
      return parent->getTU(pos, effChType);
    }
    else
    {
      return nullptr;
    }
  }
}

const TransformUnit * CodingStructure::getTU( const Position &pos, const ChannelType effChType, const int subTuIdx ) const
{
  const CompArea &_blk = area.blocks[effChType];

#if GDR_ENABLED    
  Size lumaSize = slice->getPic()->Y().lumaSize();
  if (area.lumaSize() != lumaSize)
  {
    const Position posRB = (effChType == CHANNEL_TYPE_LUMA) ? area.Y().bottomRight() : area.Cb().bottomRight();
    const Position posTL = (effChType == CHANNEL_TYPE_LUMA) ? area.Y().topLeft() : area.Cb().topLeft();

    bool isTLClean = isClean(posTL, effChType);
    bool isRBClean = isClean(posRB, effChType);
    bool isSrcClean = isTLClean || isRBClean;
    bool isTarClean = isClean(pos, effChType);
    if (isSrcClean && !isTarClean) return nullptr;
}
#endif  

  if( !_blk.contains( pos ) )
  {
    if (parent)
    {
      return parent->getTU(pos, effChType);
    }
    else
    {
      return nullptr;
    }
  }
  else
  {
    const unsigned idx = m_tuIdx[effChType][rsAddr( pos, _blk.pos(), _blk.width, unitScale[effChType] )];
    if( idx != 0 )
    {
      unsigned extraIdx = 0;
      if( isLuma( effChType ) )
      {
        const TransformUnit& tu = *tus[idx - 1];
        if( tu.cu->ispMode ) // Intra SubPartitions mode
        {
          //we obtain the offset to index the corresponding sub-partition
          if( subTuIdx != -1 )
          {
            extraIdx = subTuIdx;
          }
          else
          {
            while ( !tus[idx - 1 + extraIdx]->blocks[getFirstComponentOfChannel( effChType )].contains(pos) )
            {
              extraIdx++;
#if !INTRA_RM_SMALL_BLOCK_SIZE_CONSTRAINTS
              CHECK( tus[idx - 1 + extraIdx]->cu->treeType == TREE_C, "tu searched by position points to a chroma tree CU" );
              CHECK( extraIdx > 3, "extraIdx > 3" );
#endif
            }
          }
        }
      }
      return tus[idx - 1 + extraIdx];
    }
    else if (m_isTuEnc)
    {
      return parent->getTU(pos, effChType);
    }
    else
    {
      return nullptr;
    }
  }
}

CodingUnit& CodingStructure::addCU( const UnitArea &unit, const ChannelType chType )
{
  CodingUnit *cu = m_cuCache.get();

  cu->UnitArea::operator=( unit );
  cu->initData();
  cu->cs        = this;
  cu->slice     = nullptr;
  cu->next      = nullptr;
  cu->firstPU   = nullptr;
  cu->lastPU    = nullptr;
  cu->firstTU   = nullptr;
  cu->lastTU    = nullptr;
  cu->chType    = chType;
#if !INTRA_RM_SMALL_BLOCK_SIZE_CONSTRAINTS
  cu->treeType = treeType;
  cu->modeType = modeType;
#endif
  CodingUnit *prevCU = m_numCUs > 0 ? cus.back() : nullptr;

  if( prevCU )
  {
    prevCU->next = cu;
#if ENABLE_SPLIT_PARALLELISM

    CHECK( prevCU->cacheId != cu->cacheId, "Inconsintent cacheId between previous and current CU" );
#endif
  }

  cus.push_back( cu );

  uint32_t idx = ++m_numCUs;
  cu->idx  = idx;

  uint32_t numCh = ::getNumberValidChannels( area.chromaFormat );

  for( uint32_t i = 0; i < numCh; i++ )
  {
    if( !cu->blocks[i].valid() )
    {
      continue;
    }

    const CompArea &_selfBlk = area.blocks[i];
    const CompArea     &_blk = cu-> blocks[i];

    const UnitScale& scale = unitScale[_blk.compID];
    const Area scaledSelf  = scale.scale( _selfBlk );
    const Area scaledBlk   = scale.scale(     _blk );
    unsigned *idxPtr       = m_cuIdx[i] + rsAddr( scaledBlk.pos(), scaledSelf.pos(), scaledSelf.width );
    CHECK( *idxPtr, "Overwriting a pre-existing value, should be '0'!" );
    AreaBuf<uint32_t>( idxPtr, scaledSelf.width, scaledBlk.size() ).fill( idx );
  }

  return *cu;
}

PredictionUnit& CodingStructure::addPU( const UnitArea &unit, const ChannelType chType )
{
  PredictionUnit *pu = m_puCache.get();

  pu->UnitArea::operator=( unit );
  pu->initData();
  pu->next   = nullptr;
  pu->cs     = this;
  pu->cu     = m_isTuEnc ? cus[0] : getCU( unit.blocks[chType].pos(), chType );
  pu->chType = chType;
#if ENABLE_SPLIT_PARALLELISM

  CHECK( pu->cacheId != pu->cu->cacheId, "Inconsintent cacheId between the PU and assigned CU" );
  CHECK( pu->cu->firstPU != nullptr, "Without an RQT the firstPU should be null" );
#endif

  PredictionUnit *prevPU = m_numPUs > 0 ? pus.back() : nullptr;

  if( prevPU && prevPU->cu == pu->cu )
  {
    prevPU->next = pu;
#if ENABLE_SPLIT_PARALLELISM

    CHECK( prevPU->cacheId != pu->cacheId, "Inconsintent cacheId between previous and current PU" );
#endif
  }

  pus.push_back( pu );

  if( pu->cu->firstPU == nullptr )
  {
    pu->cu->firstPU = pu;
  }
  pu->cu->lastPU = pu;

  uint32_t idx = ++m_numPUs;
  pu->idx  = idx;

  uint32_t numCh = ::getNumberValidChannels( area.chromaFormat );
  for( uint32_t i = 0; i < numCh; i++ )
  {
    if( !pu->blocks[i].valid() )
    {
      continue;
    }

    const CompArea &_selfBlk = area.blocks[i];
    const CompArea     &_blk = pu-> blocks[i];

    const UnitScale& scale = unitScale[_blk.compID];
    const Area scaledSelf  = scale.scale( _selfBlk );
    const Area scaledBlk   = scale.scale(     _blk );
    unsigned *idxPtr       = m_puIdx[i] + rsAddr( scaledBlk.pos(), scaledSelf.pos(), scaledSelf.width );
    CHECK( *idxPtr, "Overwriting a pre-existing value, should be '0'!" );
    AreaBuf<uint32_t>( idxPtr, scaledSelf.width, scaledBlk.size() ).fill( idx );
  }

  return *pu;
}

TransformUnit& CodingStructure::addTU( const UnitArea &unit, const ChannelType chType )
{
  TransformUnit *tu = m_tuCache.get();

  tu->UnitArea::operator=( unit );
  tu->initData();
  tu->next   = nullptr;
  tu->prev   = nullptr;
  tu->cs     = this;
  tu->cu     = m_isTuEnc ? cus[0] : getCU( unit.blocks[chType].pos(), chType );
  tu->chType = chType;
#if ENABLE_SPLIT_PARALLELISM

  if( tu->cu )
  {
    CHECK(tu->cacheId != tu->cu->cacheId, "Inconsintent cacheId between the TU and assigned CU");
  }
#endif


  TransformUnit *prevTU = m_numTUs > 0 ? tus.back() : nullptr;

  if( prevTU && prevTU->cu == tu->cu )
  {
    prevTU->next = tu;
    tu->prev     = prevTU;
#if ENABLE_SPLIT_PARALLELISM

    CHECK( prevTU->cacheId != tu->cacheId, "Inconsintent cacheId between previous and current TU" );
#endif
  }

  tus.push_back( tu );

  if( tu->cu )
  {
    if( tu->cu->firstTU == nullptr )
    {
      tu->cu->firstTU = tu;
    }
    tu->cu->lastTU = tu;
  }

  uint32_t idx = ++m_numTUs;
  tu->idx  = idx;

  TCoeff *coeffs[5] = { nullptr, nullptr, nullptr, nullptr, nullptr };
#if SIGN_PREDICTION
  TCoeff *signs[5] = { nullptr, nullptr, nullptr, nullptr, nullptr };
#endif
#if REMOVE_PCM
  Pel    *pltIdx[5] = { nullptr, nullptr, nullptr, nullptr, nullptr };
#else
  Pel    *pcmbuf[5] = { nullptr, nullptr, nullptr, nullptr, nullptr };
#endif
  bool   *runType[5]   = { nullptr, nullptr, nullptr, nullptr, nullptr };

  uint32_t numCh = ::getNumberValidComponents( area.chromaFormat );

  for (uint32_t i = 0; i < numCh; i++)
  {
    if (!tu->blocks[i].valid())
    {
      continue;
    }

    if (i < ::getNumberValidChannels(area.chromaFormat))
    {
      const CompArea &_selfBlk = area.blocks[i];
      const CompArea     &_blk = tu->blocks[i];

      bool isIspTu = tu->cu != nullptr && tu->cu->ispMode && isLuma(_blk.compID);

      bool isFirstIspTu = false;
      if (isIspTu)
      {
        isFirstIspTu = CU::isISPFirst(*tu->cu, _blk, getFirstComponentOfChannel(ChannelType(i)));
      }
      if (!isIspTu || isFirstIspTu)
      {
        const UnitScale& scale = unitScale[_blk.compID];

        const Area scaledSelf = scale.scale(_selfBlk);
        const Area scaledBlk = isIspTu ? scale.scale(tu->cu->blocks[i]) : scale.scale(_blk);
        unsigned *idxPtr = m_tuIdx[i] + rsAddr(scaledBlk.pos(), scaledSelf.pos(), scaledSelf.width);
        CHECK(*idxPtr, "Overwriting a pre-existing value, should be '0'!");
        AreaBuf<uint32_t>(idxPtr, scaledSelf.width, scaledBlk.size()).fill(idx);
      }
    }

    coeffs[i] = m_coeffs[i] + m_offsets[i];
#if SIGN_PREDICTION
    signs[i]  = m_coeff_signs[i] + m_offsets[i];
#endif
#if !REMOVE_PCM
    pcmbuf[i] = m_pcmbuf[i] + m_offsets[i];
#endif

    if (i < MAX_NUM_CHANNEL_TYPE)
    {
#if REMOVE_PCM
      if (m_pltIdx[i] != nullptr)
      {
        pltIdx[i] = m_pltIdx[i] + m_offsets[i];
      }
#endif
      if (m_runType[i] != nullptr)
      {
        runType[i] = m_runType[i] + m_offsets[i];
      }
    }

    unsigned areaSize = tu->blocks[i].area();
    m_offsets[i] += areaSize;
  }
#if REMOVE_PCM
#if SIGN_PREDICTION
  tu->init(coeffs, signs, pltIdx, runType);
#else
  tu->init(coeffs, pltIdx, runType);
#endif
#else
#if SIGN_PREDICTION
  tu->init(coeffs, signs, pcmbuf, runType);
#else
  tu->init(coeffs, pcmbuf, runType);
#endif
#endif

  return *tu;
}

void CodingStructure::addEmptyTUs( Partitioner &partitioner )
{
  const UnitArea& area    = partitioner.currArea();
  bool            split   = partitioner.canSplit(TU_MAX_TR_SPLIT, *this);
  const unsigned  trDepth = partitioner.currTrDepth;

  if( split )
  {
    partitioner.splitCurrArea( TU_MAX_TR_SPLIT, *this );
    do
    {
      addEmptyTUs( partitioner );
    } while( partitioner.nextPart( *this ) );

    partitioner.exitCurrSplit();
  }
  else
  {
    TransformUnit &tu = this->addTU( CS::getArea( *this, area, partitioner.chType ), partitioner.chType );
    unsigned numBlocks = ::getNumberValidTBlocks( *this->pcv );
    for( unsigned compID = COMPONENT_Y; compID < numBlocks; compID++ )
    {
      if( tu.blocks[compID].valid() )
      {
        tu.getCoeffs( ComponentID( compID ) ).fill( 0 );
#if !REMOVE_PCM
        tu.getPcmbuf( ComponentID( compID ) ).fill( 0 );
#endif
      }
    }
    tu.depth = trDepth;
  }
}

CUTraverser CodingStructure::traverseCUs( const UnitArea& unit, const ChannelType effChType )
{
  CodingUnit* firstCU = getCU( isLuma( effChType ) ? unit.lumaPos() : unit.chromaPos(), effChType );
  CodingUnit* lastCU = firstCU;
#if !INTRA_RM_SMALL_BLOCK_SIZE_CONSTRAINTS
  if( !CS::isDualITree( *this ) ) //for a more generalized separate tree
  {
    bool bContinue = true;
    CodingUnit* currCU = firstCU;
    while( bContinue )
    {
      if( currCU == nullptr )
      {
        bContinue = false;
        lastCU = currCU;
      }
      else if( currCU->chType != effChType )
      {
        lastCU = currCU;
        currCU = currCU->next;
      }
      else
      {
        if( unit.contains( *currCU ) )
        {
          lastCU = currCU;
          currCU = currCU->next;
        }
        else
        {
          bContinue = false;
          lastCU = currCU;
        }
      }
    }
  }
  else
  {
#endif
    do
    {
    } while (lastCU && (lastCU = lastCU->next) && unit.contains(*lastCU));
#if !INTRA_RM_SMALL_BLOCK_SIZE_CONSTRAINTS
}
#endif
  return CUTraverser( firstCU, lastCU );
}

PUTraverser CodingStructure::traversePUs( const UnitArea& unit, const ChannelType effChType )
{
  PredictionUnit* firstPU = getPU( isLuma( effChType ) ? unit.lumaPos() : unit.chromaPos(), effChType );
  PredictionUnit* lastPU  = firstPU;

  do { } while( lastPU && ( lastPU = lastPU->next ) && unit.contains( *lastPU ) );

  return PUTraverser( firstPU, lastPU );
}

TUTraverser CodingStructure::traverseTUs( const UnitArea& unit, const ChannelType effChType )
{
  TransformUnit* firstTU = getTU( isLuma( effChType ) ? unit.lumaPos() : unit.chromaPos(), effChType );
  TransformUnit* lastTU  = firstTU;

  do { } while( lastTU && ( lastTU = lastTU->next ) && unit.contains( *lastTU ) );

  return TUTraverser( firstTU, lastTU );
}

cCUTraverser CodingStructure::traverseCUs( const UnitArea& unit, const ChannelType effChType ) const
{
  const CodingUnit* firstCU = getCU( isLuma( effChType ) ? unit.lumaPos() : unit.chromaPos(), effChType );
  const CodingUnit* lastCU  = firstCU;

  do { } while( lastCU && ( lastCU = lastCU->next ) && unit.contains( *lastCU ) );

  return cCUTraverser( firstCU, lastCU );
}

cPUTraverser CodingStructure::traversePUs( const UnitArea& unit, const ChannelType effChType ) const
{
  const PredictionUnit* firstPU = getPU( isLuma( effChType ) ? unit.lumaPos() : unit.chromaPos(), effChType );
  const PredictionUnit* lastPU  = firstPU;

  do { } while( lastPU && ( lastPU = lastPU->next ) && unit.contains( *lastPU ) );

  return cPUTraverser( firstPU, lastPU );
}

cTUTraverser CodingStructure::traverseTUs( const UnitArea& unit, const ChannelType effChType ) const
{
  const TransformUnit* firstTU = getTU( isLuma( effChType ) ? unit.lumaPos() : unit.chromaPos(), effChType );
  const TransformUnit* lastTU  = firstTU;

  do { } while( lastTU && ( lastTU = lastTU->next ) && unit.contains( *lastTU ) );

  return cTUTraverser( firstTU, lastTU );
}

// coding utilities

void CodingStructure::allocateVectorsAtPicLevel()
{
  const int  twice = ( !pcv->ISingleTree && slice->isIRAP() && pcv->chrFormat != CHROMA_400 ) ? 2 : 1;
  size_t allocSize = twice * unitScale[0].scale( area.blocks[0].size() ).area();

  cus.reserve( allocSize );
  pus.reserve( allocSize );
  tus.reserve( allocSize );
}

void CodingStructure::create(const ChromaFormat &_chromaFormat, const Area& _area, const bool isTopLayer, const bool isPLTused)
{
  createInternals(UnitArea(_chromaFormat, _area), isTopLayer, isPLTused);

  if (isTopLayer)
  {
    return;
  }
#if GDR_ENABLED
  m_pt = PIC_RECONSTRUCTION_0;
  m_reco0.create(area);
  m_reco1.create(area);  
  picHeader = new PicHeader();
  picHeader->initPicHeader();
#else
  m_reco.create( area );
#endif
  m_pred.create( area );
  m_resi.create( area );
  m_orgr.create( area );
}

void CodingStructure::create(const UnitArea& _unit, const bool isTopLayer, const bool isPLTused)
{
  createInternals(_unit, isTopLayer, isPLTused);

  if (isTopLayer)
  {
    return;
  }

#if GDR_ENABLED
  m_pt = PIC_RECONSTRUCTION_0;
  m_reco0.create(area);
  m_reco1.create(area);  
  picHeader = new PicHeader();
  picHeader->initPicHeader();
#else
  m_reco.create( area );
#endif
  m_pred.create( area );
  m_resi.create( area );
  m_orgr.create( area );
}

void CodingStructure::createInternals(const UnitArea& _unit, const bool isTopLayer, const bool isPLTused)
{
  area = _unit;

  memcpy( unitScale, UnitScaleArray[area.chromaFormat], sizeof( unitScale ) );

  picture = nullptr;
  parent  = nullptr;

  unsigned numCh = ::getNumberValidChannels(area.chromaFormat);

  for (unsigned i = 0; i < numCh; i++)
  {
    unsigned _area = unitScale[i].scale( area.blocks[i].size() ).area();

    m_cuIdx[i]    = _area > 0 ? new unsigned[_area] : nullptr;
    m_puIdx[i]    = _area > 0 ? new unsigned[_area] : nullptr;
    m_tuIdx[i]    = _area > 0 ? new unsigned[_area] : nullptr;
    m_isDecomp[i] = _area > 0 ? new bool    [_area] : nullptr;
  }

  numCh = getNumberValidComponents(area.chromaFormat);

  for (unsigned i = 0; i < numCh; i++)
  {
    m_offsets[i] = 0;
  }

  if( !isTopLayer ) createCoeffs(isPLTused);

  unsigned _lumaAreaScaled = g_miScaling.scale( area.lumaSize() ).area();
#if GDR_ENABLED
  m_motionBuf0 = new MotionInfo[_lumaAreaScaled];
  m_motionBuf1 = new MotionInfo[_lumaAreaScaled];
#else
  m_motionBuf       = new MotionInfo[_lumaAreaScaled];
#endif

#if JVET_W0123_TIMD_FUSION
#if GDR_ENABLED
  m_ipmBuf0 = new uint8_t[_lumaAreaScaled];
  m_ipmBuf1 = new uint8_t[_lumaAreaScaled];
#else
  m_ipmBuf          = new uint8_t[_lumaAreaScaled];
#endif
#endif
  initStructData();
}

void CodingStructure::addMiToLut(static_vector<MotionInfo, MAX_NUM_HMVP_CANDS> &lut, const MotionInfo &mi)
{
  size_t currCnt = lut.size();

  bool pruned      = false;
  int  sameCandIdx = 0;

  for (int idx = 0; idx < currCnt; idx++)
  {
    if (lut[idx] == mi)
    {
      sameCandIdx = idx;
      pruned      = true;
      break;
    }
  }

  if (pruned || currCnt == lut.capacity())
  {
    lut.erase(lut.begin() + sameCandIdx);
  }

  lut.push_back(mi);
}

void CodingStructure::resetPrevPLT(PLTBuf& prevPLT)
{
  for (int comp = 0; comp < MAX_NUM_CHANNEL_TYPE; comp++)
  {
    prevPLT.curPLTSize[comp] = 0;
  }

  for (int comp = 0; comp < MAX_NUM_COMPONENT; comp++)
  {
    memset(prevPLT.curPLT[comp], 0, MAXPLTPREDSIZE * sizeof(Pel));
  }
}

void CodingStructure::reorderPrevPLT(PLTBuf& prevPLT, uint8_t curPLTSize[MAX_NUM_CHANNEL_TYPE], Pel curPLT[MAX_NUM_COMPONENT][MAXPLTSIZE], bool reuseflag[MAX_NUM_CHANNEL_TYPE][MAXPLTPREDSIZE], uint32_t compBegin, uint32_t numComp, bool jointPLT)
{
  Pel stuffedPLT[MAX_NUM_COMPONENT][MAXPLTPREDSIZE];
  uint8_t tempCurPLTsize[MAX_NUM_CHANNEL_TYPE];
  uint8_t stuffPLTsize[MAX_NUM_COMPONENT];

  uint32_t maxPredPltSize = jointPLT ? MAXPLTPREDSIZE : MAXPLTPREDSIZE_DUALTREE;

  for (int i = compBegin; i < (compBegin + numComp); i++)
  {
    ComponentID comID = jointPLT ? (ComponentID)compBegin : ((i > 0) ? COMPONENT_Cb : COMPONENT_Y);
    tempCurPLTsize[comID] = curPLTSize[comID];
    stuffPLTsize[i] = 0;
    memcpy(stuffedPLT[i], curPLT[i], curPLTSize[comID] * sizeof(Pel));
  }

  for (int ch = compBegin; ch < (compBegin + numComp); ch++)
  {
    ComponentID comID = jointPLT ? (ComponentID)compBegin : ((ch > 0) ? COMPONENT_Cb : COMPONENT_Y);
    if (ch > 1) break;
    for (int i = 0; i < prevPLT.curPLTSize[comID]; i++)
    {
      if (tempCurPLTsize[comID] + stuffPLTsize[ch] >= maxPredPltSize)
      {
        break;
      }

      if (!reuseflag[comID][i])
      {
        if (ch == COMPONENT_Y)
        {
          stuffedPLT[0][tempCurPLTsize[comID] + stuffPLTsize[ch]] = prevPLT.curPLT[0][i];
        }
        else
        {
          stuffedPLT[1][tempCurPLTsize[comID] + stuffPLTsize[ch]] = prevPLT.curPLT[1][i];
          stuffedPLT[2][tempCurPLTsize[comID] + stuffPLTsize[ch]] = prevPLT.curPLT[2][i];
        }
        stuffPLTsize[ch]++;
      }
    }
  }

  for (int i = compBegin; i < (compBegin + numComp); i++)
  {
    ComponentID comID = jointPLT ? (ComponentID)compBegin : ((i > 0) ? COMPONENT_Cb : COMPONENT_Y);
    prevPLT.curPLTSize[comID] = curPLTSize[comID] + stuffPLTsize[comID];
    memcpy(prevPLT.curPLT[i], stuffedPLT[i], prevPLT.curPLTSize[comID] * sizeof(Pel));
    CHECK(prevPLT.curPLTSize[comID] > maxPredPltSize, " Maximum palette predictor size exceed limit");
  }
}

void CodingStructure::setPrevPLT(PLTBuf predictor)
{
  for (int comp = 0; comp < MAX_NUM_CHANNEL_TYPE; comp++)
  {
    prevPLT.curPLTSize[comp] = predictor.curPLTSize[comp];
  }
  for (int comp = 0; comp < MAX_NUM_COMPONENT; comp++)
  {
    memcpy(prevPLT.curPLT[comp], predictor.curPLT[comp], MAXPLTPREDSIZE * sizeof(Pel));
  }
}
void CodingStructure::storePrevPLT(PLTBuf& predictor)
{
  for (int comp = 0; comp < MAX_NUM_CHANNEL_TYPE; comp++)
  {
    predictor.curPLTSize[comp] = prevPLT.curPLTSize[comp];
  }
  for (int comp = 0; comp < MAX_NUM_COMPONENT; comp++)
  {
    memcpy(predictor.curPLT[comp], prevPLT.curPLT[comp], MAXPLTPREDSIZE * sizeof(Pel));
  }
}

void CodingStructure::rebindPicBufs()
{
  CHECK( parent, "rebindPicBufs can only be used for the top level CodingStructure" );

#if GDR_ENABLED    
  if (!picture->M_BUFS(0, PIC_RECONSTRUCTION).bufs.empty()) {
    m_pt = PIC_RECONSTRUCTION_0;
    m_reco0.createFromBuf(picture->M_BUFS(0, PIC_RECONSTRUCTION_0));
    m_reco1.createFromBuf(picture->M_BUFS(0, PIC_RECONSTRUCTION_1));    
  }
  else {
    m_reco0.destroy();
    m_reco1.destroy();    
  }
#else
  if (!picture->M_BUFS(0, PIC_RECONSTRUCTION).bufs.empty())
  {
    m_reco.createFromBuf(picture->M_BUFS(0, PIC_RECONSTRUCTION));
  }
  else
  {
    m_reco.destroy();
  }
#endif
  if (!picture->M_BUFS(0, PIC_PREDICTION).bufs.empty())
  {
    m_pred.createFromBuf(picture->M_BUFS(0, PIC_PREDICTION));
  }
  else
  {
    m_pred.destroy();
  }
  if (!picture->M_BUFS(0, PIC_RESIDUAL).bufs.empty())
  {
    m_resi.createFromBuf(picture->M_BUFS(0, PIC_RESIDUAL));
  }
  else
  {
    m_resi.destroy();
  }
  if( pcv->isEncoder )
  {
    if (!picture->M_BUFS(0, PIC_RESIDUAL).bufs.empty())
    {
      m_orgr.create(area.chromaFormat, area.blocks[0], pcv->maxCUWidth);
    }
    else
    {
      m_orgr.destroy();
    }
  }
}

void CodingStructure::createCoeffs(const bool isPLTused)
{
  const unsigned numCh = getNumberValidComponents( area.chromaFormat );

  for( unsigned i = 0; i < numCh; i++ )
  {
    unsigned _area = area.blocks[i].area();

    m_coeffs[i] = _area > 0 ? ( TCoeff* ) xMalloc( TCoeff, _area ) : nullptr;
#if SIGN_PREDICTION
    m_coeff_signs[i] = _area > 0 ? ( TCoeff* ) xMalloc( TCoeff, _area ) : nullptr;
#endif
#if !REMOVE_PCM
    m_pcmbuf[i] = _area > 0 ? ( Pel*    ) xMalloc( Pel,    _area ) : nullptr;
#endif
  }

  if (isPLTused)
  {
    for (unsigned i = 0; i < (isChromaEnabled(area.chromaFormat) ? 2 : 1); i++)
    {
      unsigned _area = area.blocks[i].area();

#if REMOVE_PCM
      m_pltIdx[i] = _area > 0 ? (Pel*)xMalloc(Pel, _area) : nullptr;
#endif
      m_runType[i] = _area > 0 ? (bool*)xMalloc(bool, _area) : nullptr;
    }
  }
}

void CodingStructure::destroyCoeffs()
{
  for( uint32_t i = 0; i < MAX_NUM_COMPONENT; i++ )
  {
    if (m_coeffs[i])
    {
      xFree(m_coeffs[i]);
      m_coeffs[i] = nullptr;
    }
#if SIGN_PREDICTION
    if( m_coeff_signs[i] )
    { 
      xFree( m_coeff_signs[i] );
      m_coeff_signs[i] = nullptr;
    }
#endif
#if !REMOVE_PCM
    if (m_pcmbuf[i])
    {
      xFree(m_pcmbuf[i]);
      m_pcmbuf[i] = nullptr;
    }
#endif
  }

  for (uint32_t i = 0; i < MAX_NUM_CHANNEL_TYPE; i++)
  {
#if REMOVE_PCM
    if (m_pltIdx[i])
    {
      xFree(m_pltIdx[i]);
      m_pltIdx[i] = nullptr;
    }
#endif
    if (m_runType[i])
    {
      xFree(m_runType[i]);
      m_runType[i] = nullptr;
    }
  }
}

void CodingStructure::initSubStructure( CodingStructure& subStruct, const ChannelType _chType, const UnitArea &subArea, const bool &isTuEnc )
{
  CHECK( this == &subStruct, "Trying to init self as sub-structure" );

  subStruct.useDbCost = false;
  subStruct.costDbOffset = 0;

  for( uint32_t i = 0; i < subStruct.area.blocks.size(); i++ )
  {
    CHECKD( subStruct.area.blocks[i].size() != subArea.blocks[i].size(), "Trying to init sub-structure of incompatible size" );

    subStruct.area.blocks[i].pos() = subArea.blocks[i].pos();
  }

  if( parent )
  {
    // allow this to be false at the top level (need for edge CTU's)
    CHECKD( !area.contains( subStruct.area ), "Trying to init sub-structure not contained in the parent" );
  }

  subStruct.parent    = this;
  subStruct.picture   = picture;

  subStruct.sps       = sps;
  subStruct.vps       = vps;
  subStruct.pps       = pps;

#if GDR_ENABLED
  if (!subStruct.picHeader) {
    subStruct.picHeader = new PicHeader;
    subStruct.picHeader->initPicHeader();  
  }
  *subStruct.picHeader = *picHeader;
#else
  subStruct.picHeader = picHeader;
#endif

  memcpy(subStruct.alfApss, alfApss, sizeof(alfApss));

  subStruct.lmcsAps = lmcsAps;
  subStruct.scalinglistAps = scalinglistAps;

  subStruct.slice     = slice;
  subStruct.baseQP    = baseQP;
  subStruct.prevQP[_chType]
                      = prevQP[_chType];
  subStruct.pcv       = pcv;

  subStruct.m_isTuEnc = isTuEnc;

  subStruct.motionLut = motionLut;

  subStruct.prevPLT = prevPLT;
#if !INTRA_RM_SMALL_BLOCK_SIZE_CONSTRAINTS
  subStruct.treeType  = treeType;
  subStruct.modeType  = modeType;
#endif
  subStruct.initStructData( currQP[_chType] );

  if( isTuEnc )
  {
    CHECKD( area != subStruct.area, "Trying to init sub-structure for TU-encoding of incompatible size" );

    for( const auto &pcu : cus )
    {
      CodingUnit &cu = subStruct.addCU( *pcu, _chType );

      cu = *pcu;
    }

    for( const auto &ppu : pus )
    {
      PredictionUnit &pu = subStruct.addPU( *ppu, _chType );

      pu = *ppu;
    }

    unsigned numComp = ::getNumberValidChannels( area.chromaFormat );
    for( unsigned i = 0; i < numComp; i++)
    {
      ::memcpy( subStruct.m_isDecomp[i], m_isDecomp[i], (unitScale[i].scale( area.blocks[i].size() ).area() * sizeof( bool ) ) );
    }
  }
#if GDR_ENABLED
  subStruct.m_pt = m_pt;
#endif
}

void CodingStructure::useSubStructure( const CodingStructure& subStruct, const ChannelType chType, const UnitArea &subArea, const bool cpyPred /*= true*/, const bool cpyReco /*= true*/, const bool cpyOrgResi /*= true*/, const bool cpyResi /*= true*/, const bool updateCost /*= true*/ )
{
  UnitArea clippedArea = clipArea( subArea, *picture );

  setDecomp( clippedArea );

  CPelUnitBuf subPredBuf = cpyPred ? subStruct.getPredBuf( clippedArea ) : CPelUnitBuf();
  CPelUnitBuf subResiBuf = cpyResi ? subStruct.getResiBuf( clippedArea ) : CPelUnitBuf();
  CPelUnitBuf subRecoBuf = cpyReco ? subStruct.getRecoBuf( clippedArea ) : CPelUnitBuf();

  if( parent )
  {
    // copy data to picture
    if( cpyPred )    getPredBuf   ( clippedArea ).copyFrom( subPredBuf );
    if( cpyResi )    getResiBuf   ( clippedArea ).copyFrom( subResiBuf );
    if( cpyReco )    getRecoBuf   ( clippedArea ).copyFrom( subRecoBuf );
    if( cpyOrgResi ) getOrgResiBuf( clippedArea ).copyFrom( subStruct.getOrgResiBuf( clippedArea ) );
  }

  if( cpyPred ) picture->getPredBuf( clippedArea ).copyFrom( subPredBuf );
  if( cpyResi ) picture->getResiBuf( clippedArea ).copyFrom( subResiBuf );

 #if GDR_ENABLED   
  if (isInGdrInvervalOrRecoveryPoc())
  {
    if (cpyReco) updateReconMotIPM(clippedArea, subRecoBuf); // xcomrpessCU - need
  }
  else
  {
    if (cpyReco) picture->getRecoBuf(clippedArea).copyFrom(subRecoBuf);
  }
#else
  if (cpyReco) picture->getRecoBuf(clippedArea).copyFrom(subRecoBuf);
#endif  

  if (!subStruct.m_isTuEnc && ((!slice->isIntra() || slice->getSPS()->getIBCFlag()) && chType != CHANNEL_TYPE_CHROMA))
  {
    // copy motion buffer
    MotionBuf ownMB  = getMotionBuf          ( clippedArea );
    CMotionBuf subMB = subStruct.getMotionBuf( clippedArea );

    ownMB.copyFrom( subMB );

    motionLut = subStruct.motionLut;
  }
#if JVET_W0123_TIMD_FUSION
  if (!subStruct.m_isTuEnc && chType != CHANNEL_TYPE_CHROMA)
  {
    IpmBuf ownIB  = getIpmBuf          ( clippedArea );
    CIpmBuf subIB = subStruct.getIpmBuf( clippedArea );
    ownIB.copyFrom( subIB );
  }
#endif
  prevPLT = subStruct.prevPLT;


  if ( updateCost )
  {
    fracBits += subStruct.fracBits;
    dist     += subStruct.dist;
    cost     += subStruct.cost;
    costDbOffset += subStruct.costDbOffset;
  }
  if( parent )
  {
    // allow this to be false at the top level
    CHECKD( !area.contains( subArea ), "Trying to use a sub-structure not contained in self" );
  }

  // copy the CUs over
  if( subStruct.m_isTuEnc )
  {
    // don't copy if the substruct was created for encoding of the TUs
  }
  else
  {
    for( const auto &pcu : subStruct.cus )
    {
      // add an analogue CU into own CU store
      const UnitArea &cuPatch = *pcu;
#if !INTRA_RM_SMALL_BLOCK_SIZE_CONSTRAINTS
      CodingUnit &cu = addCU( cuPatch, pcu->chType );
#else
      CodingUnit &cu = addCU(cuPatch, chType);
#endif

      // copy the CU info from subPatch
      cu = *pcu;
    }
  }

  // copy the PUs over
  if( subStruct.m_isTuEnc )
  {
    // don't copy if the substruct was created for encoding of the TUs
  }
  else
  {
    for( const auto &ppu : subStruct.pus )
    {
      // add an analogue PU into own PU store
      const UnitArea &puPatch = *ppu;
#if !INTRA_RM_SMALL_BLOCK_SIZE_CONSTRAINTS
      PredictionUnit &pu = addPU( puPatch, ppu->chType );
#else
      PredictionUnit &pu = addPU(puPatch, chType);
#endif
      // copy the PU info from subPatch
      pu = *ppu;
    }
  }
  // copy the TUs over
  for( const auto &ptu : subStruct.tus )
  {
    // add an analogue TU into own TU store
    const UnitArea &tuPatch = *ptu;
#if !INTRA_RM_SMALL_BLOCK_SIZE_CONSTRAINTS
    TransformUnit &tu = addTU( tuPatch, ptu->chType );
#else
    TransformUnit &tu = addTU(tuPatch, chType);
#endif
    // copy the TU info from subPatch
    tu = *ptu;
  }
}

void CodingStructure::copyStructure( const CodingStructure& other, const ChannelType chType, const bool copyTUs, const bool copyRecoBuf )
{
  fracBits = other.fracBits;
  dist     = other.dist;
  cost     = other.cost;
  costDbOffset = other.costDbOffset;
  CHECKD( area != other.area, "Incompatible sizes" );

  const UnitArea dualITreeArea = CS::getArea( *this, this->area, chType );

#if GDR_ENABLED
  m_pt = other.m_pt;
#endif

  // copy the CUs over
  for (const auto &pcu : other.cus)
  {
    if( !dualITreeArea.contains( *pcu ) )
    {
      continue;
    }
    // add an analogue CU into own CU store
    const UnitArea &cuPatch = *pcu;

    CodingUnit &cu = addCU(cuPatch, pcu->chType);

    // copy the CU info from subPatch
    cu = *pcu;
  }

  // copy the PUs over
  for (const auto &ppu : other.pus)
  {
    if( !dualITreeArea.contains( *ppu ) )
    {
      continue;
    }
    // add an analogue PU into own PU store
    const UnitArea &puPatch = *ppu;

    PredictionUnit &pu = addPU(puPatch, ppu->chType);
    // copy the PU info from subPatch
    pu = *ppu;
  }

  if (!other.slice->isIntra() || other.slice->getSPS()->getIBCFlag())
  {
    // copy motion buffer
    MotionBuf  ownMB = getMotionBuf();
    CMotionBuf subMB = other.getMotionBuf();

    ownMB.copyFrom( subMB );

    motionLut = other.motionLut;
  }
#if JVET_W0123_TIMD_FUSION
  IpmBuf  ownIB = getIpmBuf();
  CIpmBuf subIB = other.getIpmBuf();
  ownIB.copyFrom( subIB );
#endif
  prevPLT = other.prevPLT;

  if( copyTUs )
  {
    // copy the TUs over
    for( const auto &ptu : other.tus )
    {
      if( !dualITreeArea.contains( *ptu ) )
      {
        continue;
      }
      // add an analogue TU into own TU store
      const UnitArea &tuPatch = *ptu;
      TransformUnit &tu = addTU( tuPatch, ptu->chType );
      // copy the TU info from subPatch
      tu = *ptu;
    }
  }

  if( copyRecoBuf )
  {
    CPelUnitBuf recoBuf = other.getRecoBuf( area );

    if( parent )
    {
      // copy data to self for neighbors
      getRecoBuf( area ).copyFrom( recoBuf );
    }

    // copy data to picture
#if GDR_ENABLED   
    if (isInGdrInvervalOrRecoveryPoc())
    {
      updateReconMotIPM(area, recoBuf); // xcomrpessCU - need
    }
    else
    {
      picture->getRecoBuf(area).copyFrom(recoBuf);
    }
#else
    picture->getRecoBuf(area).copyFrom(recoBuf);
#endif
    
    if (other.pcv->isEncoder)
    {
      CPelUnitBuf predBuf = other.getPredBuf(area);
      if (parent)
      {
        getPredBuf(area).copyFrom(predBuf);
      }
      picture->getPredBuf(area).copyFrom(predBuf);
    }

    // required for DebugCTU
    int numCh = ::getNumberValidChannels( area.chromaFormat );
    for( int i = 0; i < numCh; i++ )
    {
      const size_t _area = unitScale[i].scaleArea( area.blocks[i].area() );

      memcpy( m_isDecomp[i], other.m_isDecomp[i], sizeof( *m_isDecomp[0] ) * _area );
    }
  }
}

void CodingStructure::initStructData( const int &QP, const bool &skipMotBuf )
{
  clearPUs();
  clearTUs();
  clearCUs();

  if( QP < MAX_INT )
  {
    currQP[0] = currQP[1] = QP;
  }

  if (!skipMotBuf && (!parent || ((!slice->isIntra() || slice->getSPS()->getIBCFlag()) && !m_isTuEnc)))
  {
#if GDR_ENABLED
    getMotionBuf(PIC_RECONSTRUCTION_0).memset(0);
    getMotionBuf(PIC_RECONSTRUCTION_1).memset(0);
#else
    getMotionBuf().memset(0);
#endif
  }
#if JVET_W0123_TIMD_FUSION
#if GDR_ENABLED
  getIpmBuf(PIC_RECONSTRUCTION_0).memset(0);
  getIpmBuf(PIC_RECONSTRUCTION_1).memset(0);
#else
  getIpmBuf().memset(0);
#endif
#endif

  fracBits = 0;
  dist     = 0;
  cost     = MAX_DOUBLE;
  lumaCost = MAX_DOUBLE;
  costDbOffset = 0;
  useDbCost = false;
  interHad = std::numeric_limits<Distortion>::max();

#if GDR_ENABLED
  m_pt = PIC_RECONSTRUCTION_0;
#endif

#if MULTI_HYP_PRED
  m_MEResults.clear();
#endif
}


void CodingStructure::clearTUs()
{
  int numCh = ::getNumberValidChannels( area.chromaFormat );
  for( int i = 0; i < numCh; i++ )
  {
    size_t _area = ( area.blocks[i].area() >> unitScale[i].area );

    memset( m_isDecomp[i], false, sizeof( *m_isDecomp[0] ) * _area );
    memset( m_tuIdx   [i],     0, sizeof( *m_tuIdx   [0] ) * _area );
  }

  numCh = getNumberValidComponents( area.chromaFormat );
  for( int i = 0; i < numCh; i++ )
  {
    m_offsets[i] = 0;
  }

  for( auto &pcu : cus )
  {
    pcu->firstTU = pcu->lastTU = nullptr;
  }

  m_tuCache.cache( tus );
  m_numTUs = 0;
}

void CodingStructure::clearPUs()
{
  int numCh = ::getNumberValidChannels( area.chromaFormat );
  for( int i = 0; i < numCh; i++ )
  {
    memset( m_puIdx[i], 0, sizeof( *m_puIdx[0] ) * unitScale[i].scaleArea( area.blocks[i].area() ) );
  }

  m_puCache.cache( pus );
  m_numPUs = 0;

  for( auto &pcu : cus )
  {
    pcu->firstPU = pcu->lastPU = nullptr;
  }
}

void CodingStructure::clearCUs()
{
  int numCh = ::getNumberValidChannels( area.chromaFormat );
  for( int i = 0; i < numCh; i++ )
  {
    memset( m_cuIdx[i], 0, sizeof( *m_cuIdx[0] ) * unitScale[i].scaleArea( area.blocks[i].area() ) );
  }

  m_cuCache.cache( cus );
  m_numCUs = 0;
}

MotionBuf CodingStructure::getMotionBuf( const Area& _area )
{
#if GDR_ENABLED  
  return getMotionBuf(_area, m_pt);
#else
  const CompArea& _luma = area.Y();

  CHECKD( !_luma.contains( _area ), "Trying to access motion information outside of this coding structure" );

  const Area miArea   = g_miScaling.scale( _area );
  const Area selfArea = g_miScaling.scale( _luma );

  return MotionBuf( m_motionBuf + rsAddr( miArea.pos(), selfArea.pos(), selfArea.width ), selfArea.width, miArea.size() );
#endif
}

const CMotionBuf CodingStructure::getMotionBuf( const Area& _area ) const
{
#if GDR_ENABLED  
  return getMotionBuf(_area, m_pt);
#else
  const CompArea& _luma = area.Y();

  CHECKD( !_luma.contains( _area ), "Trying to access motion information outside of this coding structure" );

  const Area miArea   = g_miScaling.scale( _area );
  const Area selfArea = g_miScaling.scale( _luma );

  return MotionBuf( m_motionBuf + rsAddr( miArea.pos(), selfArea.pos(), selfArea.width ), selfArea.width, miArea.size() );
#endif
}

#if JVET_W0123_TIMD_FUSION && RPR_ENABLE
bool  CodingStructure::picContain( const Position _pos ) 
{
  const Picture* pic = picture->unscaledPic;
  const int width   = pic->getPicWidthInLumaSamples();
  const int height  = pic->getPicHeightInLumaSamples();
  return (_pos.x >= 0) && (_pos.x < width) && (_pos.y >= 0) && (_pos.y < height);
}
#endif

MotionInfo& CodingStructure::getMotionInfo( const Position& pos )
{
#if GDR_ENABLED 
  return getMotionInfo(pos, m_pt);
#else
#if JVET_W0123_TIMD_FUSION && RPR_ENABLE
  CHECKD( !picContain( pos ), "Trying to access motion information outside of this coding structure");
#else
  CHECKD( !area.Y().contains( pos ), "Trying to access motion information outside of this coding structure" );
#endif

  //return getMotionBuf().at( g_miScaling.scale( pos - area.lumaPos() ) );
  // bypass the motion buf calling and get the value directly
  const unsigned stride = g_miScaling.scaleHor( area.lumaSize().width );
  const Position miPos  = g_miScaling.scale( pos - area.lumaPos() );

  return *( m_motionBuf + miPos.y * stride + miPos.x );
#endif
}

const MotionInfo& CodingStructure::getMotionInfo( const Position& pos ) const
{
#if GDR_ENABLED 
  return getMotionInfo(pos, m_pt);;
#else
  CHECKD( !area.Y().contains( pos ), "Trying to access motion information outside of this coding structure" );

  //return getMotionBuf().at( g_miScaling.scale( pos - area.lumaPos() ) );
  // bypass the motion buf calling and get the value directly
  const unsigned stride = g_miScaling.scaleHor( area.lumaSize().width );
  const Position miPos  = g_miScaling.scale( pos - area.lumaPos() );

  return *( m_motionBuf + miPos.y * stride + miPos.x );
#endif
}
#if GDR_ENABLED 
MotionBuf CodingStructure::getMotionBuf(const Area& _area, PictureType pt)
{
  const CompArea& _luma = area.Y();

  CHECKD(!_luma.contains(_area), "Trying to access motion information outside of this coding structure");

  const Area miArea = g_miScaling.scale(_area);
  const Area selfArea = g_miScaling.scale(_luma);

  return MotionBuf(((pt == PIC_RECONSTRUCTION_0) ? m_motionBuf0 : m_motionBuf1) + rsAddr(miArea.pos(), selfArea.pos(), selfArea.width), selfArea.width, miArea.size());
}

const CMotionBuf CodingStructure::getMotionBuf(const Area& _area, PictureType pt) const
{
  const CompArea& _luma = area.Y();

  CHECKD(!_luma.contains(_area), "Trying to access motion information outside of this coding structure");

  const Area miArea = g_miScaling.scale(_area);
  const Area selfArea = g_miScaling.scale(_luma);

  return MotionBuf(((pt == PIC_RECONSTRUCTION_0) ? m_motionBuf0 : m_motionBuf1) + rsAddr(miArea.pos(), selfArea.pos(), selfArea.width), selfArea.width, miArea.size());
}

MotionInfo& CodingStructure::getMotionInfo(const Position& pos, PictureType pt)
{
  CHECKD(!area.Y().contains(pos), "Trying to access motion information outside of this coding structure");

  //return getMotionBuf().at( g_miScaling.scale( pos - area.lumaPos() ) );
  // bypass the motion buf calling and get the value directly
  const unsigned stride = g_miScaling.scaleHor(area.lumaSize().width);
  const Position miPos = g_miScaling.scale(pos - area.lumaPos());

  return *(((pt == PIC_RECONSTRUCTION_0) ? m_motionBuf0 : m_motionBuf1) + miPos.y * stride + miPos.x);
}

const MotionInfo& CodingStructure::getMotionInfo(const Position& pos, PictureType pt) const
{
  CHECKD(!area.Y().contains(pos), "Trying to access motion information outside of this coding structure");

  //return getMotionBuf().at( g_miScaling.scale( pos - area.lumaPos() ) );
  // bypass the motion buf calling and get the value directly
  const unsigned stride = g_miScaling.scaleHor(area.lumaSize().width);
  const Position miPos = g_miScaling.scale(pos - area.lumaPos());

  return *(((pt == PIC_RECONSTRUCTION_0) ? m_motionBuf0 : m_motionBuf1) + miPos.y * stride + miPos.x);
}
#endif

#if JVET_W0123_TIMD_FUSION
IpmBuf CodingStructure::getIpmBuf( const Area& _area )
{
#if GDR_ENABLED  
  return getIpmBuf(_area, m_pt);
#else
  const CompArea& _luma = area.Y();

  CHECKD( !_luma.contains( _area ), "Trying to access motion information outside of this coding structure" );

  const Area miArea   = g_miScaling.scale( _area );
  const Area selfArea = g_miScaling.scale( _luma );

  return IpmBuf( m_ipmBuf + rsAddr( miArea.pos(), selfArea.pos(), selfArea.width ), selfArea.width, miArea.size() );
#endif
}

const CIpmBuf CodingStructure::getIpmBuf( const Area& _area ) const
{
#if GDR_ENABLED  
  return getIpmBuf(_area, m_pt);
#else
  const CompArea& _luma = area.Y();

  CHECKD( !_luma.contains( _area ), "Trying to access motion information outside of this coding structure" );

  const Area miArea   = g_miScaling.scale( _area );
  const Area selfArea = g_miScaling.scale( _luma );

  return IpmBuf( m_ipmBuf + rsAddr( miArea.pos(), selfArea.pos(), selfArea.width ), selfArea.width, miArea.size() );
#endif
}

uint8_t& CodingStructure::getIpmInfo( const Position& pos )
{
#if GDR_ENABLED  
  return getIpmInfo(pos, m_pt);
#else
#if RPR_ENABLE
  CHECKD( !picContain( pos ), "Trying to access motion information outside of this coding structure");
#else
  CHECKD( !area.Y().contains( pos ), "Trying to access motion information outside of this coding structure" );
#endif

  //return getIpmBuf().at( g_miScaling.scale( pos - area.lumaPos() ) );
  // bypass the intra prediction mode buf calling and get the value directly
  const unsigned stride = g_miScaling.scaleHor( area.lumaSize().width );
  const Position miPos  = g_miScaling.scale( pos - area.lumaPos() );

  return *( m_ipmBuf + miPos.y * stride + miPos.x );
#endif
}

const uint8_t& CodingStructure::getIpmInfo( const Position& pos ) const
{
#if GDR_ENABLED  
  return getIpmInfo(pos, m_pt);
#else
  CHECKD( !area.Y().contains( pos ), "Trying to access motion information outside of this coding structure" );

  //return getIpmBuf().at( g_miScaling.scale( pos - area.lumaPos() ) );
  // bypass the intra prediction mode buf calling and get the value directly
  const unsigned stride = g_miScaling.scaleHor( area.lumaSize().width );
  const Position miPos  = g_miScaling.scale( pos - area.lumaPos() );

  return *( m_ipmBuf + miPos.y * stride + miPos.x );
#endif
}
#endif

#if JVET_W0123_TIMD_FUSION && GDR_ENABLED
IpmBuf CodingStructure::getIpmBuf(const Area& _area, PictureType pt)
{
  const CompArea& _luma = area.Y();

  CHECKD(!_luma.contains(_area), "Trying to access motion information outside of this coding structure");

  const Area miArea = g_miScaling.scale(_area);
  const Area selfArea = g_miScaling.scale(_luma);

  return IpmBuf(((pt == PIC_RECONSTRUCTION_0) ? m_ipmBuf0 : m_ipmBuf1) + rsAddr(miArea.pos(), selfArea.pos(), selfArea.width), selfArea.width, miArea.size());
}

const CIpmBuf CodingStructure::getIpmBuf(const Area& _area, PictureType pt) const
{
  const CompArea& _luma = area.Y();

  CHECKD(!_luma.contains(_area), "Trying to access motion information outside of this coding structure");

  const Area miArea = g_miScaling.scale(_area);
  const Area selfArea = g_miScaling.scale(_luma);

  return IpmBuf(((pt == PIC_RECONSTRUCTION_0) ? m_ipmBuf0 : m_ipmBuf1) + rsAddr(miArea.pos(), selfArea.pos(), selfArea.width), selfArea.width, miArea.size());
}

uint8_t& CodingStructure::getIpmInfo(const Position& pos, PictureType pt)
{
#if RPR_ENABLE
  CHECKD( !picContain( pos ), "Trying to access motion information outside of this coding structure");
#else
  CHECKD( !area.Y().contains( pos ), "Trying to access motion information outside of this coding structure" );
#endif

  //return getIpmBuf().at( g_miScaling.scale( pos - area.lumaPos() ) );
  // bypass the intra prediction mode buf calling and get the value directly
  const unsigned stride = g_miScaling.scaleHor( area.lumaSize().width );
  const Position miPos  = g_miScaling.scale( pos - area.lumaPos() );

  return *(((pt == PIC_RECONSTRUCTION_0) ? m_ipmBuf0 : m_ipmBuf1) + miPos.y * stride + miPos.x);
}

const uint8_t& CodingStructure::getIpmInfo(const Position& pos, PictureType pt) const
{
  CHECKD( !area.Y().contains( pos ), "Trying to access motion information outside of this coding structure" );

  //return getIpmBuf().at( g_miScaling.scale( pos - area.lumaPos() ) );
  // bypass the intra prediction mode buf calling and get the value directly
  const unsigned stride = g_miScaling.scaleHor( area.lumaSize().width );
  const Position miPos  = g_miScaling.scale( pos - area.lumaPos() );

  return *(((pt == PIC_RECONSTRUCTION_0) ? m_ipmBuf0 : m_ipmBuf1) + miPos.y * stride + miPos.x);
}
#endif


// data accessors
       PelBuf     CodingStructure::getPredBuf(const CompArea &blk)           { return getBuf(blk,  PIC_PREDICTION); }
const CPelBuf     CodingStructure::getPredBuf(const CompArea &blk)     const { return getBuf(blk,  PIC_PREDICTION); }
       PelUnitBuf CodingStructure::getPredBuf(const UnitArea &unit)          { return getBuf(unit, PIC_PREDICTION); }
const CPelUnitBuf CodingStructure::getPredBuf(const UnitArea &unit)    const { return getBuf(unit, PIC_PREDICTION); }

       PelBuf     CodingStructure::getResiBuf(const CompArea &blk)           { return getBuf(blk,  PIC_RESIDUAL); }
const CPelBuf     CodingStructure::getResiBuf(const CompArea &blk)     const { return getBuf(blk,  PIC_RESIDUAL); }
       PelUnitBuf CodingStructure::getResiBuf(const UnitArea &unit)          { return getBuf(unit, PIC_RESIDUAL); }
const CPelUnitBuf CodingStructure::getResiBuf(const UnitArea &unit)    const { return getBuf(unit, PIC_RESIDUAL); }

#if GDR_ENABLED
       PelBuf     CodingStructure::getRecoBuf(const CompArea &blk)           { return getBuf(blk, m_pt);  }
const CPelBuf     CodingStructure::getRecoBuf(const CompArea &blk)     const { return getBuf(blk, m_pt);  }
       PelUnitBuf CodingStructure::getRecoBuf(const UnitArea &unit)          { return getBuf(unit, m_pt); }
const CPelUnitBuf CodingStructure::getRecoBuf(const UnitArea &unit)    const { return getBuf(unit, m_pt); }
#else
       PelBuf     CodingStructure::getRecoBuf(const CompArea &blk)           { return getBuf(blk,  PIC_RECONSTRUCTION); }
const CPelBuf     CodingStructure::getRecoBuf(const CompArea &blk)     const { return getBuf(blk,  PIC_RECONSTRUCTION); }
       PelUnitBuf CodingStructure::getRecoBuf(const UnitArea &unit)          { return getBuf(unit, PIC_RECONSTRUCTION); }
const CPelUnitBuf CodingStructure::getRecoBuf(const UnitArea &unit)    const { return getBuf(unit, PIC_RECONSTRUCTION); }
#endif
       PelBuf     CodingStructure::getOrgResiBuf(const CompArea &blk)        { return getBuf(blk,  PIC_ORG_RESI); }
const CPelBuf     CodingStructure::getOrgResiBuf(const CompArea &blk)  const { return getBuf(blk,  PIC_ORG_RESI); }
       PelUnitBuf CodingStructure::getOrgResiBuf(const UnitArea &unit)       { return getBuf(unit, PIC_ORG_RESI); }
const CPelUnitBuf CodingStructure::getOrgResiBuf(const UnitArea &unit) const { return getBuf(unit, PIC_ORG_RESI); }

       PelBuf     CodingStructure::getOrgBuf(const CompArea &blk)            { return getBuf(blk,  PIC_ORIGINAL); }
const CPelBuf     CodingStructure::getOrgBuf(const CompArea &blk)      const { return getBuf(blk,  PIC_ORIGINAL); }
       PelUnitBuf CodingStructure::getOrgBuf(const UnitArea &unit)           { return getBuf(unit, PIC_ORIGINAL); }
const CPelUnitBuf CodingStructure::getOrgBuf(const UnitArea &unit)     const { return getBuf(unit, PIC_ORIGINAL); }

       PelBuf     CodingStructure::getOrgBuf(const ComponentID &compID)      { return picture->getBuf(area.blocks[compID], PIC_ORIGINAL); }
const CPelBuf     CodingStructure::getOrgBuf(const ComponentID &compID)const { return picture->getBuf(area.blocks[compID], PIC_ORIGINAL); }
       PelUnitBuf CodingStructure::getOrgBuf()                               { return picture->getBuf(area, PIC_ORIGINAL); }
const CPelUnitBuf CodingStructure::getOrgBuf()                         const { return picture->getBuf(area, PIC_ORIGINAL); }
#if ALF_SAO_TRUE_ORG
       PelUnitBuf CodingStructure::getTrueOrgBuf()                           { return picture->getBuf(area, PIC_TRUE_ORIGINAL); }
const CPelUnitBuf CodingStructure::getTrueOrgBuf()                     const { return picture->getBuf(area, PIC_TRUE_ORIGINAL); }
#endif

PelBuf CodingStructure::getBuf( const CompArea &blk, const PictureType &type )
{
  if (!blk.valid())
  {
    return PelBuf();
  }

  if (type == PIC_ORIGINAL)
  {
    return picture->getBuf(blk, type);
  }

  const ComponentID compID = blk.compID;
  
#if GDR_ENABLED  
  PelStorage* buf = type == PIC_PREDICTION ? &m_pred : (type == PIC_RESIDUAL ? &m_resi : (type == PIC_RECONSTRUCTION_0 ? &m_reco0 : (type == PIC_RECONSTRUCTION_1 ? &m_reco1 : (type == PIC_ORG_RESI ? &m_orgr : nullptr))));
#else
  PelStorage* buf = type == PIC_PREDICTION ? &m_pred : ( type == PIC_RESIDUAL ? &m_resi : ( type == PIC_RECONSTRUCTION ? &m_reco : ( type == PIC_ORG_RESI ? &m_orgr : nullptr ) ) );
#endif  

  CHECK( !buf, "Unknown buffer requested" );

  CHECKD( !area.blocks[compID].contains( blk ), "Buffer not contained in self requested" );

  CompArea cFinal = blk;
  cFinal.relativeTo( area.blocks[compID] );

#if !KEEP_PRED_AND_RESI_SIGNALS
  if( !parent && ( type == PIC_RESIDUAL || type == PIC_PREDICTION ) )
  {
    cFinal.x &= ( pcv->maxCUWidthMask  >> getComponentScaleX( blk.compID, blk.chromaFormat ) );
    cFinal.y &= ( pcv->maxCUHeightMask >> getComponentScaleY( blk.compID, blk.chromaFormat ) );
  }
#endif

  return buf->getBuf( cFinal );
}

const CPelBuf CodingStructure::getBuf( const CompArea &blk, const PictureType &type ) const
{
  if (!blk.valid())
  {
    return PelBuf();
  }

  if (type == PIC_ORIGINAL)
  {
    return picture->getBuf(blk, type);
  }

  const ComponentID compID = blk.compID;
#if GDR_ENABLED
  const PelStorage* buf = type == PIC_PREDICTION ? &m_pred : ( type == PIC_RESIDUAL ? &m_resi : (type == PIC_RECONSTRUCTION_0 ? &m_reco0 : (type == PIC_RECONSTRUCTION_1 ? &m_reco1 : (type == PIC_ORG_RESI ? &m_orgr : nullptr ))));
#else
  const PelStorage* buf = type == PIC_PREDICTION ? &m_pred : ( type == PIC_RESIDUAL ? &m_resi : ( type == PIC_RECONSTRUCTION ? &m_reco : ( type == PIC_ORG_RESI ? &m_orgr : nullptr ) ) );
#endif

  CHECK( !buf, "Unknown buffer requested" );

  CHECKD( !area.blocks[compID].contains( blk ), "Buffer not contained in self requested" );

  CompArea cFinal = blk;
  cFinal.relativeTo( area.blocks[compID] );

#if !KEEP_PRED_AND_RESI_SIGNALS
  if( !parent && ( type == PIC_RESIDUAL || type == PIC_PREDICTION ) )
  {
    cFinal.x &= ( pcv->maxCUWidthMask  >> getComponentScaleX( blk.compID, blk.chromaFormat ) );
    cFinal.y &= ( pcv->maxCUHeightMask >> getComponentScaleY( blk.compID, blk.chromaFormat ) );
  }
#endif

  return buf->getBuf( cFinal );
}

PelUnitBuf CodingStructure::getBuf( const UnitArea &unit, const PictureType &type )
{
  // no parent fetching for buffers
  if( area.chromaFormat == CHROMA_400 )
  {
    return PelUnitBuf( area.chromaFormat, getBuf( unit.Y(), type ) );
  }
  else
  {
    return PelUnitBuf( area.chromaFormat, getBuf( unit.Y(), type ), getBuf( unit.Cb(), type ), getBuf( unit.Cr(), type ) );
  }
}

const CPelUnitBuf CodingStructure::getBuf( const UnitArea &unit, const PictureType &type ) const
{
  // no parent fetching for buffers
  if( area.chromaFormat == CHROMA_400 )
  {
    return CPelUnitBuf( area.chromaFormat, getBuf( unit.Y(), type ) );
  }
  else
  {
    return CPelUnitBuf( area.chromaFormat, getBuf( unit.Y(), type ), getBuf( unit.Cb(), type ), getBuf( unit.Cr(), type ) );
  }
}

const CodingUnit* CodingStructure::getCURestricted( const Position &pos, const CodingUnit& curCu, const ChannelType _chType ) const
{
  const CodingUnit* cu = getCU( pos, _chType );
  // exists       same slice and tile                  cu precedes curCu in encoding order
  //                                                  (thus, is either from parent CS in RD-search or its index is lower)
  const bool wavefrontsEnabled = curCu.slice->getSPS()->getEntropyCodingSyncEnabledFlag();
  int ctuSizeBit = floorLog2(curCu.cs->sps->getMaxCUWidth());
  int xNbY  = pos.x << getChannelTypeScaleX( _chType, curCu.chromaFormat );
  int xCurr = curCu.blocks[_chType].x << getChannelTypeScaleX( _chType, curCu.chromaFormat );
  bool addCheck = (wavefrontsEnabled && (xNbY >> ctuSizeBit) >= (xCurr >> ctuSizeBit) + 1 ) ? false : true;
  if( cu && CU::isSameSliceAndTile( *cu, curCu ) && ( cu->cs != curCu.cs || cu->idx <= curCu.idx ) && addCheck)
  {
#if GDR_ENABLED  
    if (cu)
    {
      const Position posRB = (_chType == CHANNEL_TYPE_LUMA) ? curCu.Y().bottomRight() : curCu.Cb().bottomRight();
      const Position posTL = (_chType == CHANNEL_TYPE_LUMA) ? curCu.Y().topLeft() : curCu.Cb().topLeft();
      bool isTLClean = isClean(posTL, _chType);
      bool isRBClean = isClean(posRB, _chType);
      bool isSrcClean = isTLClean || isRBClean;
      bool isTarClean = isClean(pos, _chType);
      if (isSrcClean && !isTarClean) return nullptr;
    }
#endif
    return cu;
  }
  else
  {
    return nullptr;
  }
}

const CodingUnit* CodingStructure::getCURestricted( const Position &pos, const Position curPos, const unsigned curSliceIdx, const unsigned curTileIdx, const ChannelType _chType ) const
{
  const CodingUnit* cu = getCU( pos, _chType );
  const bool wavefrontsEnabled = this->slice->getSPS()->getEntropyCodingSyncEnabledFlag();
  int ctuSizeBit = floorLog2(this->sps->getMaxCUWidth());
  int xNbY  = pos.x << getChannelTypeScaleX( _chType, this->area.chromaFormat );
  int xCurr = curPos.x << getChannelTypeScaleX( _chType, this->area.chromaFormat );
  bool addCheck = (wavefrontsEnabled && (xNbY >> ctuSizeBit) >= (xCurr >> ctuSizeBit) + 1 ) ? false : true;

#if GDR_ENABLED  
  if (cu)
  {            
    bool isSrcClean = isClean(curPos, _chType);
    bool isTarClean = isClean(pos, _chType);
    if (isSrcClean && !isTarClean) return nullptr;
  }
#endif

  return ( cu && cu->slice->getIndependentSliceIdx() == curSliceIdx && cu->tileIdx == curTileIdx && addCheck ) ? cu : nullptr;
}

const PredictionUnit* CodingStructure::getPURestricted( const Position &pos, const PredictionUnit& curPu, const ChannelType _chType ) const
{
  const PredictionUnit* pu = getPU( pos, _chType );
  // exists       same slice and tile                  pu precedes curPu in encoding order
  //                                                  (thus, is either from parent CS in RD-search or its index is lower)
  const bool wavefrontsEnabled = curPu.cu->slice->getSPS()->getEntropyCodingSyncEnabledFlag();
  int ctuSizeBit = floorLog2(curPu.cs->sps->getMaxCUWidth());
  int xNbY  = pos.x << getChannelTypeScaleX( _chType, curPu.chromaFormat );
  int xCurr = curPu.blocks[_chType].x << getChannelTypeScaleX( _chType, curPu.chromaFormat );
  bool addCheck = (wavefrontsEnabled && (xNbY >> ctuSizeBit) >= (xCurr >> ctuSizeBit) + 1 ) ? false : true;
  if( pu && CU::isSameSliceAndTile( *pu->cu, *curPu.cu ) && ( pu->cs != curPu.cs || pu->idx <= curPu.idx ) && addCheck )
  {
#if GDR_ENABLED  
    if (pu)
    {      
      const Position posRB = (_chType == CHANNEL_TYPE_LUMA) ? curPu.Y().bottomRight() : curPu.Cb().bottomRight();
      const Position posTL = (_chType == CHANNEL_TYPE_LUMA) ? curPu.Y().topLeft() : curPu.Cb().topLeft();
      bool isTLClean = isClean(posTL, _chType);
      bool isRBClean = isClean(posRB, _chType);
      bool isSrcClean = isTLClean || isRBClean;
      bool isTarClean = isClean(pos, _chType);
      if (isSrcClean && !isTarClean) return nullptr;
    }
#endif
    return pu;
  }
  else
  {
    return nullptr;
  }
}

const TransformUnit* CodingStructure::getTURestricted( const Position &pos, const TransformUnit& curTu, const ChannelType _chType ) const
{
  const TransformUnit* tu = getTU( pos, _chType );
  // exists       same slice and tile                  tu precedes curTu in encoding order
  //                                                  (thus, is either from parent CS in RD-search or its index is lower)
  const bool wavefrontsEnabled = curTu.cu->slice->getSPS()->getEntropyCodingSyncEnabledFlag();
  int ctuSizeBit = floorLog2(curTu.cs->sps->getMaxCUWidth());
  int xNbY  = pos.x << getChannelTypeScaleX( _chType, curTu.chromaFormat );
  int xCurr = curTu.blocks[_chType].x << getChannelTypeScaleX( _chType, curTu.chromaFormat );
  bool addCheck = (wavefrontsEnabled && (xNbY >> ctuSizeBit) >= (xCurr >> ctuSizeBit) + 1 ) ? false : true;
  if( tu && CU::isSameSliceAndTile( *tu->cu, *curTu.cu ) && ( tu->cs != curTu.cs || tu->idx <= curTu.idx ) && addCheck )
  {
#if GDR_ENABLED  
    if (tu)
    {
      const Position posRB = (_chType == CHANNEL_TYPE_LUMA) ? curTu.Y().bottomRight() : curTu.Cb().bottomRight();
      const Position posTL = (_chType == CHANNEL_TYPE_LUMA) ? curTu.Y().topLeft() : curTu.Cb().topLeft();
      bool isTLClean = isClean(posTL, _chType);
      bool isRBClean = isClean(posRB, _chType);
      bool isSrcClean = isTLClean || isRBClean;
      bool isTarClean = isClean(pos, _chType);      
      if (isSrcClean && !isTarClean) return nullptr;
    }
#endif
    return tu;
  }
  else
  {
    return nullptr;
  }
}

