From b7228c35f0366b18eaf4757c556351d54290f98e Mon Sep 17 00:00:00 2001
From: Chia-Ming Tsai <chia-ming.tsai@mediatek.com>
Date: Mon, 8 May 2023 17:42:14 +0000
Subject: [PATCH] JVET-AD0188 (EE2 Test-1.6c): Non-local cross-component
 prediction and cross-component merge mode

---
 source/App/EncoderApp/EncApp.cpp         |    3 +
 source/App/EncoderApp/EncAppCfg.cpp      |    3 +
 source/App/EncoderApp/EncAppCfg.h        |    3 +
 source/Lib/CommonLib/CodingStructure.cpp |   13 +-
 source/Lib/CommonLib/CodingStructure.h   |   45 +
 source/Lib/CommonLib/Common.h            |  258 ++
 source/Lib/CommonLib/CommonDef.h         |    9 +
 source/Lib/CommonLib/Contexts.cpp        |   17 +
 source/Lib/CommonLib/Contexts.h          |    3 +
 source/Lib/CommonLib/IntraPrediction.cpp | 3320 +++++++++++++++++++++-
 source/Lib/CommonLib/IntraPrediction.h   |   41 +-
 source/Lib/CommonLib/Slice.h             |    8 +-
 source/Lib/CommonLib/TypeDef.h           |    1 +
 source/Lib/CommonLib/Unit.cpp            |   13 +
 source/Lib/CommonLib/Unit.h              |    4 +
 source/Lib/CommonLib/UnitTools.cpp       |  462 +++
 source/Lib/CommonLib/UnitTools.h         |   29 +
 source/Lib/DecoderLib/CABACReader.cpp    |   26 +
 source/Lib/DecoderLib/CABACReader.h      |    3 +
 source/Lib/DecoderLib/DecCu.cpp          |  100 +
 source/Lib/DecoderLib/DecSlice.cpp       |   13 +-
 source/Lib/DecoderLib/VLCReader.cpp      |    3 +
 source/Lib/EncoderLib/CABACWriter.cpp    |   24 +
 source/Lib/EncoderLib/CABACWriter.h      |    3 +
 source/Lib/EncoderLib/EncCfg.h           |    7 +
 source/Lib/EncoderLib/EncCu.cpp          |   23 +
 source/Lib/EncoderLib/EncLib.cpp         |    3 +
 source/Lib/EncoderLib/EncSlice.cpp       |   13 +
 source/Lib/EncoderLib/IntraSearch.cpp    |  291 +-
 source/Lib/EncoderLib/IntraSearch.h      |   12 +
 source/Lib/EncoderLib/VLCWriter.cpp      |    4 +
 31 files changed, 4604 insertions(+), 153 deletions(-)

diff --git a/source/App/EncoderApp/EncApp.cpp b/source/App/EncoderApp/EncApp.cpp
index 5b7ec86f3..da2cd59cc 100644
--- a/source/App/EncoderApp/EncApp.cpp
+++ b/source/App/EncoderApp/EncApp.cpp
@@ -900,6 +900,9 @@ void EncApp::xInitLibCfg()
 #if JVET_AC0147_CCCM_NO_SUBSAMPLING
   m_cEncLib.setUseCccm                                           ( m_cccm );
 #endif
+#if JVET_AD0188_CCP_MERGE
+  m_cEncLib.setUseCcpMerge                                       ( m_ccpMerge );
+#endif
 #if ENABLE_OBMC
   m_cEncLib.setUseObmc                                           ( m_OBMC );
 #endif
diff --git a/source/App/EncoderApp/EncAppCfg.cpp b/source/App/EncoderApp/EncAppCfg.cpp
index 0c18ef6a2..4c4a6731b 100644
--- a/source/App/EncoderApp/EncAppCfg.cpp
+++ b/source/App/EncoderApp/EncAppCfg.cpp
@@ -1123,6 +1123,9 @@ bool EncAppCfg::parseCfg( int argc, char* argv[] )
 #if JVET_AC0147_CCCM_NO_SUBSAMPLING
   ( "CCCM",                                           m_cccm,                                               2,  "CCCM mode (0:off, 1:on, 2:on subsampling and no subsampling)  [default: 2]")
 #endif
+#if JVET_AD0188_CCP_MERGE
+  ( "CCPMerge",                                       m_ccpMerge,                                       true, "Enable cross-componet prediction merge mode for chroma intra coding" )
+#endif
 #if ENABLE_OBMC
   ("OBMC",                                            m_OBMC,                                           true, "Overlapping Block Motion Compensation")
 #endif
diff --git a/source/App/EncoderApp/EncAppCfg.h b/source/App/EncoderApp/EncAppCfg.h
index 32ab826e4..edec8beaf 100644
--- a/source/App/EncoderApp/EncAppCfg.h
+++ b/source/App/EncoderApp/EncAppCfg.h
@@ -478,6 +478,9 @@ protected:
 #if JVET_AC0147_CCCM_NO_SUBSAMPLING
   int       m_cccm;
 #endif
+#if JVET_AD0188_CCP_MERGE
+  bool      m_ccpMerge;
+#endif
 #if ENABLE_OBMC
   bool      m_OBMC;
 #endif
diff --git a/source/Lib/CommonLib/CodingStructure.cpp b/source/Lib/CommonLib/CodingStructure.cpp
index 4504d91bb..3fbee8ed9 100644
--- a/source/Lib/CommonLib/CodingStructure.cpp
+++ b/source/Lib/CommonLib/CodingStructure.cpp
@@ -2244,7 +2244,9 @@ void CodingStructure::initSubStructure( CodingStructure& subStruct, const Channe
   subStruct.m_isTuEnc = isTuEnc;
 
   subStruct.motionLut = motionLut;
-
+#if JVET_AD0188_CCP_MERGE
+  subStruct.ccpLut    = ccpLut;
+#endif
   subStruct.prevPLT = prevPLT;
 #if !INTRA_RM_SMALL_BLOCK_SIZE_CONSTRAINTS
   subStruct.treeType  = treeType;
@@ -2381,6 +2383,10 @@ void CodingStructure::useSubStructure( const CodingStructure& subStruct, const C
 
     motionLut = subStruct.motionLut;
   }
+#if JVET_AD0188_CCP_MERGE
+  ccpLut = subStruct.ccpLut;
+#endif
+
 #if JVET_W0123_TIMD_FUSION
   if (!subStruct.m_isTuEnc && chType != CHANNEL_TYPE_CHROMA)
   {
@@ -2529,6 +2535,11 @@ void CodingStructure::copyStructure( const CodingStructure& other, const Channel
 
     motionLut = other.motionLut;
   }
+
+#if JVET_AD0188_CCP_MERGE
+  ccpLut = other.ccpLut;
+#endif
+
 #if JVET_W0123_TIMD_FUSION
   IpmBuf  ownIB = getIpmBuf();
   CIpmBuf subIB = other.getIpmBuf();
diff --git a/source/Lib/CommonLib/CodingStructure.h b/source/Lib/CommonLib/CodingStructure.h
index b2369ba86..5a1f44fa2 100644
--- a/source/Lib/CommonLib/CodingStructure.h
+++ b/source/Lib/CommonLib/CodingStructure.h
@@ -263,6 +263,12 @@ public:
 
   LutMotionCand motionLut;
 
+#if JVET_AD0188_CCP_MERGE
+  LutCCP                 ccpLut;
+  template<class T> void addCCPToLut(static_vector<T, MAX_NUM_HCCP_CANDS> &lut, const T &model, int reusePos);
+  template<class T> void getOneModelFromCCPLut(const static_vector<T, MAX_NUM_HCCP_CANDS> &lut, T &model, int pos);
+#endif
+
   void addMiToLut(static_vector<MotionInfo, MAX_NUM_HMVP_CANDS>& lut, const MotionInfo &mi);
 #if JVET_Z0075_IBC_HMVP_ENLARGE
   void addMiToLutIBC(static_vector<MotionInfo, MAX_NUM_HMVP_IBC_CANDS>& lut, const MotionInfo &mi);
@@ -520,5 +526,44 @@ private:
 
 static inline uint32_t getNumberValidTBlocks(const PreCalcValues& pcv) { return (pcv.chrFormat==CHROMA_400) ? 1 : ( pcv.multiBlock422 ? MAX_NUM_TBLOCKS : MAX_NUM_COMPONENT ); }
 
+#if JVET_AD0188_CCP_MERGE
+template<class T>
+void CodingStructure::addCCPToLut(static_vector<T, MAX_NUM_HCCP_CANDS> &lut, const T &model, int reusePos)
+{
+  int currCnt = (int) lut.size();
+
+  int erasePos = 0;
+
+  if (reusePos == -1)
+  {
+    for (int j = 0; j < currCnt; j++)
+    {
+      if (lut[currCnt - j - 1] == model)
+      {
+        reusePos = j;
+        break;
+      }
+    }
+  }
+  if (reusePos != -1)
+  {
+    erasePos = currCnt - 1 - reusePos;   // reverse the order
+  }
+  if (reusePos != -1 || currCnt == lut.capacity())
+  {
+    lut.erase(lut.begin() + erasePos);
+  }
+  lut.push_back(model);
+}
+
+template<class T>
+void CodingStructure::getOneModelFromCCPLut(const static_vector<T, MAX_NUM_HCCP_CANDS> &lut, T &model, int pos)
+{
+  size_t currCnt = lut.size();
+  CHECK(pos >= currCnt, "Invalid entry in CCP LUT");
+  model = lut[currCnt - pos - 1];
+}
+
+#endif
 #endif
 
diff --git a/source/Lib/CommonLib/Common.h b/source/Lib/CommonLib/Common.h
index 11e81d98d..7a807bd7e 100644
--- a/source/Lib/CommonLib/Common.h
+++ b/source/Lib/CommonLib/Common.h
@@ -295,6 +295,264 @@ public:
   }
 };
 
+#if JVET_AD0188_CCP_MERGE
+enum CCPType
+{
+  CCP_TYPE_NONE    = 0,
+  CCP_TYPE_CCLM    = 1,
+  CCP_TYPE_MMLM    = (1 << 1),
+  CCP_TYPE_GLM0123 = (1 << 2),
+  CCP_TYPE_GLM4567 = (1 << 3),
+  CCP_TYPE_CCCM    = (1 << 4),
+  CCP_TYPE_GLCCCM  = (1 << 5),
+  CCP_TYPE_NSCCCM  = (1 << 6),
+  CCP_TYPE_MDFCCCM = (1 << 7),
+  NUM_CCP_TYPE
+};
+
+struct CCPModelCandidate
+{
+  int64_t params[2][NUM_CCP_PARAMS] = { 0 };
+#if MMLM
+  int64_t params2[2][NUM_CCP_PARAMS] = { 0 };
+  int     shift2[2] = { 0 };
+  int     yThres = 0;
+#endif
+  int     shift[2] = { 0 };
+  int     bd = 0;
+  int     midVal = 0;
+  int     type = 0;
+#if JVET_AB0174_CCCM_DIV_FREE
+  int     lumaOffset = 0;
+#endif
+#if JVET_AC0054_GLCCCM || JVET_AD0202_CCCM_MDF
+  int     corOffX = 0;
+  int     corOffY = 0;
+#endif
+#if JVET_AD0202_CCCM_MDF
+  int     cccmMultiFilterIdx = 0;
+#endif
+#if JVET_AA0126_GLM
+  int8_t  glmIdc = 0;
+#endif
+
+  template<int NUM>
+  inline bool isTheSameParams(const CCPModelCandidate& p) const
+  {
+    for (int i = 0; i < NUM; ++i)
+    {
+      if (params[0][i] != p.params[0][i] || params[1][i] != p.params[1][i])
+      {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  template<int NUM>
+  inline bool isTheSameParams2(const CCPModelCandidate& p) const
+  {
+    for (int i = 0; i < NUM; ++i)
+    {
+      if (params2[0][i] != p.params2[0][i] || params2[1][i] != p.params2[1][i])
+      {
+        return false;
+      }
+    }
+    return true;
+  }
 
+  bool operator==(const CCPModelCandidate& cand) const
+  {
+    if (type != cand.type)
+    {
+      return false;
+    }
+
+    if ((type & CCP_TYPE_MMLM) != (cand.type & CCP_TYPE_MMLM))
+    {
+      return false;
+    }
+
+    if (type & (CCP_TYPE_CCCM | CCP_TYPE_GLCCCM))
+    {
+#if JVET_AB0174_CCCM_DIV_FREE
+      if (lumaOffset != cand.lumaOffset)
+      {
+        return false;
+      }
+#endif
+      if (type & CCP_TYPE_MMLM)
+      {
+        if (yThres != cand.yThres)
+        {
+          return false;
+        }
+        if (isTheSameParams<CCCM_NUM_PARAMS>(cand) && isTheSameParams2<CCCM_NUM_PARAMS>(cand))
+        {
+          return true;
+        }
+      }
+      else
+      {
+        if (isTheSameParams<CCCM_NUM_PARAMS>(cand))
+        {
+          return true;
+        }
+      }
+      return false;
+    }
+    else if (type & (CCP_TYPE_CCLM | CCP_TYPE_GLM0123))
+    {
+      if (type & CCP_TYPE_MMLM)
+      {
+        if (yThres != cand.yThres)
+        {
+          return false;
+        }
+        if (params[0][0] == cand.params[0][0] && shift[0] == cand.shift[0]
+          && params[1][0] == cand.params[1][0] && shift[1] == cand.shift[1]
+          && params2[0][0] == cand.params2[0][0] && shift2[0] == cand.shift2[0]
+          && params2[1][0] == cand.params2[1][0] && shift2[1] == cand.shift2[1])
+        {
+          return true;
+        }
+      }
+      else
+      {
+        if (params[0][0] == cand.params[0][0] && shift[0] == cand.shift[0]
+          && params[1][0] == cand.params[1][0] && shift[1] == cand.shift[1])
+        {
+          return true;
+        }
+      }
+      return false;
+    }
+    else if (type & CCP_TYPE_GLM4567)
+    {
+#if JVET_AB0174_CCCM_DIV_FREE
+      if (lumaOffset != cand.lumaOffset)
+      {
+        return false;
+      }
+#endif
+      if (type & CCP_TYPE_MMLM)
+      {
+        if (yThres != cand.yThres)
+        {
+          return false;
+        }
+        if (isTheSameParams<GLM_NUM_PARAMS>(cand) && isTheSameParams2<GLM_NUM_PARAMS>(cand))
+        {
+          return true;
+        }
+      }
+      else
+      {
+        if (isTheSameParams<GLM_NUM_PARAMS>(cand))
+        {
+          return true;
+        }
+      }
+      return false;
+    }
+#if JVET_AC0147_CCCM_NO_SUBSAMPLING
+    else if (type & CCP_TYPE_NSCCCM)
+    {
+#if JVET_AB0174_CCCM_DIV_FREE
+      if (lumaOffset != cand.lumaOffset)
+      {
+        return false;
+      }
+#endif
+      if (type & CCP_TYPE_MMLM)
+      {
+        if (yThres != cand.yThres)
+        {
+          return false;
+        }
+        if (isTheSameParams<CCCM_NO_SUB_NUM_PARAMS>(cand) && isTheSameParams2<CCCM_NO_SUB_NUM_PARAMS>(cand))
+        {
+          return true;
+        }
+      }
+      else
+      {
+        if (isTheSameParams<CCCM_NO_SUB_NUM_PARAMS>(cand))
+        {
+          return true;
+        }
+      }
+      return false;
+    }
+#endif
+#if JVET_AD0202_CCCM_MDF
+    else if (type & CCP_TYPE_MDFCCCM)
+    {
+      if (cccmMultiFilterIdx != cand.cccmMultiFilterIdx)
+      {
+        return false;
+      }
+#if JVET_AB0174_CCCM_DIV_FREE
+      if (lumaOffset != cand.lumaOffset)
+      {
+        return false;
+      }
+#endif
+      if (type & CCP_TYPE_MMLM)
+      {
+        if (yThres != cand.yThres)
+        {
+          return false;
+        }
+        if (cccmMultiFilterIdx == 1)
+        {
+          if (isTheSameParams<CCCM_MULTI_PRED_FILTER_NUM_PARAMS>(cand) && isTheSameParams2<CCCM_MULTI_PRED_FILTER_NUM_PARAMS>(cand))
+          {
+            return true;
+          }
+        }
+        else
+        {
+          if (isTheSameParams<CCCM_MULTI_PRED_FILTER_NUM_PARAMS2>(cand) && isTheSameParams2<CCCM_MULTI_PRED_FILTER_NUM_PARAMS2>(cand))
+          {
+            return true;
+          }
+        }
+      }
+      else
+      {
+        if (cccmMultiFilterIdx == 1)
+        {
+          if (isTheSameParams<CCCM_MULTI_PRED_FILTER_NUM_PARAMS>(cand))
+          {
+            return true;
+          }
+        }
+        else
+        {
+          if (isTheSameParams<CCCM_MULTI_PRED_FILTER_NUM_PARAMS2>(cand))
+          {
+            return true;
+          }
+        }
+      }
+      return false;
+    }
+#endif
+    else
+    {
+      THROW("Wrong Type");
+      // return pos == cand.pos;
+    }
+  }
+};
+
+struct LutCCP
+{
+  static_vector<CCPModelCandidate, MAX_NUM_HCCP_CANDS> lutCCP;
+  // Postions for future extensions
+};
+#endif
 
 #endif
diff --git a/source/Lib/CommonLib/CommonDef.h b/source/Lib/CommonLib/CommonDef.h
index 586311c2c..c1b1f20bc 100644
--- a/source/Lib/CommonLib/CommonDef.h
+++ b/source/Lib/CommonLib/CommonDef.h
@@ -1403,6 +1403,15 @@ static const int EXT_PICTURE_SIZE =                             16;
 static const int IBC_BVD_PREDICTION_MAX_BIN_NUM =                4;
 #endif
 
+#if JVET_AD0188_CCP_MERGE
+static const int MAX_CCP_CAND_LIST_SIZE = 12;
+static const int MAX_NUM_HCCP_CANDS     = 6;
+#if JVET_AC0147_CCCM_NO_SUBSAMPLING
+static const int NUM_CCP_PARAMS = CCCM_NO_SUB_NUM_PARAMS;
+#else
+static const int NUM_CCP_PARAMS = CCCM_NUM_PARAMS;
+#endif
+#endif
 
 // ====================================================================================================================
 // Macro functions
diff --git a/source/Lib/CommonLib/Contexts.cpp b/source/Lib/CommonLib/Contexts.cpp
index f7123c412..2eafb7028 100644
--- a/source/Lib/CommonLib/Contexts.cpp
+++ b/source/Lib/CommonLib/Contexts.cpp
@@ -3415,6 +3415,23 @@ const CtxSet ContextSetCfg::TmrlDerive = ContextSetCfg::addCtxSet
   });
 #endif
 
+#if JVET_AD0188_CCP_MERGE
+const CtxSet ContextSetCfg::nonLocalCCP = ContextSetCfg::addCtxSet
+({
+  { CNU, },
+  { CNU, },
+  { CNU, },
+  { DWS, },
+  { DWS, },
+  { DWS, },
+  { DWE, },
+  { DWE, },
+  { DWE, },
+  { DWO, },
+  { DWO, },
+});
+#endif
+
 #elif SLICE_TYPE_WIN_SIZE
 const CtxSet ContextSetCfg::SplitFlag = ContextSetCfg::addCtxSet
 ({
diff --git a/source/Lib/CommonLib/Contexts.h b/source/Lib/CommonLib/Contexts.h
index 1baa3abb1..c31367b23 100644
--- a/source/Lib/CommonLib/Contexts.h
+++ b/source/Lib/CommonLib/Contexts.h
@@ -367,6 +367,9 @@ public:
   static const CtxSet   CclmModeFlag;
   static const CtxSet   CclmModeIdx;
   static const CtxSet   IntraChromaPredMode;
+#if JVET_AD0188_CCP_MERGE
+  static const CtxSet   nonLocalCCP;
+#endif
 #if JVET_Z0050_DIMD_CHROMA_FUSION
 #if ENABLE_DIMD
   static const CtxSet   DimdChromaMode;
diff --git a/source/Lib/CommonLib/IntraPrediction.cpp b/source/Lib/CommonLib/IntraPrediction.cpp
index 168c8f76f..6fb7eb05b 100644
--- a/source/Lib/CommonLib/IntraPrediction.cpp
+++ b/source/Lib/CommonLib/IntraPrediction.cpp
@@ -2312,6 +2312,16 @@ void IntraPrediction::predIntraChromaLM(const ComponentID compID, PelBuf &piPred
     CccmModel<GLM_NUM_PARAMS> glmModel(pu.cu->slice->getSPS()->getBitDepth(CHANNEL_TYPE_LUMA));
     xGlmCalcModel(pu, compID, chromaArea, glmModel);
     xGlmApplyModel(pu, compID, chromaArea, glmModel, piPred);
+
+ #if JVET_AD0188_CCP_MERGE
+    const_cast<PredictionUnit &>(pu).curCand.type   = CCP_TYPE_GLM4567;
+    const_cast<PredictionUnit &>(pu).curCand.glmIdc = pu.glmIdc.getIdc(compID, 0);
+    PU::glmModelToCcpParams(compID, const_cast<PredictionUnit&>(pu).curCand, glmModel 
+#if JVET_AB0174_CCCM_DIV_FREE
+                            , m_glmLumaOffset
+#endif
+                            );
+#endif
     return;
   }
 #endif
@@ -2376,6 +2386,17 @@ void IntraPrediction::predIntraChromaLM(const ComponentID compID, PelBuf &piPred
 #endif
 #endif
 
+#if JVET_AD0188_CCP_MERGE
+  int glmIdc = pu.glmIdc.getIdc(compID, 0);
+  const_cast<PredictionUnit&>(pu).curCand.type = (glmIdc > 0) ? CCP_TYPE_GLM0123 : CCP_TYPE_CCLM;
+  if (PU::isMultiModeLM(pu.intraDir[1]))
+  {
+    const_cast<PredictionUnit&>(pu).curCand.type |= CCP_TYPE_MMLM;
+  }
+  const_cast<PredictionUnit&>(pu).curCand.glmIdc = glmIdc;
+  PU::cclmModelToCcpParams(compID, const_cast<PredictionUnit&>(pu).curCand, cclmModel);
+#endif
+
   ////// final prediction
   piPred.copyFrom(temp);
 #if MMLM
@@ -3361,6 +3382,16 @@ void IntraPrediction::geneChromaFusionPred(const ComponentID compId, PelBuf &piP
   }
 #endif
   predIntraChromaLM(compId, predLmBuffer, pu2, area, pu2.intraDir[1]);
+
+#if JVET_AD0188_CCP_MERGE
+  const_cast<PredictionUnit&>(pu).curCand = pu2.curCand;
+  const_cast<PredictionUnit &>(pu).curCand.type = CCP_TYPE_CCLM;
+  if (PU::isMultiModeLM(pu2.intraDir[1]))
+  {
+    const_cast<PredictionUnit&>(pu).curCand.type |= CCP_TYPE_MMLM;
+  }
+#endif
+
 #if JVET_AC0071_DBV && JVET_AA0070_RRIBC
   if (pu.intraDir[1] == DBV_CHROMA_IDX && pu.cu->rribcFlipType != 0)
   {
@@ -12533,191 +12564,3011 @@ int IntraPrediction::calcTemplateDiff( Pel* ref, unsigned int uiStride, Pel** ta
 #endif
 #endif
 
-#if JVET_AB0174_CCCM_DIV_FREE
-#define DIV_PREC_BITS       14
-#define DIV_PREC_BITS_POW2  8
-#define DIV_SLOT_BITS       3
-#define DIV_INTR_BITS      (DIV_PREC_BITS - DIV_SLOT_BITS)
-#define DIV_INTR_ROUND     (1 << DIV_INTR_BITS >> 1)
 
-int64_t xDivide(int64_t num, int64_t denom) // Note: assumes positive denominator
+#if JVET_AD0188_CCP_MERGE
+void IntraPrediction::xGlmApplyModelOffset(const PredictionUnit &pu, const ComponentID compId,
+                                           const CompArea &chromaArea, CccmModel<GLM_NUM_PARAMS> &glmModel, int glmIdc,
+                                           PelBuf &piPred, int lumaOffset, int chromaOffset) const
 {
-  static const int pow2W[8] = {   214,   153,   113,    86,    67,    53,    43,    35  }; // DIV_PREC_BITS_POW2
-  static const int pow2O[8] = {  4822,  5952,  6624,  6792,  6408,  5424,  3792,  1466  }; // DIV_PREC_BITS
-  static const int pow2B[8] = { 12784, 12054, 11670, 11583, 11764, 12195, 12870, 13782  }; // DIV_PREC_BITS
+  const ClpRng &clpRng(pu.cu->cs->slice->clpRng(compId));
+  static Pel    samples[GLM_NUM_PARAMS];
 
-  int shift     = floorLog2Uint64(denom);
-  int round     = 1 << shift >> 1;
-  int normDiff  = (((denom << DIV_PREC_BITS) + round) >> shift) & ((1 << DIV_PREC_BITS) - 1);
-  int diffFull  = normDiff >> DIV_INTR_BITS;
-  int normDiff2 = normDiff - pow2O[diffFull];
+  CPelBuf refLumaBlk = xGlmGetGradPuBuf(pu, chromaArea, 0);
+  CPelBuf refGradBlk = xGlmGetGradPuBuf(pu, chromaArea, glmIdc);
 
-  int scale     = ((pow2W[diffFull] * ((normDiff2 * normDiff2) >> DIV_PREC_BITS)) >> DIV_PREC_BITS_POW2) - (normDiff2 >> 1) + pow2B[diffFull];
+  for (int y = 0; y < refLumaBlk.height; y++)
+  {
+    for (int x = 0; x < refLumaBlk.width; x++)
+    {
+      samples[0] = refGradBlk.at(x, y);                // luma gradient
+      samples[1] = refLumaBlk.at(x, y) + lumaOffset;   // luma value
+      samples[2] = glmModel.bias();
 
-  return ( (num << (CCCM_DECIM_BITS - DIV_PREC_BITS)) * scale + round) >> shift;
+      piPred.at(x, y) = ClipPel<Pel>(glmModel.convolve(samples) + chromaOffset, clpRng);
+    }
+  }
 }
-
-#if JVET_AC0053_GAUSSIAN_SOLVER
-void xGetDivScaleRoundShift(int64_t denom, int &scale, int &round, int &shift) // Note: assumes positive denominator
+void IntraPrediction::xCccmApplyModelOffset(const PredictionUnit &pu, const ComponentID compId,
+                                            const CccmModel<CCCM_NUM_PARAMS> &cccmModel, int modelId, int modelThr,
+                                            PelBuf &piPred, int lumaOffset, int chromaOffset[], int type,int refSizeX, int refSizeY) const
 {
-  static const int pow2W[8] = {   214,   153,   113,    86,    67,    53,    43,    35  }; // DIV_PREC_BITS_POW2
-  static const int pow2O[8] = {  4822,  5952,  6624,  6792,  6408,  5424,  3792,  1466  }; // DIV_PREC_BITS
-  static const int pow2B[8] = { 12784, 12054, 11670, 11583, 11764, 12195, 12870, 13782  }; // DIV_PREC_BITS
+  const ClpRng &clpRng(pu.cu->cs->slice->clpRng(compId));
+  static Pel    samples[CCCM_NUM_PARAMS];
 
-  shift         = floorLog2Uint64(denom);
-  round         = 1 << shift >> 1;
-  int normDiff  = (((denom << DIV_PREC_BITS) + round) >> shift) & ((1 << DIV_PREC_BITS) - 1);
-  int diffFull  = normDiff >> DIV_INTR_BITS;
-  int normDiff2 = normDiff - pow2O[diffFull];
+  CPelBuf refLumaBlk = xCccmGetLumaPuBuf(pu);
 
-  scale         = ((pow2W[diffFull] * ((normDiff2 * normDiff2) >> DIV_PREC_BITS)) >> DIV_PREC_BITS_POW2) - (normDiff2 >> 1) + pow2B[diffFull];
-  scale       <<= CCCM_DECIM_BITS - DIV_PREC_BITS;
-}
-#endif
+  int offset = modelId == 2 ? chromaOffset[1] : chromaOffset[0];
 
-#undef DIV_PREC_BITS
-#undef DIV_PREC_BITS_POW2
-#undef DIV_SLOT_BITS
-#undef DIV_INTR_BITS
-#undef DIV_INTR_ROUND
+  if (type & CCP_TYPE_CCCM)
+  {
+    for (int y = 0; y < refLumaBlk.height; y++)
+    {
+      for (int x = 0; x < refLumaBlk.width; x++)
+      {
+        if (modelId == 1 && (refLumaBlk.at(x, y) + lumaOffset) > modelThr)   // Model 1: Include only samples below or equal to the threshold
+        {
+          continue;
+        }
+        if (modelId == 2 && (refLumaBlk.at(x, y) + lumaOffset) <= modelThr)   // Model 2: Include only samples above the threshold
+        {
+          continue;
+        }
 
-int xCccmDivideLowPrec(int64_t num, int64_t denom)
-{
-  if ( num < 0 )
+        // 7-tap cross
+        samples[0] = refLumaBlk.at(x, y) + lumaOffset;       // C
+        samples[1] = refLumaBlk.at(x, y - 1) + lumaOffset;   // N
+        samples[2] = refLumaBlk.at(x, y + 1) + lumaOffset;   // S
+        samples[3] = refLumaBlk.at(x - 1, y) + lumaOffset;   // W
+        samples[4] = refLumaBlk.at(x + 1, y) + lumaOffset;   // E
+        samples[5] = const_cast<CccmModel<CCCM_NUM_PARAMS> &>(cccmModel).nonlinear(refLumaBlk.at(x, y) + lumaOffset);
+        samples[6] = const_cast<CccmModel<CCCM_NUM_PARAMS> &>(cccmModel).bias();
+
+        piPred.at(x, y) = ClipPel<Pel>(const_cast<CccmModel<CCCM_NUM_PARAMS> &>(cccmModel).convolve(samples) + offset, clpRng);
+      }
+    }
+  }
+#if JVET_AC0054_GLCCCM
+  else if (type & CCP_TYPE_GLCCCM)
   {
-    return -int(xDivide(-num, denom) >> CCCM_DECIM_BITS);
+    for (int y = 0; y < refLumaBlk.height; y++)
+    {
+      for (int x = 0; x < refLumaBlk.width; x++)
+      {
+        if (modelId == 1 && (refLumaBlk.at(x, y) + lumaOffset) > modelThr)   // Model 1: Include only samples below or equal to the threshold
+        {
+          continue;
+        }
+        if (modelId == 2 && (refLumaBlk.at(x, y) + lumaOffset) <= modelThr)   // Model 2: Include only samples above the threshold
+        {
+          continue;
+        }
+
+        samples[0] = refLumaBlk.at(x, y) + lumaOffset;   // C
+        samples[1] = (2 * refLumaBlk.at(x, y - 1) + refLumaBlk.at(x - 1, y - 1) + refLumaBlk.at(x + 1, y - 1))
+                   - (2 * refLumaBlk.at(x, y + 1) + refLumaBlk.at(x - 1, y + 1) + refLumaBlk.at(x + 1, y + 1));   // Vertical gradient
+        samples[2] = (2 * refLumaBlk.at(x - 1, y) + refLumaBlk.at(x - 1, y - 1) + refLumaBlk.at(x - 1, y + 1))
+                   - (2 * refLumaBlk.at(x + 1, y) + refLumaBlk.at(x + 1, y - 1) + refLumaBlk.at(x + 1, y + 1));   // Horizontal gradient
+        samples[3] = ((y + refSizeY + CCCM_LOC_OFFSET) << CCCM_LOC_SHIFT);   // Y coordinate
+        samples[4] = ((x + refSizeX + CCCM_LOC_OFFSET) << CCCM_LOC_SHIFT);   // X coordinate
+        samples[5] = const_cast<CccmModel<CCCM_NUM_PARAMS> &>(cccmModel).nonlinear(refLumaBlk.at(x, y) + lumaOffset);
+        samples[6] = const_cast<CccmModel<CCCM_NUM_PARAMS> &>(cccmModel).bias();
+
+        piPred.at(x, y) = ClipPel<Pel>(const_cast<CccmModel<CCCM_NUM_PARAMS> &>(cccmModel).convolve(samples) + offset, clpRng);
+      }
+    }
   }
+#endif
   else
   {
-    return int(xDivide(num, denom) >> CCCM_DECIM_BITS);
+    THROW("Invalid type");
   }
 }
 
-int64_t xCccmDivide(int64_t num, int64_t denom) // Note: assumes positive denominator
+#if JVET_AD0202_CCCM_MDF
+void IntraPrediction::xMFCccmApplyModelOffset1(const PredictionUnit &pu, const ComponentID compId,
+                                               const CccmModel<CCCM_MULTI_PRED_FILTER_NUM_PARAMS> &cccmModel, int modelId,
+                                               int modelThr, PelBuf &piPred, int lumaOffset, int chromaOffset[2]) const
 {
-  return xDivide(num, denom);
-}
+  const  ClpRng& clpRng(pu.cu->cs->slice->clpRng(compId));
+  static Pel     samples[CCCM_MULTI_PRED_FILTER_NUM_PARAMS];
+
+#if JVET_AC0147_CCCM_NO_SUBSAMPLING
+  CHECK(pu.cccmNoSubFlag, "cccmNoSubFlag shall be disabled");
+#endif
+#if JVET_AC0054_GLCCCM
+  int refSizeX = m_cccmBlkArea.x - m_cccmRefArea.x; // Reference lines available left and above
+  int refSizeY = m_cccmBlkArea.y - m_cccmRefArea.y;
 #endif
+  CPelBuf refLumaBlk1, refLumaBlk2, refLumaBlk3;
+  CPelBuf refLumaBlk = xCccmGetLumaPuBuf(pu, 0, 3, &refLumaBlk1, &refLumaBlk3, &refLumaBlk2);
 
-#if JVET_AA0057_CCCM || JVET_AC0119_LM_CHROMA_FUSION
-#if JVET_AB0174_CCCM_DIV_FREE
-void IntraPrediction::xCccmSetLumaRefValue(const PredictionUnit& pu)
-{
-  int lumaPosX = m_cccmBlkArea.x << getComponentScaleX(COMPONENT_Cb, pu.cu->chromaFormat);
-  int lumaPosY = m_cccmBlkArea.y << getComponentScaleY(COMPONENT_Cb, pu.cu->chromaFormat);
+  int offset = modelId == 2 ? chromaOffset[1] : chromaOffset[0];
 
-  if (lumaPosX || lumaPosY)
+  for (int y = 0; y < refLumaBlk.height; y++)
   {
-    lumaPosX = lumaPosX ? lumaPosX - 1 : 0;
-    lumaPosY = lumaPosY ? lumaPosY - 1 : 0;
+    for (int x = 0; x < refLumaBlk.width; x++)
+    {
+      if (modelId == 1 && (refLumaBlk.at(x, y) + lumaOffset) > modelThr) // Model 1: Include only samples below or equal to the threshold
+      {
+        continue;
+      }
+      if (modelId == 2 && (refLumaBlk.at(x, y) + lumaOffset) <= modelThr) // Model 2: Include only samples above the threshold
+      {
+        continue;
+      }
 
-    m_cccmLumaOffset = pu.cs->picture->getRecoBuf(COMPONENT_Y).at(lumaPosX, lumaPosY);
-  }
-  else
-  {
-    m_cccmLumaOffset = 1 << (pu.cu->slice->getSPS()->getBitDepth(CHANNEL_TYPE_LUMA) - 1);
+      // 7-tap cross
+      samples[0] = refLumaBlk.at(x, y) + lumaOffset; // C
+      samples[1] = refLumaBlk1.at(x, y) + lumaOffset; // W
+      samples[2] = refLumaBlk2.at(x, y) + lumaOffset; // E
+      samples[3] = refLumaBlk3.at(x, y) + lumaOffset;
+      samples[4] = const_cast<CccmModel<CCCM_MULTI_PRED_FILTER_NUM_PARAMS>&>(cccmModel).nonlinear(refLumaBlk.at(x, y) + lumaOffset);
+      samples[5] = const_cast<CccmModel<CCCM_MULTI_PRED_FILTER_NUM_PARAMS>&>(cccmModel).nonlinear(refLumaBlk1.at(x, y) + lumaOffset);
+      samples[6] = const_cast<CccmModel<CCCM_MULTI_PRED_FILTER_NUM_PARAMS>&>(cccmModel).nonlinear(refLumaBlk2.at(x, y) + lumaOffset);
+      samples[7] = ((y + refSizeY + CCCM_LOC_OFFSET) << CCCM_LOC_SHIFT); // Y coordinate
+      samples[8] = ((x + refSizeX + CCCM_LOC_OFFSET) << CCCM_LOC_SHIFT); // X coordinate
+      samples[9] = const_cast<CccmModel<CCCM_MULTI_PRED_FILTER_NUM_PARAMS>&>(cccmModel).bias();
+
+      piPred.at(x, y) = ClipPel<Pel>(const_cast<CccmModel<CCCM_MULTI_PRED_FILTER_NUM_PARAMS>&>(cccmModel).convolve(samples) + offset, clpRng);
+    }
   }
 }
-#endif
 
-// Calculate a single downsampled luma reference value (copied from IntraPrediction::xGetLumaRecPixels)
-Pel IntraPrediction::xCccmGetLumaVal(const PredictionUnit& pu, const CPelBuf pi, const int x, const int y
-#if JVET_AD0202_CCCM_MDF
-  , int downsFilterIdx
-#endif
-) const
+void IntraPrediction::xMFCccmApplyModelOffset23(const PredictionUnit &pu, const ComponentID compId,
+                                                const CccmModel<CCCM_MULTI_PRED_FILTER_NUM_PARAMS2> &cccmModel, int modelId,
+                                                int modelThr, PelBuf &piPred, int lumaOffset, int chromaOffset[2]) const
 {
-  const Pel* piSrc = pi.buf;
-  const int iRecStride = pi.stride;
-  Pel ypval = 0;
+  const  ClpRng& clpRng(pu.cu->cs->slice->clpRng(compId));
+  static Pel     samples[CCCM_MULTI_PRED_FILTER_NUM_PARAMS2];
+
 #if JVET_AC0147_CCCM_NO_SUBSAMPLING
-  if (pu.cccmNoSubFlag || pu.chromaFormat == CHROMA_444)
-#else
-  if (pu.chromaFormat == CHROMA_444)
+  CHECK(pu.cccmNoSubFlag, "cccmNoSubFlag shall be disabled");
 #endif
-  {
-    ypval = piSrc[x + iRecStride * y];
-  }
-  else if (pu.chromaFormat == CHROMA_422)
-  {
-    int s = 2;
-    int offLeft = x > 0 ? -1 : 0;
-    s += piSrc[2 * x + iRecStride * y] * 2;
-    s += piSrc[2 * x + offLeft + iRecStride * y];
-    s += piSrc[2 * x + 1 + iRecStride * y];
-    ypval = s >> 2;
-  }
-  else if (pu.cs->sps->getCclmCollocatedChromaFlag())
-  {
-    int s = 4;
-    int offLeft = x > 0 ? -1 : 0;
-    int offAbove = y > 0 ? -1 : 0;
-    s += piSrc[2 * x + iRecStride * 2 * y] * 4;
-    s += piSrc[2 * x + offLeft + iRecStride * 2 * y];
-    s += piSrc[2 * x + 1 + iRecStride * 2 * y];
-    s += piSrc[2 * x + iRecStride * (2 * y + 1)];
-    s += piSrc[2 * x + iRecStride * (2 * y + offAbove)];
-    ypval = s >> 3;
-  }
-  else
-  {
-#if JVET_AD0202_CCCM_MDF
-    const int lumaPosPicX1 = 2 * x;
-    const int lumaPosPicY1 = 2 * y;
-    int lumaPosPicX0 = lumaPosPicX1 - 1; lumaPosPicX0 = lumaPosPicX0 < 0 ? 0 : lumaPosPicX0;
-    const int lumaPosPicX2 = lumaPosPicX1 + 1;
-    const int lumaPosPicY2 = lumaPosPicY1 + 1;
-    const int shift0 = iRecStride * lumaPosPicY1;
-    const int shift1 = iRecStride * lumaPosPicY2;
-
-    if (downsFilterIdx == 0)
-    {
-      int s = 4;
+#if JVET_AC0054_GLCCCM
+  int refSizeX = m_cccmBlkArea.x - m_cccmRefArea.x; // Reference lines available left and above
+  int refSizeY = m_cccmBlkArea.y - m_cccmRefArea.y;
+#endif
+  CPelBuf refLumaBlk1, refLumaBlk3;
+  CPelBuf refLumaBlk = xCccmGetLumaPuBuf(pu, 0, 2, &refLumaBlk1, &refLumaBlk3);
+  int offset = modelId == 2 ? chromaOffset[1] : chromaOffset[0];
 
-      s += piSrc[lumaPosPicX1 + shift0] * 2;
-      s += piSrc[lumaPosPicX0 + shift0];
-      s += piSrc[lumaPosPicX2 + shift0];
-      s += piSrc[lumaPosPicX1 + shift1] * 2;
-      s += piSrc[lumaPosPicX0 + shift1];
-      s += piSrc[lumaPosPicX2 + shift1];
-      ypval = s >> 3;
-    }
-    else if (downsFilterIdx == 1)
+  for (int y = 0; y < refLumaBlk.height; y++)
+  {
+    for (int x = 0; x < refLumaBlk.width; x++)
     {
-      int s = 0;
+      if (modelId == 1 && (refLumaBlk.at(x, y) + lumaOffset) > modelThr) // Model 1: Include only samples below or equal to the threshold
+      {
+        continue;
+      }
+      if (modelId == 2 && (refLumaBlk.at(x, y) + lumaOffset) <= modelThr) // Model 2: Include only samples above the threshold
+      {
+        continue;
+      }
 
-      s += piSrc[lumaPosPicX0 + shift0];
-      s -= piSrc[lumaPosPicX2 + shift0];
-      s += piSrc[lumaPosPicX0 + shift1];
-      s -= piSrc[lumaPosPicX2 + shift1];
+      // 7-tap cross
+      if (pu.cccmMultiFilterIdx == 2)
+      {
+        samples[0] = refLumaBlk.at(x, y) + lumaOffset; // C
+        samples[1] = refLumaBlk.at(x - 1, y) + lumaOffset; // W
+        samples[2] = refLumaBlk.at(x + 1, y) + lumaOffset; // E
+        samples[3] = refLumaBlk1.at(x, y) + lumaOffset; // C
+        samples[4] = refLumaBlk1.at(x - 1, y) + lumaOffset; // W
+        samples[5] = refLumaBlk1.at(x + 1, y) + lumaOffset; // E
+        samples[6] = const_cast<CccmModel<CCCM_MULTI_PRED_FILTER_NUM_PARAMS2>&>(cccmModel).nonlinear(refLumaBlk.at(x, y)) + lumaOffset;
+        samples[7] = const_cast<CccmModel<CCCM_MULTI_PRED_FILTER_NUM_PARAMS2>&>(cccmModel).nonlinear(refLumaBlk.at(x - 1, y)) + lumaOffset;
+        samples[8] = const_cast<CccmModel<CCCM_MULTI_PRED_FILTER_NUM_PARAMS2>&>(cccmModel).nonlinear(refLumaBlk.at(x + 1, y)) + lumaOffset;
+        samples[9] = ((x + refSizeX + CCCM_LOC_OFFSET) << CCCM_LOC_SHIFT); // X coordinate
+        samples[10] = const_cast<CccmModel<CCCM_MULTI_PRED_FILTER_NUM_PARAMS2>&>(cccmModel).bias();
+      }
+      else if (pu.cccmMultiFilterIdx == 3)
+      {
+        samples[0] = refLumaBlk.at(x, y) + lumaOffset; // C
+        samples[1] = refLumaBlk.at(x + 1, y - 1) + lumaOffset; // EN
+        samples[2] = refLumaBlk.at(x - 1, y + 1) + lumaOffset; // WS
+        samples[3] = refLumaBlk3.at(x, y) + lumaOffset; // C
+        samples[4] = refLumaBlk3.at(x + 1, y - 1) + lumaOffset; // EN
+        samples[5] = refLumaBlk3.at(x - 1, y + 1) + lumaOffset; // WS
+        samples[6] = const_cast<CccmModel<CCCM_MULTI_PRED_FILTER_NUM_PARAMS2>&>(cccmModel).nonlinear(refLumaBlk.at(x, y)) + lumaOffset;
+        samples[7] = const_cast<CccmModel<CCCM_MULTI_PRED_FILTER_NUM_PARAMS2>&>(cccmModel).nonlinear(refLumaBlk.at(x + 1, y - 1)) + lumaOffset;
+        samples[8] = const_cast<CccmModel<CCCM_MULTI_PRED_FILTER_NUM_PARAMS2>&>(cccmModel).nonlinear(refLumaBlk.at(x - 1, y + 1)) + lumaOffset;
+        samples[9] = ((y + refSizeY + CCCM_LOC_OFFSET) << CCCM_LOC_SHIFT); // Y coordinate
+        samples[10] = const_cast<CccmModel<CCCM_MULTI_PRED_FILTER_NUM_PARAMS2>&>(cccmModel).bias();
+      }
+      else
+      {
+        THROW("Invalid cccmMultiFilterIdx");
+      }
 
-      ypval = s < 0 ? 0 : s;
+      piPred.at(x, y) = ClipPel<Pel>(const_cast<CccmModel<CCCM_MULTI_PRED_FILTER_NUM_PARAMS2>&>(cccmModel).convolve(samples) + offset, clpRng);
     }
-    else if (downsFilterIdx == 2)
-    {
-      int s = 0;
+  }
+}
 
-      s += piSrc[lumaPosPicX0 + shift0];
-      s += piSrc[lumaPosPicX1 + shift0] * 2;
-      s += piSrc[lumaPosPicX2 + shift0];
-      s -= piSrc[lumaPosPicX0 + shift1];
-      s -= piSrc[lumaPosPicX1 + shift1] * 2;
-      s -= piSrc[lumaPosPicX2 + shift1];
+void IntraPrediction::xGetUpdatedOffsetMFCCCM1(const PredictionUnit& pu, const ComponentID compID, int modelNum,
+                                               const CompArea& chromaArea, CccmModel<CCCM_MULTI_PRED_FILTER_NUM_PARAMS> cccmModel[2],
+                                               int modelThr, int lumaOffset, int chromaOffset[], int type, int refSizeX, int refSizeY)
+{
+  CHECK(compID != chromaArea.compID, "Invalid component ID");
 
-      ypval = s < 0 ? 0 : s;
-    }
-    else
-    {
-      int s = 0;
+  static Pel samples[CCCM_MULTI_PRED_FILTER_NUM_PARAMS];
 
-      s -= piSrc[lumaPosPicX0 + shift0];
-      s += piSrc[lumaPosPicX1 + shift0];
-      s += piSrc[lumaPosPicX2 + shift0] * 2;
-      s -= piSrc[lumaPosPicX0 + shift1] * 2;
-      s -= piSrc[lumaPosPicX1 + shift1];
-      s += piSrc[lumaPosPicX2 + shift1];
+  CPelBuf refLumaBlk1, refLumaBlk2, refLumaBlk3;
+  CPelBuf refLumaBlk = xCccmGetLumaPuBuf(pu, 0, 3, &refLumaBlk1, &refLumaBlk3, &refLumaBlk2);
 
-      ypval = s < 0 ? 0 : s;
+  const SizeType cWidth = chromaArea.width;
+  const SizeType cHeight = chromaArea.height;
+
+  CodingStructure& cs = *(pu.cs);
+  const CodingUnit& cu = *(pu.cu);
+
+  const bool aboveAvailable = cu.cs->getCU(cu.blocks[compID].pos().offset(0, -1), toChannelType(compID)) ? true : false;
+  const bool leftAvailable  = cu.cs->getCU(cu.blocks[compID].pos().offset(-1, 0), toChannelType(compID)) ? true : false;
+
+  const Pel* curChroma0;
+
+  PelBuf chromaReco = cs.picture->getRecoBuf(chromaArea);
+  Pel* curChromaBuf = chromaReco.buf;
+  const int curStride = chromaReco.stride;
+
+  int totalOffset[2] = { 0, 0 };
+  int count[2] = { 0, 0 };
+
+  Pel predChroma;
+
+  if (aboveAvailable)
+  {
+    curChroma0 = curChromaBuf - curStride;
+#if MMLM
+    if (type & CCP_TYPE_MMLM)
+    {
+      for (int pos = 0; pos < cWidth; pos++)
+      {
+        samples[0] = refLumaBlk.at(pos, -1) + lumaOffset; // C
+        samples[1] = refLumaBlk1.at(pos, -1) + lumaOffset; // W
+        samples[2] = refLumaBlk2.at(pos, -1) + lumaOffset; // E
+        samples[3] = refLumaBlk3.at(pos, -1) + lumaOffset;
+        samples[4] = cccmModel[0].nonlinear(refLumaBlk.at(pos, -1) + lumaOffset);
+        samples[5] = cccmModel[0].nonlinear(refLumaBlk1.at(pos, -1) + lumaOffset);
+        samples[6] = cccmModel[0].nonlinear(refLumaBlk2.at(pos, -1) + lumaOffset);
+        samples[7] = (( -1 + refSizeY + CCCM_LOC_OFFSET) << CCCM_LOC_SHIFT); // Y coordinate
+        samples[8] = ((pos + refSizeX + CCCM_LOC_OFFSET) << CCCM_LOC_SHIFT); // X coordinate
+        samples[9] = cccmModel[0].bias();
+        if ((refLumaBlk.at(pos, -1) + lumaOffset) <= modelThr)
+        {
+          predChroma = cccmModel[0].convolve(samples);
+          totalOffset[0] += curChroma0[pos] - predChroma;
+          count[0]++;
+        }
+        else
+        {
+          predChroma = cccmModel[1].convolve(samples);
+          totalOffset[1] += curChroma0[pos] - predChroma;
+          count[1]++;
+        }
+      }
+    }
+    else
+#endif
+    {
+      for (int pos = 0; pos < cWidth; pos++)
+      {
+        samples[0] = refLumaBlk.at(pos, -1) + lumaOffset; // C
+        samples[1] = refLumaBlk1.at(pos, -1) + lumaOffset; // W
+        samples[2] = refLumaBlk2.at(pos, -1) + lumaOffset; // E
+        samples[3] = refLumaBlk3.at(pos, -1) + lumaOffset;
+        samples[4] = cccmModel[0].nonlinear(refLumaBlk.at(pos, -1) + lumaOffset);
+        samples[5] = cccmModel[0].nonlinear(refLumaBlk1.at(pos, -1) + lumaOffset);
+        samples[6] = cccmModel[0].nonlinear(refLumaBlk2.at(pos, -1) + lumaOffset);
+        samples[7] = (( -1 + refSizeY + CCCM_LOC_OFFSET) << CCCM_LOC_SHIFT); // Y coordinate
+        samples[8] = ((pos + refSizeX + CCCM_LOC_OFFSET) << CCCM_LOC_SHIFT); // X coordinate
+        samples[9] = cccmModel[0].bias();
+        predChroma = cccmModel[0].convolve(samples);
+        totalOffset[0] += curChroma0[pos] - predChroma;
+        count[0]++;
+      }
+    }
+  }
+
+  if (leftAvailable)
+  {
+    curChroma0 = curChromaBuf - 1;
+#if MMLM
+    if (type & CCP_TYPE_MMLM)
+    {
+      for (int pos = 0; pos < cHeight; pos++)
+      {
+        samples[0] = refLumaBlk.at(-1, pos) + lumaOffset; // C
+        samples[1] = refLumaBlk1.at(-1, pos) + lumaOffset; // W
+        samples[2] = refLumaBlk2.at(-1, pos) + lumaOffset; // E
+        samples[3] = refLumaBlk3.at(-1, pos) + lumaOffset;
+        samples[4] = cccmModel[0].nonlinear(refLumaBlk.at(-1, pos) + lumaOffset);
+        samples[5] = cccmModel[0].nonlinear(refLumaBlk1.at(-1, pos) + lumaOffset);
+        samples[6] = cccmModel[0].nonlinear(refLumaBlk2.at(-1, pos) + lumaOffset);
+        samples[7] = ((pos + refSizeY + CCCM_LOC_OFFSET) << CCCM_LOC_SHIFT); // Y coordinate
+        samples[8] = (( -1 + refSizeX + CCCM_LOC_OFFSET) << CCCM_LOC_SHIFT); // X coordinate
+        samples[9] = cccmModel[0].bias();
+        if ((refLumaBlk.at(-1, pos) + lumaOffset) <= modelThr)
+        {
+          predChroma = cccmModel[0].convolve(samples);
+          totalOffset[0] += curChroma0[pos * curStride] - predChroma;
+          count[0]++;
+        }
+        else
+        {
+          predChroma = cccmModel[1].convolve(samples);
+          totalOffset[1] += curChroma0[pos * curStride] - predChroma;
+          count[1]++;
+        }
+      }
+    }
+    else
+#endif
+    {
+      for (int pos = 0; pos < cHeight; pos++)
+      {
+        samples[0] = refLumaBlk.at(-1, pos) + lumaOffset; // C
+        samples[1] = refLumaBlk1.at(-1, pos) + lumaOffset; // W
+        samples[2] = refLumaBlk2.at(-1, pos) + lumaOffset; // E
+        samples[3] = refLumaBlk3.at(-1, pos) + lumaOffset;
+        samples[4] = cccmModel[0].nonlinear(refLumaBlk.at(-1, pos) + lumaOffset);
+        samples[5] = cccmModel[0].nonlinear(refLumaBlk1.at(-1, pos) + lumaOffset);
+        samples[6] = cccmModel[0].nonlinear(refLumaBlk2.at(-1, pos) + lumaOffset);
+        samples[7] = ((pos + refSizeY + CCCM_LOC_OFFSET) << CCCM_LOC_SHIFT); // Y coordinate
+        samples[8] = (( -1 + refSizeX + CCCM_LOC_OFFSET) << CCCM_LOC_SHIFT); // X coordinate
+        samples[9] = cccmModel[0].bias();
+        predChroma = cccmModel[0].convolve(samples);
+        totalOffset[0] += curChroma0[pos * curStride] - predChroma;
+        count[0]++;
+      }
+    }
+  }
+
+  chromaOffset[0] = chromaOffset[1] = 0;
+
+  if (modelNum == 2)
+  {
+    if (count[0])
+    {
+      chromaOffset[0] = PU::getMeanValue(totalOffset[0], count[0]);   // totalOffset[0] / count[0];
+    }
+    if (count[1])
+    {
+      chromaOffset[1] = PU::getMeanValue(totalOffset[1], count[1]);   // totalOffset[1] / count[1];
+    }
+  }
+  else
+  {
+    if (count[0])
+    {
+      chromaOffset[0] = PU::getMeanValue(totalOffset[0], count[0]);   // totalOffset[0] / count[0];
+    }
+  }
+}
+
+void IntraPrediction::xGetUpdatedOffsetMFCCCM23(const PredictionUnit& pu, const ComponentID compID, int modelNum,
+                                                const CompArea& chromaArea, CccmModel<CCCM_MULTI_PRED_FILTER_NUM_PARAMS2> cccmModel[2],
+                                                int modelThr, int lumaOffset, int chromaOffset[], int type, int refSizeX, int refSizeY)
+{
+  CHECK(compID != chromaArea.compID, "Invalid component ID");
+
+  static Pel samples[CCCM_MULTI_PRED_FILTER_NUM_PARAMS2];
+
+  CPelBuf refLumaBlk1, refLumaBlk3;
+  CPelBuf refLumaBlk = xCccmGetLumaPuBuf(pu, 0, 2, &refLumaBlk1, &refLumaBlk3);
+
+  const SizeType cWidth = chromaArea.width;
+  const SizeType cHeight = chromaArea.height;
+
+  CodingStructure& cs = *(pu.cs);
+  const CodingUnit& cu = *(pu.cu);
+
+  const bool aboveAvailable = cu.cs->getCU(cu.blocks[compID].pos().offset(0, -1), toChannelType(compID)) ? true : false;
+  const bool leftAvailable  = cu.cs->getCU(cu.blocks[compID].pos().offset(-1, 0), toChannelType(compID)) ? true : false;
+
+  const Pel* curChroma0;
+
+  PelBuf chromaReco = cs.picture->getRecoBuf(chromaArea);
+  Pel* curChromaBuf = chromaReco.buf;
+  const int curStride = chromaReco.stride;
+
+  int totalOffset[2] = { 0, 0 };
+  int count[2] = { 0, 0 };
+
+  Pel predChroma;
+
+  if (aboveAvailable)
+  {
+    curChroma0 = curChromaBuf - curStride;
+#if MMLM
+    if (type & CCP_TYPE_MMLM)
+    {
+      if (pu.cccmMultiFilterIdx == 2)
+      {
+        for (int pos = 0; pos < cWidth; pos++)
+        {
+          samples[0] = refLumaBlk.at(pos, -1) + lumaOffset; // C
+          samples[1] = refLumaBlk.at(pos - 1, -1) + lumaOffset; // W
+          samples[2] = refLumaBlk.at(pos + 1, -1) + lumaOffset; // E
+          samples[3] = refLumaBlk1.at(pos, -1) + lumaOffset; // C
+          samples[4] = refLumaBlk1.at(pos - 1, -1) + lumaOffset; // W
+          samples[5] = refLumaBlk1.at(pos + 1, -1) + lumaOffset; // E
+          samples[6] = cccmModel[0].nonlinear(refLumaBlk.at(pos, -1) + lumaOffset);
+          samples[7] = cccmModel[0].nonlinear(refLumaBlk.at(pos - 1, -1) + lumaOffset);
+          samples[8] = cccmModel[0].nonlinear(refLumaBlk.at(pos + 1, -1) + lumaOffset);
+          samples[9] = ((pos + refSizeX + CCCM_LOC_OFFSET) << CCCM_LOC_SHIFT); // X coordinate
+          samples[10] = cccmModel[0].bias();
+          if ((refLumaBlk.at(pos, -1) + lumaOffset) <= modelThr)
+          {
+            predChroma = cccmModel[0].convolve(samples);
+            totalOffset[0] += curChroma0[pos] - predChroma;
+            count[0]++;
+          }
+          else
+          {
+            predChroma = cccmModel[1].convolve(samples);
+            totalOffset[1] += curChroma0[pos] - predChroma;
+            count[1]++;
+          }
+        }
+      }
+      else if (pu.cccmMultiFilterIdx == 3)
+      {
+        for (int pos = 0; pos < cWidth; pos++)
+        {
+          samples[0] = refLumaBlk.at(pos, -1) + lumaOffset; // C
+          samples[1] = refLumaBlk.at(pos + 1, -2) + lumaOffset; // EN
+          samples[2] = refLumaBlk.at(pos - 1, 0) + lumaOffset; // WS
+          samples[3] = refLumaBlk3.at(pos, -1) + lumaOffset; // C
+          samples[4] = refLumaBlk3.at(pos + 1, -2) + lumaOffset; // EN
+          samples[5] = refLumaBlk3.at(pos - 1, 0) + lumaOffset; // WS
+          samples[6] = cccmModel[0].nonlinear(refLumaBlk.at(pos, -1) + lumaOffset);
+          samples[7] = cccmModel[0].nonlinear(refLumaBlk.at(pos + 1, -2) + lumaOffset);
+          samples[8] = cccmModel[0].nonlinear(refLumaBlk.at(pos - 1, 0) + lumaOffset);
+          samples[9] = ((-1 + refSizeY + CCCM_LOC_OFFSET) << CCCM_LOC_SHIFT); // Y coordinate
+          samples[10] = cccmModel[0].bias();
+          if ((refLumaBlk.at(pos, -1) + lumaOffset) <= modelThr)
+          {
+            predChroma = cccmModel[0].convolve(samples);
+            totalOffset[0] += curChroma0[pos] - predChroma;
+            count[0]++;
+          }
+          else
+          {
+            predChroma = cccmModel[1].convolve(samples);
+            totalOffset[1] += curChroma0[pos] - predChroma;
+            count[1]++;
+          }
+        }
+      }
+      else
+      {
+        THROW("Invalid cccmMultiFilterIdx");
+      }
+    }
+    else
+#endif
+    {
+      if (pu.cccmMultiFilterIdx == 2)
+      {
+        for (int pos = 0; pos < cWidth; pos++)
+        {
+          samples[0] = refLumaBlk.at(pos, -1) + lumaOffset; // C
+          samples[1] = refLumaBlk.at(pos - 1, -1) + lumaOffset; // W
+          samples[2] = refLumaBlk.at(pos + 1, -1) + lumaOffset; // E
+          samples[3] = refLumaBlk1.at(pos, -1) + lumaOffset; // C
+          samples[4] = refLumaBlk1.at(pos - 1, -1) + lumaOffset; // W
+          samples[5] = refLumaBlk1.at(pos + 1, -1) + lumaOffset; // E
+          samples[6] = cccmModel[0].nonlinear(refLumaBlk.at(pos, -1) + lumaOffset);
+          samples[7] = cccmModel[0].nonlinear(refLumaBlk.at(pos - 1, -1) + lumaOffset);
+          samples[8] = cccmModel[0].nonlinear(refLumaBlk.at(pos + 1, -1) + lumaOffset);
+          samples[9] = ((pos + refSizeX + CCCM_LOC_OFFSET) << CCCM_LOC_SHIFT); // X coordinate
+          samples[10] = cccmModel[0].bias();
+          predChroma = cccmModel[0].convolve(samples);
+          totalOffset[0] += curChroma0[pos] - predChroma;
+          count[0]++;
+        }
+      }
+      else if (pu.cccmMultiFilterIdx == 3)
+      {
+        for (int pos = 0; pos < cWidth; pos++)
+        {
+          samples[0] = refLumaBlk.at(pos, -1) + lumaOffset; // C
+          samples[1] = refLumaBlk.at(pos + 1, -2) + lumaOffset; // EN
+          samples[2] = refLumaBlk.at(pos - 1, 0) + lumaOffset; // WS
+          samples[3] = refLumaBlk3.at(pos, -1) + lumaOffset; // C
+          samples[4] = refLumaBlk3.at(pos + 1, -2) + lumaOffset; // EN
+          samples[5] = refLumaBlk3.at(pos - 1, 0) + lumaOffset; // WS
+          samples[6] = cccmModel[0].nonlinear(refLumaBlk.at(pos, -1) + lumaOffset);
+          samples[7] = cccmModel[0].nonlinear(refLumaBlk.at(pos + 1, -2) + lumaOffset);
+          samples[8] = cccmModel[0].nonlinear(refLumaBlk.at(pos - 1, 0) + lumaOffset);
+          samples[9] = ((-1 + refSizeY + CCCM_LOC_OFFSET) << CCCM_LOC_SHIFT); // Y coordinate
+          samples[10] = cccmModel[0].bias();
+          predChroma = cccmModel[0].convolve(samples);
+          totalOffset[0] += curChroma0[pos] - predChroma;
+          count[0]++;
+        }
+      }
+      else
+      {
+        THROW("Invalid cccmMultiFilterIdx");
+      }
+    }
+  }
+
+  if (leftAvailable)
+  {
+    curChroma0 = curChromaBuf - 1;
+#if MMLM
+    if (type & CCP_TYPE_MMLM)
+    {
+      if (pu.cccmMultiFilterIdx == 2)
+      {
+        for (int pos = 0; pos < cHeight; pos++)
+        {
+          samples[0] = refLumaBlk.at(-1, pos) + lumaOffset; // C
+          samples[1] = refLumaBlk.at(-2, pos) + lumaOffset; // W
+          samples[2] = refLumaBlk.at(0, pos) + lumaOffset; // E
+          samples[3] = refLumaBlk1.at(-1, pos) + lumaOffset; // C
+          samples[4] = refLumaBlk1.at(-2, pos) + lumaOffset; // W
+          samples[5] = refLumaBlk1.at(0, pos) + lumaOffset; // E
+          samples[6] = cccmModel[0].nonlinear(refLumaBlk.at(-1, pos) + lumaOffset);
+          samples[7] = cccmModel[0].nonlinear(refLumaBlk.at(-2, pos) + lumaOffset);
+          samples[8] = cccmModel[0].nonlinear(refLumaBlk.at(0, pos) + lumaOffset);
+          samples[9] = ((-1 + refSizeX + CCCM_LOC_OFFSET) << CCCM_LOC_SHIFT); // X coordinate
+          samples[10] = cccmModel[0].bias();
+          if ((refLumaBlk.at(-1, pos) + lumaOffset) <= modelThr)
+          {
+            predChroma = cccmModel[0].convolve(samples);
+            totalOffset[0] += curChroma0[pos * curStride] - predChroma;
+            count[0]++;
+          }
+          else
+          {
+            predChroma = cccmModel[1].convolve(samples);
+            totalOffset[1] += curChroma0[pos * curStride] - predChroma;
+            count[1]++;
+          }
+        }
+      }
+      else if (pu.cccmMultiFilterIdx == 3)
+      {
+        for (int pos = 0; pos < cHeight; pos++)
+        {
+          samples[0] = refLumaBlk.at(-1, pos) + lumaOffset;      // C
+          samples[1] = refLumaBlk.at(0, pos - 1) + lumaOffset;  // EN
+          samples[2] = refLumaBlk.at(-2, pos + 1) + lumaOffset;  // WS
+          samples[3] = refLumaBlk3.at(-1, pos) + lumaOffset;     // C
+          samples[4] = refLumaBlk3.at(0, pos - 1) + lumaOffset; // EN
+          samples[5] = refLumaBlk3.at(-2, pos + 1) + lumaOffset; // WS
+          samples[6] = cccmModel[0].nonlinear(refLumaBlk.at(-1, pos) + lumaOffset);
+          samples[7] = cccmModel[0].nonlinear(refLumaBlk.at(0, pos - 1) + lumaOffset);
+          samples[8] = cccmModel[0].nonlinear(refLumaBlk.at(-2, pos + 1) + lumaOffset);
+          samples[9] = ((pos + refSizeY + CCCM_LOC_OFFSET) << CCCM_LOC_SHIFT); // Y coordinate
+          samples[10] = cccmModel[0].bias();
+          if ((refLumaBlk.at(-1, pos) + lumaOffset) <= modelThr)
+          {
+            predChroma = cccmModel[0].convolve(samples);
+            totalOffset[0] += curChroma0[pos * curStride] - predChroma;
+            count[0]++;
+          }
+          else
+          {
+            predChroma = cccmModel[1].convolve(samples);
+            totalOffset[1] += curChroma0[pos * curStride] - predChroma;
+            count[1]++;
+          }
+        }
+      }
+      else
+      {
+        THROW("Invalid cccmMultiFilterIdx");
+      }
+    }
+    else
+#endif
+    {
+      if (pu.cccmMultiFilterIdx == 2)
+      {
+        for (int pos = 0; pos < cHeight; pos++)
+        {
+          samples[0] = refLumaBlk.at(-1, pos) + lumaOffset;  // C
+          samples[1] = refLumaBlk.at(-2, pos) + lumaOffset;  // W
+          samples[2] = refLumaBlk.at(0, pos) + lumaOffset;  // E
+          samples[3] = refLumaBlk1.at(-1, pos) + lumaOffset; // C
+          samples[4] = refLumaBlk1.at(-2, pos) + lumaOffset; // W
+          samples[5] = refLumaBlk1.at(0, pos) + lumaOffset; // E
+          samples[6] = cccmModel[0].nonlinear(refLumaBlk.at(-1, pos)) + lumaOffset;
+          samples[7] = cccmModel[0].nonlinear(refLumaBlk.at(-2, pos)) + lumaOffset;
+          samples[8] = cccmModel[0].nonlinear(refLumaBlk.at(0, pos)) + lumaOffset;
+          samples[9] = ((-1 + refSizeX + CCCM_LOC_OFFSET) << CCCM_LOC_SHIFT); // X coordinate
+          samples[10] = cccmModel[0].bias();
+          predChroma = cccmModel[0].convolve(samples);
+          totalOffset[0] += curChroma0[pos * curStride] - predChroma;
+          count[0]++;
+        }
+      }
+      else if (pu.cccmMultiFilterIdx == 3)
+      {
+        for (int pos = 0; pos < cHeight; pos++)
+        {
+          samples[0] = refLumaBlk.at(-1, pos) + lumaOffset;      // C
+          samples[1] = refLumaBlk.at(0, pos - 1) + lumaOffset;  // EN
+          samples[2] = refLumaBlk.at(-2, pos + 1) + lumaOffset;  // WS
+          samples[3] = refLumaBlk3.at(-1, pos) + lumaOffset;     // C
+          samples[4] = refLumaBlk3.at(0, pos - 1) + lumaOffset; // EN
+          samples[5] = refLumaBlk3.at(-2, pos + 1) + lumaOffset; // WS
+          samples[6] = cccmModel[0].nonlinear(refLumaBlk.at(-1, pos) + lumaOffset);
+          samples[7] = cccmModel[0].nonlinear(refLumaBlk.at(0, pos - 1) + lumaOffset);
+          samples[8] = cccmModel[0].nonlinear(refLumaBlk.at(-2, pos + 1) + lumaOffset);
+          samples[9] = ((pos + refSizeY + CCCM_LOC_OFFSET) << CCCM_LOC_SHIFT); // Y coordinate
+          samples[10] = cccmModel[0].bias();
+          predChroma = cccmModel[0].convolve(samples);
+          totalOffset[0] += curChroma0[pos * curStride] - predChroma;
+          count[0]++;
+        }
+      }
+      else
+      {
+        THROW("Invalid cccmMultiFilterIdx");
+      }
+    }
+  }
+
+  chromaOffset[0] = chromaOffset[1] = 0;
+
+  if (modelNum == 2)
+  {
+    if (count[0])
+    {
+      chromaOffset[0] = PU::getMeanValue(totalOffset[0], count[0]);   // totalOffset[0] / count[0];
+    }
+    if (count[1])
+    {
+      chromaOffset[1] = PU::getMeanValue(totalOffset[1], count[1]);   // totalOffset[1] / count[1];
+    }
+  }
+  else
+  {
+    if (count[0])
+    {
+      chromaOffset[0] = PU::getMeanValue(totalOffset[0], count[0]);   // totalOffset[0] / count[0];
+    }
+  }
+}
+#endif
+
+#if JVET_AC0147_CCCM_NO_SUBSAMPLING
+void IntraPrediction::xNSCccmApplyModelOffset(const PredictionUnit &pu, const ComponentID compId,
+                                              const CccmModel<CCCM_NO_SUB_NUM_PARAMS> &cccmModel, int modelId,
+                                              int modelThr, PelBuf &piPred, int lumaOffset, int chromaOffset[2]) const
+{
+  const ClpRng &clpRng(pu.cu->cs->slice->clpRng(compId));
+  static Pel    samples[CCCM_NO_SUB_NUM_PARAMS];
+
+  CPelBuf   refLumaBlk   = xCccmGetLumaPuBuf(pu);
+  const int chromaScaleX = getChannelTypeScaleX(CHANNEL_TYPE_CHROMA, pu.cu->slice->getSPS()->getChromaFormatIdc());
+  const int chromaScaleY = getChannelTypeScaleY(CHANNEL_TYPE_CHROMA, pu.cu->slice->getSPS()->getChromaFormatIdc());
+  const int stepX        = 1 << chromaScaleX;
+  const int stepY        = 1 << chromaScaleY;
+  int offset = modelId == 2 ? chromaOffset[1] : chromaOffset[0];
+
+  for (int y = 0; y < refLumaBlk.height; y += stepY)
+  {
+    for (int x = 0; x < refLumaBlk.width; x += stepX)
+    {
+      const Pel *src0 = refLumaBlk.bufAt(x, y);
+      const Pel *src1 = refLumaBlk.bufAt(x, y + 1);
+
+      if (modelId == 1 && (src0[0] + lumaOffset) > modelThr)   // Model 1: Include only samples below or equal to the threshold
+      {
+        continue;
+      }
+      if (modelId == 2 && (src0[0] + lumaOffset) <= modelThr)   // Model 2: Include only samples above the threshold
+      {
+        continue;
+      }
+
+      samples[0]  = src0[0] + lumaOffset;
+      samples[1]  = src0[-1] + lumaOffset;
+      samples[2]  = src0[1] + lumaOffset;
+      samples[3]  = src1[0] + lumaOffset;
+      samples[4]  = src1[-1] + lumaOffset;
+      samples[5]  = src1[1] + lumaOffset;
+      samples[6]  = const_cast<CccmModel<CCCM_NO_SUB_NUM_PARAMS> &>(cccmModel).nonlinear(src0[0] + lumaOffset);
+      samples[7]  = const_cast<CccmModel<CCCM_NO_SUB_NUM_PARAMS> &>(cccmModel).nonlinear(src1[0] + lumaOffset);
+      samples[8]  = const_cast<CccmModel<CCCM_NO_SUB_NUM_PARAMS> &>(cccmModel).nonlinear(src0[1] + lumaOffset);
+      samples[9]  = const_cast<CccmModel<CCCM_NO_SUB_NUM_PARAMS> &>(cccmModel).nonlinear(src0[-1] + lumaOffset);
+      samples[10] = const_cast<CccmModel<CCCM_NO_SUB_NUM_PARAMS> &>(cccmModel).bias();
+      piPred.at(x >> chromaScaleX, y >> chromaScaleY) =
+        ClipPel<Pel>(const_cast<CccmModel<CCCM_NO_SUB_NUM_PARAMS> &>(cccmModel).convolve(samples) + offset, clpRng);
+    }
+  }
+}
+void IntraPrediction::xGetUpdatedOffsetNSCCCM(const PredictionUnit &pu, const ComponentID compID, int modelNum,
+                                              const CompArea                   &chromaArea,
+                                              CccmModel<CCCM_NO_SUB_NUM_PARAMS> cccmModel[2],
+                                            int modelThr, int lumaOffset, int chromaOffset[])
+{
+  static Pel    samples[CCCM_NO_SUB_NUM_PARAMS];
+
+  CPelBuf   refLumaBlk   = xCccmGetLumaPuBuf(pu);
+  const int chromaScaleX = getChannelTypeScaleX(CHANNEL_TYPE_CHROMA, pu.cu->slice->getSPS()->getChromaFormatIdc());
+  const int chromaScaleY = getChannelTypeScaleY(CHANNEL_TYPE_CHROMA, pu.cu->slice->getSPS()->getChromaFormatIdc());
+  const int stepX        = 1 << chromaScaleX;
+  const int stepY        = 1 << chromaScaleY;
+
+  const SizeType cWidth  = chromaArea.width;
+  const SizeType cHeight = chromaArea.height;
+
+  CodingStructure  &cs = *(pu.cs);
+  const CodingUnit &cu = *(pu.cu);
+
+  const bool aboveAvailable = cu.cs->getCU(cu.blocks[compID].pos().offset(0, -1), toChannelType(compID)) ? true : false;
+  const bool leftAvailable  = cu.cs->getCU(cu.blocks[compID].pos().offset(-1, 0), toChannelType(compID)) ? true : false;
+
+  const Pel *curChroma0;
+
+
+  PelBuf chromaReco = cs.picture->getRecoBuf(chromaArea);
+
+  Pel      *curChromaBuf = chromaReco.buf;
+  const int curStride    = chromaReco.stride;
+
+  int totalOffset[2] = { 0, 0 };
+  int count[2]       = { 0, 0 };
+
+  Pel predChroma;
+
+  if (aboveAvailable)
+  {
+    curChroma0 = curChromaBuf - curStride;
+#if MMLM
+    if (modelNum == 2)
+    {
+      for (int pos = 0, x = 0; pos < cWidth; pos++, x += stepX)
+      {
+        const Pel *src0 = refLumaBlk.bufAt(x, -stepY);
+        const Pel *src1 = refLumaBlk.bufAt(x, -stepY + 1);
+        samples[0]  = src0[0] + lumaOffset;
+        samples[1]  = src0[-1] + lumaOffset;
+        samples[2]  = src0[1] + lumaOffset;
+        samples[3]  = src1[0] + lumaOffset;
+        samples[4]  = src1[-1] + lumaOffset;
+        samples[5]  = src1[1] + lumaOffset;
+        samples[6]  = const_cast<CccmModel<CCCM_NO_SUB_NUM_PARAMS> &>(cccmModel[0]).nonlinear(src0[0] + lumaOffset);
+        samples[7]  = const_cast<CccmModel<CCCM_NO_SUB_NUM_PARAMS> &>(cccmModel[0]).nonlinear(src1[0] + lumaOffset);
+        samples[8]  = const_cast<CccmModel<CCCM_NO_SUB_NUM_PARAMS> &>(cccmModel[0]).nonlinear(src0[1] + lumaOffset);
+        samples[9]  = const_cast<CccmModel<CCCM_NO_SUB_NUM_PARAMS> &>(cccmModel[0]).nonlinear(src0[-1] + lumaOffset);
+        samples[10] = const_cast<CccmModel<CCCM_NO_SUB_NUM_PARAMS> &>(cccmModel[0]).bias();
+
+        if (src0[0] + lumaOffset <= modelThr)
+        {
+          predChroma = cccmModel[0].convolve(samples);
+          totalOffset[0] += curChroma0[pos] - predChroma;
+          count[0]++;
+        }
+        else
+        {
+          predChroma = cccmModel[1].convolve(samples);
+          totalOffset[1] += curChroma0[pos] - predChroma;
+          count[1]++;
+        }
+      }
+    }
+    else
+#endif
+    {
+      for (int pos = 0, x = 0; pos < cWidth; pos++, x += stepX)
+      {
+        const Pel *src0 = refLumaBlk.bufAt(x, -stepY);
+        const Pel *src1 = refLumaBlk.bufAt(x, -stepY + 1);
+        samples[0]  = src0[0] + lumaOffset;
+        samples[1]  = src0[-1] + lumaOffset;
+        samples[2]  = src0[1] + lumaOffset;
+        samples[3]  = src1[0] + lumaOffset;
+        samples[4]  = src1[-1] + lumaOffset;
+        samples[5]  = src1[1] + lumaOffset;
+        samples[6]  = const_cast<CccmModel<CCCM_NO_SUB_NUM_PARAMS> &>(cccmModel[0]).nonlinear(src0[0] + lumaOffset);
+        samples[7]  = const_cast<CccmModel<CCCM_NO_SUB_NUM_PARAMS> &>(cccmModel[0]).nonlinear(src1[0] + lumaOffset);
+        samples[8]  = const_cast<CccmModel<CCCM_NO_SUB_NUM_PARAMS> &>(cccmModel[0]).nonlinear(src0[1] + lumaOffset);
+        samples[9]  = const_cast<CccmModel<CCCM_NO_SUB_NUM_PARAMS> &>(cccmModel[0]).nonlinear(src0[-1] + lumaOffset);
+        samples[10] = const_cast<CccmModel<CCCM_NO_SUB_NUM_PARAMS> &>(cccmModel[0]).bias();
+        predChroma = cccmModel[0].convolve(samples);
+        totalOffset[0] += curChroma0[pos] - predChroma;
+        count[0]++;
+      }
+    }
+  }
+
+  if (leftAvailable)
+  {
+    curChroma0 = curChromaBuf - 1;
+#if MMLM
+    if (modelNum == 2)
+    {
+      for (int pos = 0, y = 0; pos < cHeight; pos++, y += stepY)
+      {
+        const Pel *src0 = refLumaBlk.bufAt(-stepX, y);
+        const Pel *src1 = refLumaBlk.bufAt(-stepX, y + 1);
+        samples[0]  = src0[0] + lumaOffset;
+        samples[1]  = src0[-1] + lumaOffset;
+        samples[2]  = src0[1] + lumaOffset;
+        samples[3]  = src1[0] + lumaOffset;
+        samples[4]  = src1[-1] + lumaOffset;
+        samples[5]  = src1[1] + lumaOffset;
+        samples[6]  = const_cast<CccmModel<CCCM_NO_SUB_NUM_PARAMS> &>(cccmModel[0]).nonlinear(src0[0] + lumaOffset);
+        samples[7]  = const_cast<CccmModel<CCCM_NO_SUB_NUM_PARAMS> &>(cccmModel[0]).nonlinear(src1[0] + lumaOffset);
+        samples[8]  = const_cast<CccmModel<CCCM_NO_SUB_NUM_PARAMS> &>(cccmModel[0]).nonlinear(src0[1] + lumaOffset);
+        samples[9]  = const_cast<CccmModel<CCCM_NO_SUB_NUM_PARAMS> &>(cccmModel[0]).nonlinear(src0[-1] + lumaOffset);
+        samples[10] = const_cast<CccmModel<CCCM_NO_SUB_NUM_PARAMS> &>(cccmModel[0]).bias();
+
+        if (src0[0] + lumaOffset <= modelThr)
+        {
+          predChroma = cccmModel[0].convolve(samples);
+          totalOffset[0] += curChroma0[pos * curStride] - predChroma;
+          count[0]++;
+        }
+        else
+        {
+          predChroma = cccmModel[1].convolve(samples);
+          totalOffset[1] += curChroma0[pos * curStride] - predChroma;
+          count[1]++;
+        }
+      }
+    }
+    else
+#endif
+    {
+      for (int pos = 0, y = 0; pos < cHeight; pos++, y += stepY)
+      {
+        const Pel *src0 = refLumaBlk.bufAt(-stepX, y);
+        const Pel *src1 = refLumaBlk.bufAt(-stepX, y + 1);
+        samples[0]  = src0[0] + lumaOffset;
+        samples[1]  = src0[-1] + lumaOffset;
+        samples[2]  = src0[1] + lumaOffset;
+        samples[3]  = src1[0] + lumaOffset;
+        samples[4]  = src1[-1] + lumaOffset;
+        samples[5]  = src1[1] + lumaOffset;
+        samples[6]  = const_cast<CccmModel<CCCM_NO_SUB_NUM_PARAMS> &>(cccmModel[0]).nonlinear(src0[0] + lumaOffset);
+        samples[7]  = const_cast<CccmModel<CCCM_NO_SUB_NUM_PARAMS> &>(cccmModel[0]).nonlinear(src1[0] + lumaOffset);
+        samples[8]  = const_cast<CccmModel<CCCM_NO_SUB_NUM_PARAMS> &>(cccmModel[0]).nonlinear(src0[1] + lumaOffset);
+        samples[9]  = const_cast<CccmModel<CCCM_NO_SUB_NUM_PARAMS> &>(cccmModel[0]).nonlinear(src0[-1] + lumaOffset);
+        samples[10] = const_cast<CccmModel<CCCM_NO_SUB_NUM_PARAMS> &>(cccmModel[0]).bias();
+
+        predChroma = cccmModel[0].convolve(samples);
+        totalOffset[0] += curChroma0[pos * curStride] - predChroma;
+        count[0]++;
+      }
+    }
+  }
+
+  chromaOffset[0] = chromaOffset[1] = 0;
+
+  if (modelNum == 2)
+  {
+    if (count[0])
+    {
+      chromaOffset[0] = PU::getMeanValue(totalOffset[0], count[0]);   // totalOffset[0] / count[0];
+    }
+    if (count[1])
+    {
+      chromaOffset[1] = PU::getMeanValue(totalOffset[1], count[1]);   // totalOffset[1] / count[1];
+    }
+  }
+  else
+  {
+    if (count[0])
+    {
+      chromaOffset[0] = PU::getMeanValue(totalOffset[0], count[0]);   // totalOffset[0] / count[0];
+    }
+  }
+}
+
+int IntraPrediction::xGetCostNSCCCM(const PredictionUnit &pu, const ComponentID compID, int modelNum,
+                                    const CompArea &chromaArea, CccmModel<CCCM_NO_SUB_NUM_PARAMS> cccmModel[2],
+                                    int modelThr, int lumaOffset, int chromaOffset[])
+{
+  const ClpRng &clpRng(pu.cu->cs->slice->clpRng(compID));
+  static Pel samples[CCCM_NO_SUB_NUM_PARAMS];
+
+  CPelBuf   refLumaBlk   = xCccmGetLumaPuBuf(pu);
+  const int chromaScaleX = getChannelTypeScaleX(CHANNEL_TYPE_CHROMA, pu.cu->slice->getSPS()->getChromaFormatIdc());
+  const int chromaScaleY = getChannelTypeScaleY(CHANNEL_TYPE_CHROMA, pu.cu->slice->getSPS()->getChromaFormatIdc());
+  const int stepX        = 1 << chromaScaleX;
+  const int stepY        = 1 << chromaScaleY;
+
+  const SizeType cWidth  = chromaArea.width;
+  const SizeType cHeight = chromaArea.height;
+
+  CodingStructure  &cs = *(pu.cs);
+  const CodingUnit &cu = *(pu.cu);
+
+  const bool aboveAvailable = cu.cs->getCU(cu.blocks[compID].pos().offset(0, -1), toChannelType(compID)) ? true : false;
+  const bool leftAvailable  = cu.cs->getCU(cu.blocks[compID].pos().offset(-1, 0), toChannelType(compID)) ? true : false;
+
+  const Pel *curChroma0;
+
+  PelBuf chromaReco = cs.picture->getRecoBuf(chromaArea);
+
+  Pel      *curChromaBuf = chromaReco.buf;
+  const int curStride    = chromaReco.stride;
+
+  int sad = 0;
+
+  Pel predChroma;
+
+  if (aboveAvailable)
+  {
+    curChroma0 = curChromaBuf - curStride;
+#if MMLM
+    if (modelNum == 2)
+    {
+      for (int pos = 0, x = 0; pos < cWidth; pos++, x += stepX)
+      {
+        const Pel *src0 = refLumaBlk.bufAt(x, -stepY);
+        const Pel *src1 = refLumaBlk.bufAt(x, -stepY + 1);
+        samples[0]  = src0[0] + lumaOffset;
+        samples[1]  = src0[-1] + lumaOffset;
+        samples[2]  = src0[1] + lumaOffset;
+        samples[3]  = src1[0] + lumaOffset;
+        samples[4]  = src1[-1] + lumaOffset;
+        samples[5]  = src1[1] + lumaOffset;
+        samples[6]  = const_cast<CccmModel<CCCM_NO_SUB_NUM_PARAMS> &>(cccmModel[0]).nonlinear(src0[0] + lumaOffset);
+        samples[7]  = const_cast<CccmModel<CCCM_NO_SUB_NUM_PARAMS> &>(cccmModel[0]).nonlinear(src1[0] + lumaOffset);
+        samples[8]  = const_cast<CccmModel<CCCM_NO_SUB_NUM_PARAMS> &>(cccmModel[0]).nonlinear(src0[1] + lumaOffset);
+        samples[9]  = const_cast<CccmModel<CCCM_NO_SUB_NUM_PARAMS> &>(cccmModel[0]).nonlinear(src0[-1] + lumaOffset);
+        samples[10] = const_cast<CccmModel<CCCM_NO_SUB_NUM_PARAMS> &>(cccmModel[0]).bias();
+
+        if (src0[0] + lumaOffset <= modelThr)
+        {
+          predChroma = cccmModel[0].convolve(samples) + chromaOffset[0];
+        }
+        else
+        {
+          predChroma = cccmModel[1].convolve(samples) + chromaOffset[1];
+        }
+        predChroma = ClipPel<Pel>(predChroma, clpRng);
+        sad += abs(curChroma0[pos] - predChroma);
+      }
+    }
+    else
+#endif
+    {
+      for (int pos = 0, x = 0; pos < cWidth; pos++, x += stepX)
+      {
+        const Pel *src0 = refLumaBlk.bufAt(x, -stepY);
+        const Pel *src1 = refLumaBlk.bufAt(x, -stepY + 1);
+        samples[0]  = src0[0] + lumaOffset;
+        samples[1]  = src0[-1] + lumaOffset;
+        samples[2]  = src0[1] + lumaOffset;
+        samples[3]  = src1[0] + lumaOffset;
+        samples[4]  = src1[-1] + lumaOffset;
+        samples[5]  = src1[1] + lumaOffset;
+        samples[6]  = const_cast<CccmModel<CCCM_NO_SUB_NUM_PARAMS> &>(cccmModel[0]).nonlinear(src0[0] + lumaOffset);
+        samples[7]  = const_cast<CccmModel<CCCM_NO_SUB_NUM_PARAMS> &>(cccmModel[0]).nonlinear(src1[0] + lumaOffset);
+        samples[8]  = const_cast<CccmModel<CCCM_NO_SUB_NUM_PARAMS> &>(cccmModel[0]).nonlinear(src0[1] + lumaOffset);
+        samples[9]  = const_cast<CccmModel<CCCM_NO_SUB_NUM_PARAMS> &>(cccmModel[0]).nonlinear(src0[-1] + lumaOffset);
+        samples[10] = const_cast<CccmModel<CCCM_NO_SUB_NUM_PARAMS> &>(cccmModel[0]).bias();
+        predChroma  = cccmModel[0].convolve(samples) + chromaOffset[0];
+        predChroma  = ClipPel<Pel>(predChroma, clpRng);
+        sad += abs(curChroma0[pos] - predChroma);
+      }
+    }
+  }
+
+  if (leftAvailable)
+  {
+    curChroma0 = curChromaBuf - 1;
+#if MMLM
+    if (modelNum == 2)
+    {
+      for (int pos = 0, y = 0; pos < cHeight; pos++, y += stepY)
+      {
+        const Pel *src0 = refLumaBlk.bufAt(-stepX, y);
+        const Pel *src1 = refLumaBlk.bufAt(-stepX, y + 1);
+        samples[0]      = src0[0] + lumaOffset;
+        samples[1]      = src0[-1] + lumaOffset;
+        samples[2]      = src0[1] + lumaOffset;
+        samples[3]      = src1[0] + lumaOffset;
+        samples[4]      = src1[-1] + lumaOffset;
+        samples[5]      = src1[1] + lumaOffset;
+        samples[6]      = const_cast<CccmModel<CCCM_NO_SUB_NUM_PARAMS> &>(cccmModel[0]).nonlinear(src0[0] + lumaOffset);
+        samples[7]      = const_cast<CccmModel<CCCM_NO_SUB_NUM_PARAMS> &>(cccmModel[0]).nonlinear(src1[0] + lumaOffset);
+        samples[8]      = const_cast<CccmModel<CCCM_NO_SUB_NUM_PARAMS> &>(cccmModel[0]).nonlinear(src0[1] + lumaOffset);
+        samples[9]      = const_cast<CccmModel<CCCM_NO_SUB_NUM_PARAMS> &>(cccmModel[0]).nonlinear(src0[-1] + lumaOffset);
+        samples[10]     = const_cast<CccmModel<CCCM_NO_SUB_NUM_PARAMS> &>(cccmModel[0]).bias();
+
+        if (src0[0] + lumaOffset <= modelThr)
+        {
+          predChroma = cccmModel[0].convolve(samples) + chromaOffset[0];
+        }
+        else
+        {
+          predChroma = cccmModel[1].convolve(samples) + chromaOffset[1];
+        } 
+        predChroma = ClipPel<Pel>(predChroma, clpRng);
+        sad += abs(curChroma0[pos * curStride] - predChroma);
+      }
+    }
+    else
+#endif
+    {
+      for (int pos = 0, y = 0; pos < cHeight; pos++, y += stepY)
+      {
+        const Pel *src0 = refLumaBlk.bufAt(-stepX, y);
+        const Pel *src1 = refLumaBlk.bufAt(-stepX, y + 1);
+        samples[0]  = src0[0] + lumaOffset;
+        samples[1]  = src0[-1] + lumaOffset;
+        samples[2]  = src0[1] + lumaOffset;
+        samples[3]  = src1[0] + lumaOffset;
+        samples[4]  = src1[-1] + lumaOffset;
+        samples[5]  = src1[1] + lumaOffset;
+        samples[6]  = const_cast<CccmModel<CCCM_NO_SUB_NUM_PARAMS> &>(cccmModel[0]).nonlinear(src0[0] + lumaOffset);
+        samples[7]  = const_cast<CccmModel<CCCM_NO_SUB_NUM_PARAMS> &>(cccmModel[0]).nonlinear(src1[0] + lumaOffset);
+        samples[8]  = const_cast<CccmModel<CCCM_NO_SUB_NUM_PARAMS> &>(cccmModel[0]).nonlinear(src0[1] + lumaOffset);
+        samples[9]  = const_cast<CccmModel<CCCM_NO_SUB_NUM_PARAMS> &>(cccmModel[0]).nonlinear(src0[-1] + lumaOffset);
+        samples[10] = const_cast<CccmModel<CCCM_NO_SUB_NUM_PARAMS> &>(cccmModel[0]).bias();
+
+        predChroma = cccmModel[0].convolve(samples);
+        predChroma = ClipPel<Pel>(predChroma, clpRng);
+        sad += abs(curChroma0[pos * curStride] - predChroma);
+      }
+    }
+  }
+  return sad;
+}
+#endif
+
+void IntraPrediction::xGetUpdatedOffsetCCLM(const PredictionUnit &pu, const ComponentID compID,
+                                            const CompArea &chromaArea, CclmModel &cclmModel, int modelNum, int glmIdc)
+{
+  int    srcStride = 0;
+  PelBuf temp;
+
+  if (glmIdc > 0)
+  {
+    Pel *glmTemp = m_glmTempCb[glmIdc];
+    srcStride    = 2 * MAX_CU_SIZE + 1;
+    temp         = PelBuf(glmTemp + srcStride + 1, srcStride, Size(chromaArea));
+  }
+  else
+  {
+    srcStride = MAX_CU_SIZE + 1;
+    temp      = PelBuf(m_piTemp + srcStride + 1, srcStride, Size(chromaArea));
+  }
+
+  const SizeType cWidth  = chromaArea.width;
+  const SizeType cHeight = chromaArea.height;
+
+  CodingStructure  &cs = *(pu.cs);
+  const CodingUnit &cu = *(pu.cu);
+
+  const bool aboveAvailable = cu.cs->getCU(cu.blocks[compID].pos().offset(0, -1), toChannelType(compID)) ? true : false;
+  const bool leftAvailable  = cu.cs->getCU(cu.blocks[compID].pos().offset(-1, 0), toChannelType(compID)) ? true : false;
+
+  Pel *srcColor0, *curChroma0;
+
+  srcColor0 = temp.bufAt(0, 0);
+
+  PelBuf chromaReco = cs.picture->getRecoBuf(chromaArea);
+
+  Pel      *curChromaBuf = chromaReco.buf;
+  const int curStride    = chromaReco.stride;
+
+  int totalOffset[2] = { 0, 0 };
+  int count[2]       = { 0, 0 };
+  if (aboveAvailable)
+  {
+    curChroma0 = curChromaBuf - curStride;
+    Pel *src   = srcColor0 - srcStride;
+    if (modelNum == 2)
+    {
+      Pel predChroma;
+      for (int pos = 0; pos < cWidth; pos++)
+      {
+        if (src[pos] <= cclmModel.yThres)
+        {
+          predChroma = rightShift(cclmModel.a * src[pos], cclmModel.shift) + cclmModel.b;
+          totalOffset[0] += curChroma0[pos] - predChroma;
+          count[0]++;
+        }
+        else
+        {
+          predChroma = rightShift(cclmModel.a2 * src[pos], cclmModel.shift2) + cclmModel.b2;
+          totalOffset[1] += curChroma0[pos] - predChroma;
+          count[1]++;
+        }
+      }
+    }
+    else
+    {
+      for (int pos = 0; pos < cWidth; pos++)
+      {
+        Pel predChroma = rightShift(cclmModel.a * src[pos], cclmModel.shift) + cclmModel.b;
+        totalOffset[0] += curChroma0[pos] - predChroma;
+        count[0]++;
+      }
+    }
+  }
+
+  if (leftAvailable)
+  {
+    curChroma0 = curChromaBuf - 1;
+    Pel *src = srcColor0 - 1;
+    if (modelNum == 2)
+    {
+      Pel predChroma;
+      for (int pos = 0; pos < cHeight; pos++)
+      {
+        if (src[pos * srcStride] <= cclmModel.yThres)
+        {
+          predChroma = rightShift(cclmModel.a * src[pos * srcStride], cclmModel.shift) + cclmModel.b;
+          totalOffset[0] += curChroma0[pos * curStride] - predChroma;
+          count[0]++;
+        }
+        else
+        {
+          predChroma = rightShift(cclmModel.a2 * src[pos * srcStride], cclmModel.shift2) + cclmModel.b2;
+          totalOffset[1] += curChroma0[pos * curStride] - predChroma;
+          count[1]++;
+        }
+      }
+    }
+    else
+    {
+      for (int pos = 0; pos < cHeight; pos++)
+      {
+        Pel predChroma = rightShift(cclmModel.a * src[pos * srcStride], cclmModel.shift) + cclmModel.b;
+        totalOffset[0] += curChroma0[pos * curStride] - predChroma;
+        count[0]++;
+      }
+    }
+  }
+
+  if (modelNum == 2)
+  {
+    if (count[0])
+    {
+      cclmModel.b += PU::getMeanValue(totalOffset[0], count[0]);   // totalOffset[0] / count[0];
+    }
+    if (count[1])
+    {
+      cclmModel.b2 += PU::getMeanValue(totalOffset[1], count[1]);   //totalOffset[1] / count[1];
+    }
+  }
+  else
+  {
+    if (count[0])
+    {
+      cclmModel.b += PU::getMeanValue(totalOffset[0], count[0]);   //totalOffset[0] / count[0];
+    }
+  }
+}
+
+int  IntraPrediction::xGetCostCCLM(const PredictionUnit &pu, const ComponentID compID, const CompArea &chromaArea,CclmModel &cclmModel, int modelNum, int glmIdc)
+{
+  int    srcStride = 0;
+  PelBuf temp;
+
+  if (glmIdc > 0)
+  {
+    Pel *glmTemp = m_glmTempCb[glmIdc];
+    srcStride    = 2 * MAX_CU_SIZE + 1;
+    temp         = PelBuf(glmTemp + srcStride + 1, srcStride, Size(chromaArea));
+  }
+  else
+  {
+    srcStride = MAX_CU_SIZE + 1;
+    temp      = PelBuf(m_piTemp + srcStride + 1, srcStride, Size(chromaArea));
+  }
+
+  const SizeType cWidth  = chromaArea.width;
+  const SizeType cHeight = chromaArea.height;
+
+  CodingStructure  &cs = *(pu.cs);
+  const CodingUnit &cu = *(pu.cu);
+
+  const bool aboveAvailable = cu.cs->getCU(cu.blocks[compID].pos().offset(0, -1), toChannelType(compID)) ? true : false;
+  const bool leftAvailable  = cu.cs->getCU(cu.blocks[compID].pos().offset(-1, 0), toChannelType(compID)) ? true : false;
+
+  Pel *srcColor0, *curChroma0;
+
+  srcColor0 = temp.bufAt(0, 0);
+
+  PelBuf chromaReco = cs.picture->getRecoBuf(chromaArea);
+
+  Pel      *curChromaBuf = chromaReco.buf;
+  const int curStride    = chromaReco.stride;
+
+  int sad = 0;
+  if (aboveAvailable)
+  {
+    curChroma0 = curChromaBuf - curStride;
+    Pel *src   = srcColor0 - srcStride;
+    if (modelNum == 2)
+    {
+      Pel predChroma;
+      for (int pos = 0; pos < cWidth; pos++)
+      {
+        if (src[pos] <= cclmModel.yThres)
+        {
+          predChroma = rightShift(cclmModel.a * src[pos], cclmModel.shift) + cclmModel.b;
+        }
+        else
+        {
+          predChroma = rightShift(cclmModel.a2 * src[pos], cclmModel.shift2) + cclmModel.b2;
+        }
+        sad += abs( curChroma0[pos] - predChroma);
+      }
+    }
+    else
+    {
+      for (int pos = 0; pos < cWidth; pos++)
+      {
+        Pel predChroma = rightShift(cclmModel.a * src[pos], cclmModel.shift) + cclmModel.b;
+        sad += abs(curChroma0[pos] - predChroma);
+      }
+    }
+  }
+
+  if (leftAvailable)
+  {
+    curChroma0 = curChromaBuf - 1;
+    Pel *src = srcColor0 - 1;
+    if (modelNum == 2)
+    {
+      Pel predChroma;
+      for (int pos = 0; pos < cHeight; pos++)
+      {
+        if (src[pos * srcStride] <= cclmModel.yThres)
+        {
+          predChroma = rightShift(cclmModel.a * src[pos * srcStride], cclmModel.shift) + cclmModel.b;
+
+        }
+        else
+        {
+          predChroma = rightShift(cclmModel.a2 * src[pos * srcStride], cclmModel.shift2) + cclmModel.b2;
+        }
+        sad += abs(curChroma0[pos * curStride] - predChroma);
+      }
+    }
+    else
+    {
+      for (int pos = 0; pos < cHeight; pos++)
+      {
+        Pel predChroma = rightShift(cclmModel.a * src[pos * srcStride], cclmModel.shift) + cclmModel.b;
+        sad += abs(curChroma0[pos * curStride] - predChroma);
+      }
+    }
+  }
+  return sad;
+}
+
+void IntraPrediction::xGetUpdatedOffsetCCCM(const PredictionUnit &pu, const ComponentID compID, int modelNum,
+                                            const CompArea &chromaArea, CccmModel<CCCM_NUM_PARAMS> cccmModel[2],
+                                            int modelThr, int lumaOffset, int chromaOffset[], int type,
+                                            int refSizeX, int refSizeY)
+{
+  int srcStride = 0;
+
+  CHECK(compID != chromaArea.compID, "Invalid component ID");
+
+  static Pel samples[CCCM_NUM_PARAMS];
+
+  CPelBuf temp = xCccmGetLumaPuBuf(pu);
+
+  const SizeType cWidth  = chromaArea.width;
+  const SizeType cHeight = chromaArea.height;
+
+  CodingStructure  &cs = *(pu.cs);
+  const CodingUnit &cu = *(pu.cu);
+
+  const bool aboveAvailable = cu.cs->getCU(cu.blocks[compID].pos().offset(0, -1), toChannelType(compID)) ? true : false;
+  const bool leftAvailable  = cu.cs->getCU(cu.blocks[compID].pos().offset(-1, 0), toChannelType(compID)) ? true : false;
+
+  const Pel *srcColor0, *curChroma0;
+
+  srcColor0 = temp.bufAt(0, 0);
+  srcStride = temp.stride;
+
+  PelBuf chromaReco = cs.picture->getRecoBuf(chromaArea);
+
+  Pel      *curChromaBuf = chromaReco.buf;
+  const int curStride    = chromaReco.stride;
+
+  int totalOffset[2] = { 0, 0 };
+  int count[2]       = { 0, 0 };
+
+  Pel predChroma;
+
+  if (aboveAvailable)
+  {
+    curChroma0     = curChromaBuf - curStride;
+    const Pel *src = srcColor0 - srcStride;
+#if MMLM
+    if (modelNum == 2)
+    {
+      if (type & CCP_TYPE_CCCM)
+      {
+        for (int pos = 0; pos < cWidth; pos++, src++)
+        {
+          samples[0] = *src + lumaOffset;                 // C
+          samples[1] = *(src - srcStride) + lumaOffset;   // N
+          samples[2] = *(src + srcStride) + lumaOffset;   // S
+          samples[3] = *(src - 1) + lumaOffset;           // W
+          samples[4] = *(src + 1) + lumaOffset;           // E
+          samples[5] = cccmModel[0].nonlinear(*src + lumaOffset);
+          samples[6] = cccmModel[0].bias();
+          if (*src + lumaOffset <= modelThr)
+          {
+            predChroma = cccmModel[0].convolve(samples);
+            totalOffset[0] += curChroma0[pos] - predChroma;
+            count[0]++;
+          }
+          else
+          {
+            predChroma = cccmModel[1].convolve(samples);
+            totalOffset[1] += curChroma0[pos] - predChroma;
+            count[1]++;
+          }
+        }
+      }
+#if JVET_AC0054_GLCCCM
+      else if (type & CCP_TYPE_GLCCCM)
+      {
+        for (int pos = 0; pos < cWidth; pos++, src++)
+        {
+          samples[0] = *src + lumaOffset;                 // C
+          samples[1] = (2 * (*(src - srcStride)) + (*(src - 1 - srcStride)) + (*(src - srcStride + 1)))
+                     - (2 * (*(src + srcStride)) + (*(src - 1 + srcStride)) + (*(src + srcStride + 1)));   // Vertical gradient
+          samples[2] = (2 * (*(src - 1)) + (*(src - 1 - srcStride)) + (*(src - 1 + srcStride)))
+                     - (2 * (*(src + 1)) + (*(src + 1 - srcStride)) + (*(src + 1 + srcStride)));   // Horizontal gradient
+          samples[3] = (( -1 + refSizeY + CCCM_LOC_OFFSET) << CCCM_LOC_SHIFT);   // Y coordinate
+          samples[4] = ((pos + refSizeX + CCCM_LOC_OFFSET) << CCCM_LOC_SHIFT);   // X coordinate
+          samples[5] = cccmModel[0].nonlinear(*src + lumaOffset);
+          samples[6] = cccmModel[0].bias();
+          if (*src + lumaOffset <= modelThr)
+          {
+            predChroma = cccmModel[0].convolve(samples);
+            totalOffset[0] += curChroma0[pos] - predChroma;
+            count[0]++;
+          }
+          else
+          {
+            predChroma = cccmModel[1].convolve(samples);
+            totalOffset[1] += curChroma0[pos] - predChroma;
+            count[1]++;
+          }
+        }
+      }
+      else
+      {
+        THROW("Invalid type");
+      }
+#endif
+    }
+    else
+#endif
+    {
+      if (type & CCP_TYPE_CCCM)
+      {
+        for (int pos = 0; pos < cWidth; pos++, src++)
+        {
+          samples[0] = *src + lumaOffset;                 // C
+          samples[1] = *(src - srcStride) + lumaOffset;   // N
+          samples[2] = *(src + srcStride) + lumaOffset;   // S
+          samples[3] = *(src - 1) + lumaOffset;           // W
+          samples[4] = *(src + 1) + lumaOffset;           // E
+          samples[5] = cccmModel[0].nonlinear(*src + lumaOffset);
+          samples[6] = cccmModel[0].bias();
+          predChroma = cccmModel[0].convolve(samples);
+          totalOffset[0] += curChroma0[pos] - predChroma;
+          count[0]++;
+        }
+      }
+#if JVET_AC0054_GLCCCM
+      else if (type & CCP_TYPE_GLCCCM)
+      {
+        for (int pos = 0; pos < cWidth; pos++, src++)
+        {
+          samples[0] = *src + lumaOffset;   // C
+          samples[1] = (2 * (*(src - srcStride)) + (*(src - 1 - srcStride)) + (*(src - srcStride + 1)))
+                     - (2 * (*(src + srcStride)) + (*(src - 1 + srcStride)) + (*(src + srcStride + 1)));   // Vertical gradient
+          samples[2] = (2 * (*(src - 1)) + (*(src - 1 - srcStride)) + (*(src - 1 + srcStride)))
+                     - (2 * (*(src + 1)) + (*(src + 1 - srcStride)) + (*(src + 1 + srcStride)));   // Horizontal gradient
+          samples[3] = (( -1 + refSizeY + CCCM_LOC_OFFSET) << CCCM_LOC_SHIFT);            // Y coordinate
+          samples[4] = ((pos + refSizeX + CCCM_LOC_OFFSET) << CCCM_LOC_SHIFT);            // X coordinate
+          samples[5] = cccmModel[0].nonlinear(*src + lumaOffset);
+          samples[6] = cccmModel[0].bias();
+          predChroma = cccmModel[0].convolve(samples);
+          totalOffset[0] += curChroma0[pos] - predChroma;
+          count[0]++;
+        }
+      }
+      else
+      {
+        THROW("Invalid type");
+      }
+#endif
+    }
+  }
+
+  if (leftAvailable)
+  {
+    curChroma0 = curChromaBuf - 1;
+    const Pel *src = srcColor0 - 1;
+#if MMLM
+    if (modelNum == 2)
+    {
+      if (type & CCP_TYPE_CCCM)
+      {
+        for (int pos = 0; pos < cHeight; pos++, src += srcStride)
+        {
+          samples[0] = *src + lumaOffset;                 // C
+          samples[1] = *(src - srcStride) + lumaOffset;   // N
+          samples[2] = *(src + srcStride) + lumaOffset;   // S
+          samples[3] = *(src - 1) + lumaOffset;           // W
+          samples[4] = *(src + 1) + lumaOffset;           // E
+          samples[5] = cccmModel[0].nonlinear(*src + lumaOffset);
+          samples[6] = cccmModel[0].bias();
+          if (*src + lumaOffset <= modelThr)
+          {
+            predChroma = cccmModel[0].convolve(samples);
+            totalOffset[0] += curChroma0[pos * curStride] - predChroma;
+            count[0]++;
+          }
+          else
+          {
+            predChroma = cccmModel[1].convolve(samples);
+            totalOffset[1] += curChroma0[pos * curStride] - predChroma;
+            count[1]++;
+          }
+        }
+      }
+#if JVET_AC0054_GLCCCM
+      else if (type & CCP_TYPE_GLCCCM)
+      {
+        for (int pos = 0; pos < cHeight; pos++, src += srcStride)
+        {
+          samples[0] = *src + lumaOffset;   // C
+          samples[1] = (2 * (*(src - srcStride)) + (*(src - 1 - srcStride)) + (*(src - srcStride + 1)))
+                     - (2 * (*(src + srcStride)) + (*(src - 1 + srcStride)) + (*(src + srcStride + 1)));   // Vertical gradient
+          samples[2] = (2 * (*(src - 1)) + (*(src - 1 - srcStride)) + (*(src - 1 + srcStride)))
+                     - (2 * (*(src + 1)) + (*(src + 1 - srcStride)) + (*(src + 1 + srcStride)));   // Horizontal gradient
+          samples[3] = ((pos + refSizeY + CCCM_LOC_OFFSET) << CCCM_LOC_SHIFT);            // Y coordinate
+          samples[4] = (( -1 + refSizeX + CCCM_LOC_OFFSET) << CCCM_LOC_SHIFT);            // X coordinate
+          samples[5] = cccmModel[0].nonlinear(*src + lumaOffset);
+          samples[6] = cccmModel[0].bias();
+          if (*src + lumaOffset <= modelThr)
+          {
+            predChroma = cccmModel[0].convolve(samples);
+            totalOffset[0] += curChroma0[pos * curStride] - predChroma;
+            count[0]++;
+          }
+          else
+          {
+            predChroma = cccmModel[1].convolve(samples);
+            totalOffset[1] += curChroma0[pos * curStride] - predChroma;
+            count[1]++;
+          }
+        }
+      }
+      else
+      {
+        THROW("Invalid type");
+      }
+#endif
+    }
+    else
+#endif
+    {
+      if (type & CCP_TYPE_CCCM)
+      {
+        for (int pos = 0; pos < cHeight; pos++, src += srcStride)
+        {
+          samples[0] = *src + lumaOffset;                 // C
+          samples[1] = *(src - srcStride) + lumaOffset;   // N
+          samples[2] = *(src + srcStride) + lumaOffset;   // S
+          samples[3] = *(src - 1) + lumaOffset;           // W
+          samples[4] = *(src + 1) + lumaOffset;           // E
+          samples[5] = cccmModel[0].nonlinear(*src + lumaOffset);
+          samples[6] = cccmModel[0].bias();
+          predChroma = cccmModel[0].convolve(samples);
+          totalOffset[0] += curChroma0[pos * curStride] - predChroma;
+          count[0]++;
+        }
+      }
+#if JVET_AC0054_GLCCCM
+      else if (type & CCP_TYPE_GLCCCM)
+      {
+        for (int pos = 0; pos < cHeight; pos++, src += srcStride)
+        {
+          samples[0] = *src + lumaOffset;   // C
+          samples[1] = (2 * (*(src - srcStride)) + (*(src - 1 - srcStride)) + (*(src - srcStride + 1)))
+                     - (2 * (*(src + srcStride)) + (*(src - 1 + srcStride)) + (*(src + srcStride + 1)));   // Vertical gradient
+          samples[2] = (2 * (*(src - 1)) + (*(src - 1 - srcStride)) + (*(src - 1 + srcStride)))
+                     - (2 * (*(src + 1)) + (*(src + 1 - srcStride)) + (*(src + 1 + srcStride)));   // Horizontal gradient
+          samples[3] = ((pos + refSizeY + CCCM_LOC_OFFSET) << CCCM_LOC_SHIFT);             // Y coordinate
+          samples[4] = (( -1 + refSizeX + CCCM_LOC_OFFSET) << CCCM_LOC_SHIFT);            // X coordinate
+          samples[5] = cccmModel[0].nonlinear(*src + lumaOffset);
+          samples[6] = cccmModel[0].bias();
+          predChroma = cccmModel[0].convolve(samples);
+          totalOffset[0] += curChroma0[pos * curStride] - predChroma;
+          count[0]++;
+        }
+      }
+      else
+      {
+        THROW("Invalid type");
+      }
+#endif
+    }
+  }
+
+  chromaOffset[0] = chromaOffset[1] = 0;
+
+  if (modelNum == 2)
+  {
+    if (count[0])
+    {
+      chromaOffset[0] = PU::getMeanValue(totalOffset[0], count[0]);   // totalOffset[0] / count[0];
+    }
+    if (count[1])
+    {
+      chromaOffset[1] = PU::getMeanValue(totalOffset[1], count[1]);   // totalOffset[1] / count[1];
+    }
+  }
+  else
+  {
+    if (count[0])
+    {
+      chromaOffset[0] = PU::getMeanValue(totalOffset[0], count[0]);   // totalOffset[0] / count[0];
+    }
+  }
+}
+int IntraPrediction::xGetCostCCCM(const PredictionUnit &pu, const ComponentID compID, int modelNum,
+                                  const CompArea &chromaArea, CccmModel<CCCM_NUM_PARAMS> cccmModel[2],
+                                  int modelThr, int lumaOffset, int chromaOffset[], int type, int refSizeX, int refSizeY)
+{
+  int srcStride = 0;
+  const ClpRng &clpRng(pu.cu->cs->slice->clpRng(compID));
+
+  CHECK(compID != chromaArea.compID, "Invalid component ID");
+
+  static Pel samples[CCCM_NUM_PARAMS];
+
+  CPelBuf temp = xCccmGetLumaPuBuf(pu);
+
+  const SizeType cWidth  = chromaArea.width;
+  const SizeType cHeight = chromaArea.height;
+
+  CodingStructure  &cs = *(pu.cs);
+  const CodingUnit &cu = *(pu.cu);
+
+  const bool aboveAvailable = cu.cs->getCU(cu.blocks[compID].pos().offset(0, -1), toChannelType(compID)) ? true : false;
+  const bool leftAvailable  = cu.cs->getCU(cu.blocks[compID].pos().offset(-1, 0), toChannelType(compID)) ? true : false;
+
+  const Pel *srcColor0, *curChroma0;
+
+  srcColor0 = temp.bufAt(0, 0);
+  srcStride = temp.stride;
+
+  PelBuf chromaReco = cs.picture->getRecoBuf(chromaArea);
+
+  Pel      *curChromaBuf = chromaReco.buf;
+  const int curStride    = chromaReco.stride;
+
+  int sad = 0;
+
+  Pel predChroma;
+
+  if (aboveAvailable)
+  {
+    curChroma0     = curChromaBuf - curStride;
+    const Pel *src = srcColor0 - srcStride;
+#if MMLM
+    if (type & CCP_TYPE_MMLM)
+    {
+      if (type & CCP_TYPE_CCCM)
+      {
+        for (int pos = 0; pos < cWidth; pos++, src++)
+        {
+          samples[0] = *src + lumaOffset;                 // C
+          samples[1] = *(src - srcStride) + lumaOffset;   // N
+          samples[2] = *(src + srcStride) + lumaOffset;   // S
+          samples[3] = *(src - 1) + lumaOffset;           // W
+          samples[4] = *(src + 1) + lumaOffset;           // E
+          samples[5] = cccmModel[0].nonlinear(*src + lumaOffset);
+          samples[6] = cccmModel[0].bias();
+          if (*src + lumaOffset <= modelThr)
+          {
+            predChroma = cccmModel[0].convolve(samples) + chromaOffset[0];
+          }
+          else
+          {
+            predChroma = cccmModel[1].convolve(samples) + chromaOffset[1];
+          }
+          predChroma = ClipPel<Pel>(predChroma, clpRng);
+          sad += abs(curChroma0[pos] - predChroma);
+        }
+      }
+#if JVET_AC0054_GLCCCM
+      else if (type & CCP_TYPE_GLCCCM)
+      {
+
+        for (int pos = 0; pos < cWidth; pos++, src++)
+        {
+          samples[0] = *src + lumaOffset;                 // C
+          samples[1] = (2 * (*(src - srcStride)) + (*(src - 1 - srcStride)) + (*(src - srcStride + 1)))
+                     - (2 * (*(src + srcStride)) + (*(src - 1 + srcStride)) + (*(src + srcStride + 1)));   // Vertical gradient
+          samples[2] = (2 * (*(src - 1)) + (*(src - 1 - srcStride)) + (*(src - 1 + srcStride)))
+                     - (2 * (*(src + 1)) + (*(src + 1 - srcStride)) + (*(src + 1 + srcStride)));   // Horizontal gradient
+          samples[3] = (( -1 + refSizeY + CCCM_LOC_OFFSET) << CCCM_LOC_SHIFT);   // Y coordinate
+          samples[4] = ((pos + refSizeX + CCCM_LOC_OFFSET) << CCCM_LOC_SHIFT);   // X coordinate
+          samples[5] = cccmModel[0].nonlinear(*src + lumaOffset);
+          samples[6] = cccmModel[0].bias();
+          if (*src + lumaOffset <= modelThr)
+          {
+            predChroma = cccmModel[0].convolve(samples) + chromaOffset[0];
+          }
+          else
+          {
+            predChroma = cccmModel[1].convolve(samples) + chromaOffset[1];
+          }
+          predChroma = ClipPel<Pel>(predChroma, clpRng);
+          sad += abs(curChroma0[pos] - predChroma);
+        }
+
+      }
+      else
+      {
+        THROW("Invalid type");
+      }
+#endif
+    }
+    else
+#endif
+    {
+      if (type & CCP_TYPE_CCCM)
+      {
+        for (int pos = 0; pos < cWidth; pos++, src++)
+        {
+          samples[0] = *src + lumaOffset;                 // C
+          samples[1] = *(src - srcStride) + lumaOffset;   // N
+          samples[2] = *(src + srcStride) + lumaOffset;   // S
+          samples[3] = *(src - 1) + lumaOffset;           // W
+          samples[4] = *(src + 1) + lumaOffset;           // E
+          samples[5] = cccmModel[0].nonlinear(*src + lumaOffset);
+          samples[6] = cccmModel[0].bias();
+          predChroma = cccmModel[0].convolve(samples) + chromaOffset[0];
+          predChroma = ClipPel<Pel>(predChroma, clpRng);
+          sad += abs(curChroma0[pos] - predChroma);
+        }
+      }
+#if JVET_AC0054_GLCCCM
+      else if (type & CCP_TYPE_GLCCCM)
+      {
+        for (int pos = 0; pos < cWidth; pos++, src++)
+        {
+          samples[0] = *src + lumaOffset;   // C
+          samples[1] = (2 * (*(src - srcStride)) + (*(src - 1 - srcStride)) + (*(src - srcStride + 1)))
+                     - (2 * (*(src + srcStride)) + (*(src - 1 + srcStride)) + (*(src + srcStride + 1)));   // Vertical gradient
+          samples[2] = (2 * (*(src - 1)) + (*(src - 1 - srcStride)) + (*(src - 1 + srcStride)))
+                     - (2 * (*(src + 1)) + (*(src + 1 - srcStride)) + (*(src + 1 + srcStride)));   // Horizontal gradient
+          samples[3] = (( -1 + refSizeY + CCCM_LOC_OFFSET) << CCCM_LOC_SHIFT);            // Y coordinate
+          samples[4] = ((pos + refSizeX + CCCM_LOC_OFFSET) << CCCM_LOC_SHIFT);            // X coordinate
+          samples[5] = cccmModel[0].nonlinear(*src + lumaOffset);
+          samples[6] = cccmModel[0].bias();
+          predChroma = cccmModel[0].convolve(samples) + chromaOffset[0];
+          predChroma = ClipPel<Pel>(predChroma, clpRng);
+          sad += abs(curChroma0[pos] - predChroma);
+        }
+      }
+      else
+      {
+        THROW("Invalid type");
+      }
+#endif
+    }
+  }
+
+  if (leftAvailable)
+  {
+    curChroma0 = curChromaBuf - 1;
+    const Pel *src = srcColor0 - 1;
+#if MMLM
+    if (type & CCP_TYPE_MMLM)
+    {
+      if (type & CCP_TYPE_CCCM)
+      {
+        for (int pos = 0; pos < cHeight; pos++, src += srcStride)
+        {
+          samples[0] = *src + lumaOffset;                 // C
+          samples[1] = *(src - srcStride) + lumaOffset;   // N
+          samples[2] = *(src + srcStride) + lumaOffset;   // S
+          samples[3] = *(src - 1) + lumaOffset;           // W
+          samples[4] = *(src + 1) + lumaOffset;           // E
+          samples[5] = cccmModel[0].nonlinear(*src + lumaOffset);
+          samples[6] = cccmModel[0].bias();
+          if (*src + lumaOffset <= modelThr)
+          {
+            predChroma = cccmModel[0].convolve(samples) + chromaOffset[0];
+          }
+          else
+          {
+            predChroma = cccmModel[1].convolve(samples) + chromaOffset[1];
+          }
+          predChroma = ClipPel<Pel>(predChroma, clpRng);
+          sad += abs(curChroma0[pos * curStride] - predChroma);
+        }
+      }
+#if JVET_AC0054_GLCCCM
+      else if (type & CCP_TYPE_GLCCCM)
+      {
+        for (int pos = 0; pos < cHeight; pos++, src += srcStride)
+        {
+          samples[0] = *src + lumaOffset;   // C
+          samples[1] = (2 * (*(src - srcStride)) + (*(src - 1 - srcStride)) + (*(src - srcStride + 1)))
+                     - (2 * (*(src + srcStride)) + (*(src - 1 + srcStride)) + (*(src + srcStride + 1)));   // Vertical gradient
+          samples[2] = (2 * (*(src - 1)) + (*(src - 1 - srcStride)) + (*(src - 1 + srcStride)))
+                     - (2 * (*(src + 1)) + (*(src + 1 - srcStride)) + (*(src + 1 + srcStride)));   // Horizontal gradient
+          samples[3] = ((pos + refSizeY + CCCM_LOC_OFFSET) << CCCM_LOC_SHIFT);            // Y coordinate
+          samples[4] = (( -1 + refSizeX + CCCM_LOC_OFFSET) << CCCM_LOC_SHIFT);            // X coordinate
+          samples[5] = cccmModel[0].nonlinear(*src + lumaOffset);
+          samples[6] = cccmModel[0].bias();
+          if (*src + lumaOffset <= modelThr)
+          {
+            predChroma = cccmModel[0].convolve(samples) + chromaOffset[0];
+          }
+          else
+          {
+            predChroma = cccmModel[1].convolve(samples) + chromaOffset[1];
+          }
+          predChroma = ClipPel<Pel>(predChroma, clpRng);
+          sad += abs(curChroma0[pos * curStride] - predChroma);
+        }
+      }
+      else
+      {
+        THROW("Invalid type");
+      }
+#endif
+    }
+    else
+#endif
+    {
+      if (type & CCP_TYPE_CCCM)
+      {
+        for (int pos = 0; pos < cHeight; pos++, src += srcStride)
+        {
+          samples[0] = *src + lumaOffset;                 // C
+          samples[1] = *(src - srcStride) + lumaOffset;   // N
+          samples[2] = *(src + srcStride) + lumaOffset;   // S
+          samples[3] = *(src - 1) + lumaOffset;           // W
+          samples[4] = *(src + 1) + lumaOffset;           // E
+          samples[5] = cccmModel[0].nonlinear(*src + lumaOffset);
+          samples[6] = cccmModel[0].bias();
+          predChroma = cccmModel[0].convolve(samples) + chromaOffset[0];
+          predChroma = ClipPel<Pel>(predChroma, clpRng);
+          sad += abs(curChroma0[pos * curStride] - predChroma);
+        }
+      }
+#if JVET_AC0054_GLCCCM
+      else if (type & CCP_TYPE_GLCCCM)
+      {
+        for (int pos = 0; pos < cHeight; pos++, src += srcStride)
+        {
+          samples[0] = *src + lumaOffset;   // C
+          samples[1] = (2 * (*(src - srcStride)) + (*(src - 1 - srcStride)) + (*(src - srcStride + 1)))
+                     - (2 * (*(src + srcStride)) + (*(src - 1 + srcStride)) + (*(src + srcStride + 1)));   // Vertical gradient
+          samples[2] = (2 * (*(src - 1)) + (*(src - 1 - srcStride)) + (*(src - 1 + srcStride)))
+                     - (2 * (*(src + 1)) + (*(src + 1 - srcStride)) + (*(src + 1 + srcStride)));   // Horizontal gradient
+          samples[3] = ((pos + refSizeY + CCCM_LOC_OFFSET) << CCCM_LOC_SHIFT);            // Y coordinate
+          samples[4] = (( -1 + refSizeX + CCCM_LOC_OFFSET) << CCCM_LOC_SHIFT);            // X coordinate
+          samples[5] = cccmModel[0].nonlinear(*src + lumaOffset);
+          samples[6] = cccmModel[0].bias();
+          predChroma = cccmModel[0].convolve(samples) + chromaOffset[0];
+          predChroma = ClipPel<Pel>(predChroma, clpRng);
+          sad += abs(curChroma0[pos * curStride] - predChroma);
+        }
+      }
+      else
+      {
+        THROW("Invalid type");
+      }
+#endif
+    }
+  }
+  return sad;
+}
+
+#if JVET_AD0202_CCCM_MDF
+int IntraPrediction::xGetCostMFCCCM1(const PredictionUnit& pu, const ComponentID compID, int modelNum,
+                                     const CompArea& chromaArea, CccmModel<CCCM_MULTI_PRED_FILTER_NUM_PARAMS> cccmModel[2],
+                                     int modelThr, int lumaOffset, int chromaOffset[], int type, int refSizeX, int refSizeY)
+{
+  CHECK(compID != chromaArea.compID, "Invalid component ID");
+
+  const ClpRng& clpRng(pu.cu->cs->slice->clpRng(compID));
+  static Pel samples[CCCM_MULTI_PRED_FILTER_NUM_PARAMS];
+
+  CPelBuf refLumaBlk1, refLumaBlk2, refLumaBlk3;
+  CPelBuf refLumaBlk = xCccmGetLumaPuBuf(pu, 0, 3, &refLumaBlk1, &refLumaBlk3, &refLumaBlk2);
+
+  const SizeType cWidth = chromaArea.width;
+  const SizeType cHeight = chromaArea.height;
+
+  CodingStructure& cs = *(pu.cs);
+  const CodingUnit& cu = *(pu.cu);
+
+  const bool aboveAvailable = cu.cs->getCU(cu.blocks[compID].pos().offset(0, -1), toChannelType(compID)) ? true : false;
+  const bool leftAvailable = cu.cs->getCU(cu.blocks[compID].pos().offset(-1, 0), toChannelType(compID)) ? true : false;
+
+  const Pel* curChroma0;
+
+  PelBuf chromaReco = cs.picture->getRecoBuf(chromaArea);
+
+  Pel* curChromaBuf = chromaReco.buf;
+  const int curStride = chromaReco.stride;
+
+  int sad = 0;
+
+  Pel predChroma;
+
+  if (aboveAvailable)
+  {
+    curChroma0 = curChromaBuf - curStride;
+#if MMLM
+    if (type & CCP_TYPE_MMLM)
+    {
+      for (int pos = 0; pos < cWidth; pos++)
+      {
+        samples[0] = refLumaBlk.at(pos, -1) + lumaOffset; // C
+        samples[1] = refLumaBlk1.at(pos, -1) + lumaOffset; // W
+        samples[2] = refLumaBlk2.at(pos, -1) + lumaOffset; // E
+        samples[3] = refLumaBlk3.at(pos, -1) + lumaOffset;
+        samples[4] = cccmModel[0].nonlinear(refLumaBlk.at(pos, -1) + lumaOffset);
+        samples[5] = cccmModel[0].nonlinear(refLumaBlk1.at(pos, -1) + lumaOffset);
+        samples[6] = cccmModel[0].nonlinear(refLumaBlk2.at(pos, -1) + lumaOffset);
+        samples[7] = ((-1 + refSizeY + CCCM_LOC_OFFSET) << CCCM_LOC_SHIFT); // Y coordinate
+        samples[8] = ((pos + refSizeX + CCCM_LOC_OFFSET) << CCCM_LOC_SHIFT); // X coordinate
+        samples[9] = cccmModel[0].bias();
+        if ((refLumaBlk.at(pos, -1) + lumaOffset) <= modelThr)
+        {
+          predChroma = cccmModel[0].convolve(samples) + chromaOffset[0];
+        }
+        else
+        {
+          predChroma = cccmModel[1].convolve(samples) + chromaOffset[1];
+        }
+        predChroma = ClipPel<Pel>(predChroma, clpRng);
+        sad += abs(curChroma0[pos] - predChroma);
+      }
+    }
+    else
+#endif
+    {
+      for (int pos = 0; pos < cWidth; pos++)
+      {
+        samples[0] = refLumaBlk.at(pos, -1) + lumaOffset; // C
+        samples[1] = refLumaBlk1.at(pos, -1) + lumaOffset; // W
+        samples[2] = refLumaBlk2.at(pos, -1) + lumaOffset; // E
+        samples[3] = refLumaBlk3.at(pos, -1) + lumaOffset;
+        samples[4] = cccmModel[0].nonlinear(refLumaBlk.at(pos, -1) + lumaOffset);
+        samples[5] = cccmModel[0].nonlinear(refLumaBlk1.at(pos, -1) + lumaOffset);
+        samples[6] = cccmModel[0].nonlinear(refLumaBlk2.at(pos, -1) + lumaOffset);
+        samples[7] = ((-1 + refSizeY + CCCM_LOC_OFFSET) << CCCM_LOC_SHIFT); // Y coordinate
+        samples[8] = ((pos + refSizeX + CCCM_LOC_OFFSET) << CCCM_LOC_SHIFT); // X coordinate
+        samples[9] = cccmModel[0].bias();
+        predChroma = cccmModel[0].convolve(samples) + chromaOffset[0];
+        predChroma = ClipPel<Pel>(predChroma, clpRng);
+        sad += abs(curChroma0[pos] - predChroma);
+      }
+    }
+  }
+
+  if (leftAvailable)
+  {
+    curChroma0 = curChromaBuf - 1;
+#if MMLM
+    if (type & CCP_TYPE_MMLM)
+    {
+      for (int pos = 0; pos < cHeight; pos++)
+      {
+        samples[0] = refLumaBlk.at(-1, pos) + lumaOffset; // C
+        samples[1] = refLumaBlk1.at(-1, pos) + lumaOffset; // W
+        samples[2] = refLumaBlk2.at(-1, pos) + lumaOffset; // E
+        samples[3] = refLumaBlk3.at(-1, pos) + lumaOffset;
+        samples[4] = cccmModel[0].nonlinear(refLumaBlk.at(-1, pos) + lumaOffset);
+        samples[5] = cccmModel[0].nonlinear(refLumaBlk1.at(-1, pos) + lumaOffset);
+        samples[6] = cccmModel[0].nonlinear(refLumaBlk2.at(-1, pos) + lumaOffset);
+        samples[7] = ((pos + refSizeY + CCCM_LOC_OFFSET) << CCCM_LOC_SHIFT); // Y coordinate
+        samples[8] = ((-1 + refSizeX + CCCM_LOC_OFFSET) << CCCM_LOC_SHIFT); // X coordinate
+        samples[9] = cccmModel[0].bias();
+        if ((refLumaBlk.at(-1, pos) + lumaOffset) <= modelThr)
+        {
+          predChroma = cccmModel[0].convolve(samples) + chromaOffset[0];
+        }
+        else
+        {
+          predChroma = cccmModel[1].convolve(samples) + chromaOffset[1];
+        }
+        predChroma = ClipPel<Pel>(predChroma, clpRng);
+        sad += abs(curChroma0[pos * curStride] - predChroma);
+      }
+    }
+    else
+#endif
+    {
+      for (int pos = 0; pos < cHeight; pos++)
+      {
+        samples[0] = refLumaBlk.at(-1, pos) + lumaOffset; // C
+        samples[1] = refLumaBlk1.at(-1, pos) + lumaOffset; // W
+        samples[2] = refLumaBlk2.at(-1, pos) + lumaOffset; // E
+        samples[3] = refLumaBlk3.at(-1, pos) + lumaOffset;
+        samples[4] = cccmModel[0].nonlinear(refLumaBlk.at(-1, pos) + lumaOffset);
+        samples[5] = cccmModel[0].nonlinear(refLumaBlk1.at(-1, pos) + lumaOffset);
+        samples[6] = cccmModel[0].nonlinear(refLumaBlk2.at(-1, pos) + lumaOffset);
+        samples[7] = ((pos + refSizeY + CCCM_LOC_OFFSET) << CCCM_LOC_SHIFT); // Y coordinate
+        samples[8] = ((-1 + refSizeX + CCCM_LOC_OFFSET) << CCCM_LOC_SHIFT); // X coordinate
+        samples[9] = cccmModel[0].bias();
+        predChroma = cccmModel[0].convolve(samples) + chromaOffset[0];
+        predChroma = ClipPel<Pel>(predChroma, clpRng);
+        sad += abs(curChroma0[pos * curStride] - predChroma);
+      }
+    }
+  }
+  return sad;
+}
+
+int IntraPrediction::xGetCostMFCCCM23(const PredictionUnit& pu, const ComponentID compID, int modelNum,
+                                      const CompArea& chromaArea, CccmModel<CCCM_MULTI_PRED_FILTER_NUM_PARAMS2> cccmModel[2],
+                                      int modelThr, int lumaOffset, int chromaOffset[], int type, int refSizeX, int refSizeY)
+{
+  CHECK(compID != chromaArea.compID, "Invalid component ID");
+
+  const ClpRng& clpRng(pu.cu->cs->slice->clpRng(compID));
+  static Pel samples[CCCM_MULTI_PRED_FILTER_NUM_PARAMS2];
+
+  CPelBuf refLumaBlk1, refLumaBlk3;
+  CPelBuf refLumaBlk = xCccmGetLumaPuBuf(pu, 0, 2, &refLumaBlk1, &refLumaBlk3);
+
+  const SizeType cWidth = chromaArea.width;
+  const SizeType cHeight = chromaArea.height;
+
+  CodingStructure& cs = *(pu.cs);
+  const CodingUnit& cu = *(pu.cu);
+
+  const bool aboveAvailable = cu.cs->getCU(cu.blocks[compID].pos().offset(0, -1), toChannelType(compID)) ? true : false;
+  const bool leftAvailable = cu.cs->getCU(cu.blocks[compID].pos().offset(-1, 0), toChannelType(compID)) ? true : false;
+
+  const Pel* curChroma0;
+
+  PelBuf chromaReco = cs.picture->getRecoBuf(chromaArea);
+  Pel* curChromaBuf = chromaReco.buf;
+  const int curStride = chromaReco.stride;
+
+  int sad = 0;
+
+  Pel predChroma;
+
+  if (aboveAvailable)
+  {
+    curChroma0 = curChromaBuf - curStride;
+#if MMLM
+    if (type & CCP_TYPE_MMLM)
+    {
+      if (pu.cccmMultiFilterIdx == 2)
+      {
+        for (int pos = 0; pos < cWidth; pos++)
+        {
+          samples[0] = refLumaBlk.at(pos,     -1) + lumaOffset; // C
+          samples[1] = refLumaBlk.at(pos - 1, -1) + lumaOffset; // W
+          samples[2] = refLumaBlk.at(pos + 1, -1) + lumaOffset; // E
+          samples[3] = refLumaBlk1.at(pos,     -1) + lumaOffset; // C
+          samples[4] = refLumaBlk1.at(pos - 1, -1) + lumaOffset; // W
+          samples[5] = refLumaBlk1.at(pos + 1, -1) + lumaOffset; // E
+          samples[6] = cccmModel[0].nonlinear(refLumaBlk.at(pos,     -1) + lumaOffset);
+          samples[7] = cccmModel[0].nonlinear(refLumaBlk.at(pos - 1, -1) + lumaOffset);
+          samples[8] = cccmModel[0].nonlinear(refLumaBlk.at(pos + 1, -1) + lumaOffset);
+          samples[9] = ((pos + refSizeX + CCCM_LOC_OFFSET) << CCCM_LOC_SHIFT); // X coordinate
+          samples[10] = cccmModel[0].bias();
+          if ((refLumaBlk.at(pos, -1) + lumaOffset) <= modelThr)
+          {
+            predChroma = cccmModel[0].convolve(samples) + chromaOffset[0];
+          }
+          else
+          {
+            predChroma = cccmModel[1].convolve(samples) + chromaOffset[1];
+          }
+          predChroma = ClipPel<Pel>(predChroma, clpRng);
+          sad += abs(curChroma0[pos] - predChroma);
+        }
+      }
+      else if(pu.cccmMultiFilterIdx == 3)
+      {
+        for (int pos = 0; pos < cWidth; pos++)
+        {
+          samples[0] = refLumaBlk.at(pos,     -1) + lumaOffset; // C
+          samples[1] = refLumaBlk.at(pos + 1, -2) + lumaOffset; // EN
+          samples[2] = refLumaBlk.at(pos - 1,  0) + lumaOffset; // WS
+          samples[3] = refLumaBlk3.at(pos,     -1) + lumaOffset; // C
+          samples[4] = refLumaBlk3.at(pos + 1, -2) + lumaOffset; // EN
+          samples[5] = refLumaBlk3.at(pos - 1,  0) + lumaOffset; // WS
+          samples[6] = cccmModel[0].nonlinear(refLumaBlk.at(pos,     -1) + lumaOffset);
+          samples[7] = cccmModel[0].nonlinear(refLumaBlk.at(pos + 1, -2) + lumaOffset);
+          samples[8] = cccmModel[0].nonlinear(refLumaBlk.at(pos - 1,  0) + lumaOffset);
+          samples[9] = ((-1 + refSizeY + CCCM_LOC_OFFSET) << CCCM_LOC_SHIFT); // Y coordinate
+          samples[10] = cccmModel[0].bias();
+          if ((refLumaBlk.at(pos, -1) + lumaOffset) <= modelThr)
+          {
+            predChroma = cccmModel[0].convolve(samples) + chromaOffset[0];
+          }
+          else
+          {
+            predChroma = cccmModel[1].convolve(samples) + chromaOffset[1];
+          }
+          predChroma = ClipPel<Pel>(predChroma, clpRng);
+          sad += abs(curChroma0[pos] - predChroma);
+        }
+      }
+    }
+    else
+#endif
+    {
+      if (pu.cccmMultiFilterIdx == 2)
+      {
+        for (int pos = 0; pos < cWidth; pos++)
+        {
+          samples[0] = refLumaBlk.at(pos,     -1) + lumaOffset; // C
+          samples[1] = refLumaBlk.at(pos - 1, -1) + lumaOffset; // W
+          samples[2] = refLumaBlk.at(pos + 1, -1) + lumaOffset; // E
+          samples[3] = refLumaBlk1.at(pos,     -1) + lumaOffset; // C
+          samples[4] = refLumaBlk1.at(pos - 1, -1) + lumaOffset; // W
+          samples[5] = refLumaBlk1.at(pos + 1, -1) + lumaOffset; // E
+          samples[6] = cccmModel[0].nonlinear(refLumaBlk.at(pos,     -1) + lumaOffset);
+          samples[7] = cccmModel[0].nonlinear(refLumaBlk.at(pos - 1, -1) + lumaOffset);
+          samples[8] = cccmModel[0].nonlinear(refLumaBlk.at(pos + 1, -1) + lumaOffset);
+          samples[9] = ((pos + refSizeX + CCCM_LOC_OFFSET) << CCCM_LOC_SHIFT); // X coordinate
+          samples[10] = cccmModel[0].bias();
+          predChroma = cccmModel[0].convolve(samples) + chromaOffset[0];
+          predChroma = ClipPel<Pel>(predChroma, clpRng);
+          sad += abs(curChroma0[pos] - predChroma);
+        }
+      }
+      else // if (pu.cccmMultiFilterIdx == 3)
+      {
+        for (int pos = 0; pos < cWidth; pos++)
+        {
+          samples[0] = refLumaBlk.at(pos,     -1) + lumaOffset; // C
+          samples[1] = refLumaBlk.at(pos + 1, -2) + lumaOffset; // EN
+          samples[2] = refLumaBlk.at(pos - 1,  0) + lumaOffset; // WS
+          samples[3] = refLumaBlk3.at(pos,     -1) + lumaOffset; // C
+          samples[4] = refLumaBlk3.at(pos + 1, -2) + lumaOffset; // EN
+          samples[5] = refLumaBlk3.at(pos - 1,  0) + lumaOffset; // WS
+          samples[6] = cccmModel[0].nonlinear(refLumaBlk.at(pos,     -1) + lumaOffset);
+          samples[7] = cccmModel[0].nonlinear(refLumaBlk.at(pos + 1, -2) + lumaOffset);
+          samples[8] = cccmModel[0].nonlinear(refLumaBlk.at(pos - 1,  0) + lumaOffset);
+          samples[9] = ((-1 + refSizeY + CCCM_LOC_OFFSET) << CCCM_LOC_SHIFT); // Y coordinate
+          samples[10] = cccmModel[0].bias();
+          predChroma = cccmModel[0].convolve(samples) + chromaOffset[0];
+          predChroma = ClipPel<Pel>(predChroma, clpRng);
+          sad += abs(curChroma0[pos] - predChroma);
+        }
+      }
+    }
+  }
+
+  if (leftAvailable)
+  {
+    curChroma0 = curChromaBuf - 1;
+#if MMLM
+    if (type & CCP_TYPE_MMLM)
+    {
+      if (pu.cccmMultiFilterIdx == 2)
+      {
+        for (int pos = 0; pos < cHeight; pos++)
+        {
+          samples[0] = refLumaBlk.at(-1, pos) + lumaOffset; // C
+          samples[1] = refLumaBlk.at(-2, pos) + lumaOffset; // W
+          samples[2] = refLumaBlk.at( 0, pos) + lumaOffset; // E
+          samples[3] = refLumaBlk1.at(-1, pos) + lumaOffset; // C
+          samples[4] = refLumaBlk1.at(-2, pos) + lumaOffset; // W
+          samples[5] = refLumaBlk1.at( 0, pos) + lumaOffset; // E
+          samples[6] = cccmModel[0].nonlinear(refLumaBlk.at(-1, pos) + lumaOffset);
+          samples[7] = cccmModel[0].nonlinear(refLumaBlk.at(-2, pos) + lumaOffset);
+          samples[8] = cccmModel[0].nonlinear(refLumaBlk.at( 0, pos) + lumaOffset);
+          samples[9] = ((-1 + refSizeX + CCCM_LOC_OFFSET) << CCCM_LOC_SHIFT); // X coordinate
+          samples[10] = cccmModel[0].bias();
+          if ((refLumaBlk.at(-1, pos) + lumaOffset) <= modelThr)
+          {
+            predChroma = cccmModel[0].convolve(samples) + chromaOffset[0];
+          }
+          else
+          {
+            predChroma = cccmModel[1].convolve(samples) + chromaOffset[1];
+          }
+          predChroma = ClipPel<Pel>(predChroma, clpRng);
+          sad += abs(curChroma0[pos * curStride] - predChroma);
+        }
+      }
+      else // if (pu.cccmMultiFilterIdx == 3)
+      {
+        for (int pos = 0; pos < cHeight; pos++)
+        {
+          samples[0] = refLumaBlk.at(-1, pos) + lumaOffset;      // C
+          samples[1] = refLumaBlk.at( 0, pos - 1) + lumaOffset;  // EN
+          samples[2] = refLumaBlk.at(-2, pos + 1) + lumaOffset;  // WS
+          samples[3] = refLumaBlk3.at(-1, pos) + lumaOffset;     // C
+          samples[4] = refLumaBlk3.at( 0, pos - 1) + lumaOffset; // EN
+          samples[5] = refLumaBlk3.at(-2, pos + 1) + lumaOffset; // WS
+          samples[6] = cccmModel[0].nonlinear(refLumaBlk.at(-1, pos) + lumaOffset);
+          samples[7] = cccmModel[0].nonlinear(refLumaBlk.at( 0, pos - 1) + lumaOffset);
+          samples[8] = cccmModel[0].nonlinear(refLumaBlk.at(-2, pos + 1) + lumaOffset);
+          samples[9] = ((pos + refSizeY + CCCM_LOC_OFFSET) << CCCM_LOC_SHIFT); // Y coordinate
+          samples[10] = cccmModel[0].bias();
+          if ((refLumaBlk.at(-1, pos) + lumaOffset) <= modelThr)
+          {
+            predChroma = cccmModel[0].convolve(samples) + chromaOffset[0];
+          }
+          else
+          {
+            predChroma = cccmModel[1].convolve(samples) + chromaOffset[1];
+          }
+          predChroma = ClipPel<Pel>(predChroma, clpRng);
+          sad += abs(curChroma0[pos * curStride] - predChroma);
+        }
+      }
+    }
+    else
+#endif
+    {
+      if (pu.cccmMultiFilterIdx == 2)
+      {
+        for (int pos = 0; pos < cHeight; pos++)
+        {
+          samples[0] = refLumaBlk.at(-1, pos) + lumaOffset;  // C
+          samples[1] = refLumaBlk.at(-2, pos) + lumaOffset;  // W
+          samples[2] = refLumaBlk.at( 0, pos) + lumaOffset;  // E
+          samples[3] = refLumaBlk1.at(-1, pos) + lumaOffset; // C
+          samples[4] = refLumaBlk1.at(-2, pos) + lumaOffset; // W
+          samples[5] = refLumaBlk1.at( 0, pos) + lumaOffset; // E
+          samples[6] = cccmModel[0].nonlinear(refLumaBlk.at(-1, pos)) + lumaOffset;
+          samples[7] = cccmModel[0].nonlinear(refLumaBlk.at(-2, pos)) + lumaOffset;
+          samples[8] = cccmModel[0].nonlinear(refLumaBlk.at( 0, pos)) + lumaOffset;
+          samples[9] = ((-1 + refSizeX + CCCM_LOC_OFFSET) << CCCM_LOC_SHIFT); // X coordinate
+          samples[10] = cccmModel[0].bias();
+          predChroma = cccmModel[0].convolve(samples) + chromaOffset[0];
+          predChroma = ClipPel<Pel>(predChroma, clpRng);
+          sad += abs(curChroma0[pos * curStride] - predChroma);
+        }
+      }
+      else // if (pu.cccmMultiFilterIdx == 3)
+      {
+        for (int pos = 0; pos < cHeight; pos++)
+        {
+          samples[0] = refLumaBlk.at(-1, pos) + lumaOffset;      // C
+          samples[1] = refLumaBlk.at( 0, pos - 1) + lumaOffset;  // EN
+          samples[2] = refLumaBlk.at(-2, pos + 1) + lumaOffset;  // WS
+          samples[3] = refLumaBlk3.at(-1, pos) + lumaOffset;     // C
+          samples[4] = refLumaBlk3.at( 0, pos - 1) + lumaOffset; // EN
+          samples[5] = refLumaBlk3.at(-2, pos + 1) + lumaOffset; // WS
+          samples[6] = cccmModel[0].nonlinear(refLumaBlk.at(-1, pos) + lumaOffset);
+          samples[7] = cccmModel[0].nonlinear(refLumaBlk.at( 0, pos - 1) + lumaOffset);
+          samples[8] = cccmModel[0].nonlinear(refLumaBlk.at(-2, pos + 1) + lumaOffset);
+          samples[9] = ((pos + refSizeY + CCCM_LOC_OFFSET) << CCCM_LOC_SHIFT); // Y coordinate
+          samples[10] = cccmModel[0].bias();
+          predChroma = cccmModel[0].convolve(samples) + chromaOffset[0];
+          predChroma = ClipPel<Pel>(predChroma, clpRng);
+          sad += abs(curChroma0[pos * curStride] - predChroma);
+        }
+      }
+    }
+  }
+  return sad;
+}
+#endif
+
+void IntraPrediction::xGetUpdatedOffsetGLM(const PredictionUnit &pu, const ComponentID compID,
+                                           const CompArea &chromaArea, CccmModel<GLM_NUM_PARAMS> &glmModel, int glmIdc,
+                                           int lumaOffset, int &chromaOffset)
+{
+  CHECK(compID != chromaArea.compID, "Invalid component ID");
+
+  static Pel samples[GLM_NUM_PARAMS];
+
+  CPelBuf refLumaBlk = xGlmGetGradPuBuf(pu, chromaArea, 0);
+  CPelBuf refGradBlk = xGlmGetGradPuBuf(pu, chromaArea, glmIdc);
+
+  const SizeType cWidth  = chromaArea.width;
+  const SizeType cHeight = chromaArea.height;
+
+  CodingStructure  &cs = *(pu.cs);
+  const CodingUnit &cu = *(pu.cu);
+
+  const bool aboveAvailable = cu.cs->getCU(cu.blocks[compID].pos().offset(0, -1), toChannelType(compID)) ? true : false;
+  const bool leftAvailable  = cu.cs->getCU(cu.blocks[compID].pos().offset(-1, 0), toChannelType(compID)) ? true : false;
+
+  const Pel *srcColor0, *srcColor1, *curChroma0;
+
+  srcColor0      = refGradBlk.bufAt(0, 0);
+  int srcStride0 = refGradBlk.stride;
+  srcColor1      = refLumaBlk.bufAt(0, 0);
+  int srcStride1 = refLumaBlk.stride;
+
+  PelBuf chromaReco = cs.picture->getRecoBuf(chromaArea);
+
+  Pel      *curChromaBuf = chromaReco.buf;
+  const int curStride    = chromaReco.stride;
+
+  int totalOffset = 0;
+  int count       = 0;
+
+  if (aboveAvailable)
+  {
+    curChroma0      = curChromaBuf - curStride;
+    const Pel *src0 = srcColor0 - srcStride0;
+    const Pel *src1 = srcColor1 - srcStride1;
+
+    {
+      for (int pos = 0; pos < cWidth; pos++, src0++, src1++)
+      {
+        samples[0] = *src0;                // luma gradient
+        samples[1] = *src1 + lumaOffset;   // luma value
+        samples[2] = glmModel.bias();
+
+        Pel predChroma = glmModel.convolve(samples);
+        totalOffset += curChroma0[pos] - predChroma;
+        count++;
+      }
+    }
+  }
+
+  if (leftAvailable)
+  {
+    curChroma0 = curChromaBuf - 1;
+
+    const Pel *src0 = srcColor0 - 1;
+    const Pel *src1 = srcColor1 - 1;
+
+    for (int pos = 0; pos < cHeight; pos++, src0 += srcStride0, src1 += srcStride1)
+    {
+      samples[0] = *src0;                // luma gradient
+      samples[1] = *src1 + lumaOffset;   // luma value
+      samples[2] = glmModel.bias();
+
+      Pel predChroma = glmModel.convolve(samples);
+      totalOffset += curChroma0[pos * curStride] - predChroma;
+      count++;
+    }
+  }
+
+  chromaOffset = 0;
+
+  if (count)
+  {
+    chromaOffset = PU::getMeanValue(totalOffset, count);   // totalOffset / count;
+  }
+}
+int IntraPrediction::xGetCostGLM(const PredictionUnit &pu, const ComponentID compID, const CompArea &chromaArea,
+                                 CccmModel<GLM_NUM_PARAMS> &glmModel, int glmIdc, int lumaOffset, int &chromaOffset)
+{
+  const ClpRng &clpRng(pu.cu->cs->slice->clpRng(compID));
+  CHECK(compID != chromaArea.compID, "Invalid component ID");
+
+  static Pel samples[GLM_NUM_PARAMS];
+
+  CPelBuf refLumaBlk = xGlmGetGradPuBuf(pu, chromaArea, 0);
+  CPelBuf refGradBlk = xGlmGetGradPuBuf(pu, chromaArea, glmIdc);
+
+  const SizeType cWidth  = chromaArea.width;
+  const SizeType cHeight = chromaArea.height;
+
+  CodingStructure  &cs = *(pu.cs);
+  const CodingUnit &cu = *(pu.cu);
+
+  const bool aboveAvailable = cu.cs->getCU(cu.blocks[compID].pos().offset(0, -1), toChannelType(compID)) ? true : false;
+  const bool leftAvailable  = cu.cs->getCU(cu.blocks[compID].pos().offset(-1, 0), toChannelType(compID)) ? true : false;
+
+  const Pel *srcColor0, *srcColor1, *curChroma0;
+
+  srcColor0      = refGradBlk.bufAt(0, 0);
+  int srcStride0 = refGradBlk.stride;
+  srcColor1      = refLumaBlk.bufAt(0, 0);
+  int srcStride1 = refLumaBlk.stride;
+
+  PelBuf chromaReco = cs.picture->getRecoBuf(chromaArea);
+
+  Pel      *curChromaBuf = chromaReco.buf;
+  const int curStride    = chromaReco.stride;
+
+  int sad = 0;
+
+  if (aboveAvailable)
+  {
+    curChroma0      = curChromaBuf - curStride;
+    const Pel *src0 = srcColor0 - srcStride0;
+    const Pel *src1 = srcColor1 - srcStride1;
+
+    {
+      for (int pos = 0; pos < cWidth; pos++, src0++, src1++)
+      {
+        samples[0] = *src0;                // luma gradient
+        samples[1] = *src1 + lumaOffset;   // luma value
+        samples[2] = glmModel.bias();
+
+        Pel predChroma = glmModel.convolve(samples) + chromaOffset;
+        predChroma     = ClipPel<Pel>(predChroma, clpRng);
+        sad += abs(curChroma0[pos] - predChroma);
+      }
+    }
+  }
+
+  if (leftAvailable)
+  {
+    curChroma0 = curChromaBuf - 1;
+
+    const Pel *src0 = srcColor0 - 1;
+    const Pel *src1 = srcColor1 - 1;
+
+    for (int pos = 0; pos < cHeight; pos++, src0 += srcStride0, src1 += srcStride1)
+    {
+      samples[0] = *src0;                // luma gradient
+      samples[1] = *src1 + lumaOffset;   // luma value
+      samples[2] = glmModel.bias();
+
+      Pel predChroma = glmModel.convolve(samples) + chromaOffset;
+      predChroma     = ClipPel<Pel>(predChroma, clpRng);
+      sad += abs(curChroma0[pos * curStride] - predChroma);
+    }
+  }
+
+  return sad;
+}
+void IntraPrediction::xCclmApplyModel(const PredictionUnit &pu, const ComponentID compId,
+                                      CccmModel<CCCM_NUM_PARAMS> &cccmModel, int modelId, int modelThr,
+                                      PelBuf &piPred) const
+{
+  const ClpRng &clpRng(pu.cu->cs->slice->clpRng(compId));
+  static Pel    samples[CCCM_NUM_PARAMS];
+
+  CPelBuf refLumaBlk = xCccmGetLumaPuBuf(pu);
+
+  for (int y = 0; y < refLumaBlk.height; y++)
+  {
+    for (int x = 0; x < refLumaBlk.width; x++)
+    {
+      if (modelId == 1 && refLumaBlk.at(x, y) > modelThr)   // Model 1: Include only samples below or equal to the threshold
+      {
+        continue;
+      }
+      if (modelId == 2 && refLumaBlk.at(x, y) <= modelThr)   // Model 2: Include only samples above the threshold
+      {
+        continue;
+      }
+      samples[0] = refLumaBlk.at(x, y);   // C
+      samples[1] = cccmModel.bias();
+
+      piPred.at(x, y) = ClipPel<Pel>(Pel((cccmModel.params[0] * samples[0] + cccmModel.params[1] * samples[1] + CCCM_DECIM_ROUND) >> CCCM_DECIM_BITS), clpRng);
+    }
+  }
+}
+
+void IntraPrediction::reorderCCPCandidates(const PredictionUnit &pu, CCPModelCandidate candList[], int reorderlistSize)
+{
+  int candCost[MAX_CCP_CAND_LIST_SIZE];
+  for (int i = 0; i < reorderlistSize; i++)
+  {
+    candCost[i] = xGetOneCCPCandCost(pu, candList[i]);
+  }
+
+  //Inserting sorting
+  for (int i = 1; i < reorderlistSize; i++)
+  {
+    for (int j = 0; j < i; j++)
+    {
+      if (candCost[i] < candCost[j])
+      {
+        CCPModelCandidate tmpCand = candList[i];
+        int tmpCost = candCost[i];
+        for (int k = i; k > j; k--)
+        {
+          candList[k] = candList[k - 1];
+          candCost[k] = candCost[k - 1];
+        }
+        candList[j] = tmpCand;
+        candCost[j] = tmpCost;
+        break;
+      }
+    }
+  }
+}
+
+int  IntraPrediction::xGetOneCCPCandCost(const PredictionUnit &pu, CCPModelCandidate &ccpCand)
+{
+  CompArea chromaArea = pu.Cb();
+  int cost = 0;
+  const int modelNum = (ccpCand.type & CCP_TYPE_MMLM) ? 2 : 1;
+  const int bitDepth = pu.cu->slice->getSPS()->getBitDepth(CHANNEL_TYPE_LUMA);
+  int offsetCb[2] = { 0, 0 };
+  int offsetCr[2] = { 0, 0 };
+
+  CHECK(ccpCand.type == CCP_TYPE_NONE, "CCP model with no type")
+  {
+    if (ccpCand.type & (CCP_TYPE_CCCM | CCP_TYPE_GLCCCM))
+    {
+#if JVET_AB0174_CCCM_DIV_FREE
+      int lumaOffset = m_cccmLumaOffset - ccpCand.lumaOffset;
+#else
+      int lumaOffset = 0;
+#endif
+      int refSizeX = ccpCand.corOffX;
+      int refSizeY = ccpCand.corOffY;
+
+      CccmModel<CCCM_NUM_PARAMS> cccmModelCb[2] = { CccmModel<CCCM_NUM_PARAMS>(bitDepth), CccmModel<CCCM_NUM_PARAMS>(bitDepth) };
+      CccmModel<CCCM_NUM_PARAMS> cccmModelCr[2] = { CccmModel<CCCM_NUM_PARAMS>(bitDepth), CccmModel<CCCM_NUM_PARAMS>(bitDepth) };
+
+      PU::ccpParamsToCccmModel(ccpCand, cccmModelCb, cccmModelCr);
+
+      cost += xGetCostCCCM(pu, COMPONENT_Cb, modelNum, pu.Cb(), cccmModelCb, ccpCand.yThres, lumaOffset, offsetCb, ccpCand.type, refSizeX, refSizeY);
+      cost += xGetCostCCCM(pu, COMPONENT_Cr, modelNum, pu.Cr(), cccmModelCr, ccpCand.yThres, lumaOffset, offsetCr, ccpCand.type, refSizeX, refSizeY);
+    }
+#if JVET_AD0202_CCCM_MDF
+    else if (ccpCand.type & (CCP_TYPE_MDFCCCM))
+    {
+      const_cast<PredictionUnit&>(pu).cccmMultiFilterIdx = ccpCand.cccmMultiFilterIdx;
+#if JVET_AB0174_CCCM_DIV_FREE
+      int lumaOffset = m_cccmLumaOffset - ccpCand.lumaOffset;
+#else
+      int lumaOffset = 0;
+#endif
+      int refSizeX = ccpCand.corOffX;
+      int refSizeY = ccpCand.corOffY;
+
+      if (ccpCand.cccmMultiFilterIdx == 1)
+      {
+        CccmModel<CCCM_MULTI_PRED_FILTER_NUM_PARAMS> cccmModelCb[2] = { CccmModel<CCCM_MULTI_PRED_FILTER_NUM_PARAMS>(bitDepth), CccmModel<CCCM_MULTI_PRED_FILTER_NUM_PARAMS>(bitDepth) };
+        CccmModel<CCCM_MULTI_PRED_FILTER_NUM_PARAMS> cccmModelCr[2] = { CccmModel<CCCM_MULTI_PRED_FILTER_NUM_PARAMS>(bitDepth), CccmModel<CCCM_MULTI_PRED_FILTER_NUM_PARAMS>(bitDepth) };
+
+        PU::ccpParamsToCccmModel(ccpCand, cccmModelCb, cccmModelCr);
+
+        cost += xGetCostMFCCCM1(pu, COMPONENT_Cb, modelNum, pu.Cb(), cccmModelCb, ccpCand.yThres, lumaOffset, offsetCb, ccpCand.type, refSizeX, refSizeY);
+        cost += xGetCostMFCCCM1(pu, COMPONENT_Cr, modelNum, pu.Cr(), cccmModelCr, ccpCand.yThres, lumaOffset, offsetCr, ccpCand.type, refSizeX, refSizeY);
+      }
+      else
+      {
+        CHECK(ccpCand.cccmMultiFilterIdx != 2 && ccpCand.cccmMultiFilterIdx != 3, "Unexpected cccmMultiFilterIdx");
+
+        CccmModel<CCCM_MULTI_PRED_FILTER_NUM_PARAMS2> cccmModelCb[2] = { CccmModel<CCCM_MULTI_PRED_FILTER_NUM_PARAMS2>(bitDepth), CccmModel<CCCM_MULTI_PRED_FILTER_NUM_PARAMS2>(bitDepth) };
+        CccmModel<CCCM_MULTI_PRED_FILTER_NUM_PARAMS2> cccmModelCr[2] = { CccmModel<CCCM_MULTI_PRED_FILTER_NUM_PARAMS2>(bitDepth), CccmModel<CCCM_MULTI_PRED_FILTER_NUM_PARAMS2>(bitDepth) };
+
+        PU::ccpParamsToCccmModel(ccpCand, cccmModelCb, cccmModelCr);
+
+        cost += xGetCostMFCCCM23(pu, COMPONENT_Cb, modelNum, pu.Cb(), cccmModelCb, ccpCand.yThres, lumaOffset, offsetCb, ccpCand.type, refSizeX, refSizeY);
+        cost += xGetCostMFCCCM23(pu, COMPONENT_Cr, modelNum, pu.Cr(), cccmModelCr, ccpCand.yThres, lumaOffset, offsetCr, ccpCand.type, refSizeX, refSizeY);
+      }
+      const_cast<PredictionUnit&>(pu).cccmMultiFilterIdx = 0;
+    }
+#endif
+#if JVET_AC0147_CCCM_NO_SUBSAMPLING
+    else if (ccpCand.type & CCP_TYPE_NSCCCM)
+    {
+#if JVET_AB0174_CCCM_DIV_FREE
+      int lumaOffset = m_cccmLumaOffset - ccpCand.lumaOffset;
+#else
+      int lumaOffset = 0;
+#endif
+      const_cast<PredictionUnit &>(pu).cccmNoSubFlag = 1;
+
+      CccmModel<CCCM_NO_SUB_NUM_PARAMS> cccmModelCb[2] = { CccmModel<CCCM_NO_SUB_NUM_PARAMS>(bitDepth), CccmModel<CCCM_NO_SUB_NUM_PARAMS>(bitDepth) };
+      CccmModel<CCCM_NO_SUB_NUM_PARAMS> cccmModelCr[2] = { CccmModel<CCCM_NO_SUB_NUM_PARAMS>(bitDepth), CccmModel<CCCM_NO_SUB_NUM_PARAMS>(bitDepth) };
+        
+      PU::ccpParamsToCccmModel(ccpCand, cccmModelCb, cccmModelCr);
+        
+      cost += xGetCostNSCCCM(pu, COMPONENT_Cb, modelNum, pu.Cb(), cccmModelCb, ccpCand.yThres, lumaOffset, offsetCb);
+      cost += xGetCostNSCCCM(pu, COMPONENT_Cr, modelNum, pu.Cr(), cccmModelCr, ccpCand.yThres, lumaOffset, offsetCr);
+
+      const_cast<PredictionUnit &>(pu).cccmNoSubFlag = 0;
+    }
+#endif
+    else if (ccpCand.type & (CCP_TYPE_CCLM | CCP_TYPE_GLM0123))
+    {
+      CPelBuf temp;
+      int     lumaStride;
+
+      if (ccpCand.type & CCP_TYPE_GLM0123)
+      {
+        Pel *glmTemp = m_glmTempCb[ccpCand.glmIdc];
+        lumaStride   = 2 * MAX_CU_SIZE + 1;
+        temp         = PelBuf(glmTemp + lumaStride + 1, lumaStride, Size(chromaArea));
+      }
+      else
+      {
+        lumaStride = MAX_CU_SIZE + 1;
+        temp       = PelBuf(m_piTemp + lumaStride + 1, lumaStride, Size(chromaArea));
+      }
+
+      CclmModel cclmModels;
+      PU::ccpParamsToCclmModel(COMPONENT_Cb, ccpCand, cclmModels);
+      cost += xGetCostCCLM(pu, COMPONENT_Cb, pu.Cb(), cclmModels, 1, ccpCand.glmIdc);
+      PU::ccpParamsToCclmModel(COMPONENT_Cr, ccpCand, cclmModels);
+      cost += xGetCostCCLM(pu, COMPONENT_Cr, pu.Cr(), cclmModels, 1, ccpCand.glmIdc);
+    }
+    else if (ccpCand.type & CCP_TYPE_GLM4567)
+    {
+      int glmIdc       = ccpCand.glmIdc;
+      int chromaOffset = 0;
+#if JVET_AB0174_CCCM_DIV_FREE
+      int lumaOffset = m_glmLumaOffset - ccpCand.lumaOffset;
+#else
+      int lumaOffset = 0;
+#endif
+      CccmModel<GLM_NUM_PARAMS> glmModel(bitDepth);
+      PU::ccpParamsToGlmModel(COMPONENT_Cb, ccpCand, glmModel);
+      cost += xGetCostGLM(pu, COMPONENT_Cb, pu.Cb(), glmModel, glmIdc, lumaOffset, chromaOffset);
+      PU::ccpParamsToGlmModel(COMPONENT_Cr, ccpCand, glmModel);
+      cost += xGetCostGLM(pu, COMPONENT_Cr, pu.Cr(), glmModel, glmIdc, lumaOffset, chromaOffset);
+    }
+    else
+    {
+      THROW("Invalid type!");
+    }
+  }
+  return cost;
+}
+
+void IntraPrediction::predCCPCandidate(const PredictionUnit &pu, PelBuf &predCb, PelBuf &predCr)
+{
+  const int bitDepth = pu.cu->slice->getSPS()->getBitDepth(CHANNEL_TYPE_LUMA);
+
+  if (pu.idxNonLocalCCP)
+  {
+    CompArea                   chromaArea = pu.Cb();
+    int                        cWidth     = chromaArea.width;
+    int                        cHeight    = chromaArea.height;
+    CccmModel<CCCM_NUM_PARAMS> cccmModelCb[2] = { CccmModel<CCCM_NUM_PARAMS>(bitDepth), CccmModel<CCCM_NUM_PARAMS>(bitDepth) };
+    CccmModel<CCCM_NUM_PARAMS> cccmModelCr[2] = { CccmModel<CCCM_NUM_PARAMS>(bitDepth), CccmModel<CCCM_NUM_PARAMS>(bitDepth) };
+
+    CHECK(pu.curCand.type == CCP_TYPE_NONE, "CCP model with no type")
+    {
+      int modelNum = (pu.curCand.type & CCP_TYPE_MMLM) ? 2 : 1;
+
+      if (pu.curCand.type & (CCP_TYPE_CCCM | CCP_TYPE_GLCCCM))
+      {
+#if JVET_AB0174_CCCM_DIV_FREE
+        int lumaOffset = m_cccmLumaOffset - pu.curCand.lumaOffset;
+#else
+        int lumaOffset = 0;
+#endif
+        int refSizeX = pu.curCand.corOffX;
+        int refSizeY = pu.curCand.corOffY;
+        int offsetCb[2] = { 0, 0 };
+        int offsetCr[2] = { 0, 0 };
+
+        PU::ccpParamsToCccmModel(pu.curCand, cccmModelCb, cccmModelCr);
+
+        if (!(pu.curCand.type & CCP_TYPE_MMLM))
+        {
+          xGetUpdatedOffsetCCCM(pu, COMPONENT_Cb, modelNum, pu.Cb(), cccmModelCb, 0, lumaOffset, offsetCb, pu.curCand.type, refSizeX, refSizeY);
+          xGetUpdatedOffsetCCCM(pu, COMPONENT_Cr, modelNum, pu.Cr(), cccmModelCr, 0, lumaOffset, offsetCr, pu.curCand.type, refSizeX, refSizeY);
+
+          xCccmApplyModelOffset(pu, COMPONENT_Cb, cccmModelCb[0], 0, 0, predCb, lumaOffset, offsetCb, pu.curCand.type, refSizeX, refSizeY);
+          xCccmApplyModelOffset(pu, COMPONENT_Cr, cccmModelCr[0], 0, 0, predCr, lumaOffset, offsetCr, pu.curCand.type, refSizeX, refSizeY);
+        }
+        else
+        {
+          // Multimode case
+          int modelThr = pu.curCand.yThres;
+
+          xGetUpdatedOffsetCCCM(pu, COMPONENT_Cb, modelNum, pu.Cb(), cccmModelCb, modelThr, lumaOffset, offsetCb, pu.curCand.type, refSizeX, refSizeY);
+          xGetUpdatedOffsetCCCM(pu, COMPONENT_Cr, modelNum, pu.Cr(), cccmModelCr, modelThr, lumaOffset, offsetCr, pu.curCand.type, refSizeX, refSizeY);
+
+          xCccmApplyModelOffset(pu, COMPONENT_Cb, cccmModelCb[0], 1, modelThr, predCb, lumaOffset, offsetCb, pu.curCand.type, refSizeX, refSizeY);
+          xCccmApplyModelOffset(pu, COMPONENT_Cr, cccmModelCr[0], 1, modelThr, predCr, lumaOffset, offsetCr, pu.curCand.type, refSizeX, refSizeY);
+
+          xCccmApplyModelOffset(pu, COMPONENT_Cb, cccmModelCb[1], 2, modelThr, predCb, lumaOffset, offsetCb, pu.curCand.type, refSizeX, refSizeY);
+          xCccmApplyModelOffset(pu, COMPONENT_Cr, cccmModelCr[1], 2, modelThr, predCr, lumaOffset, offsetCr, pu.curCand.type, refSizeX, refSizeY);
+        }
+      }
+#if JVET_AC0147_CCCM_NO_SUBSAMPLING
+      else if (pu.curCand.type & CCP_TYPE_NSCCCM)
+      {
+        CccmModel<CCCM_NO_SUB_NUM_PARAMS> nscccmModelCb[2] = { CccmModel<CCCM_NO_SUB_NUM_PARAMS>(bitDepth), CccmModel<CCCM_NO_SUB_NUM_PARAMS>(bitDepth) };
+        CccmModel<CCCM_NO_SUB_NUM_PARAMS> nscccmModelCr[2] = { CccmModel<CCCM_NO_SUB_NUM_PARAMS>(bitDepth), CccmModel<CCCM_NO_SUB_NUM_PARAMS>(bitDepth) };
+#if JVET_AB0174_CCCM_DIV_FREE
+        int lumaOffset = m_cccmLumaOffset - pu.curCand.lumaOffset;
+#else
+        int lumaOffset = 0;
+#endif
+        const_cast<PredictionUnit &>(pu).cccmNoSubFlag = 1;
+        int offsetCb[2] = { 0, 0 };
+        int offsetCr[2] = { 0, 0 };
+
+        PU::ccpParamsToCccmModel(pu.curCand, nscccmModelCb, nscccmModelCr);
+
+        if (!(pu.curCand.type & CCP_TYPE_MMLM))
+        {
+          xGetUpdatedOffsetNSCCCM(pu, COMPONENT_Cb, modelNum, pu.Cb(), nscccmModelCb, 0, lumaOffset, offsetCb);
+          xGetUpdatedOffsetNSCCCM(pu, COMPONENT_Cr, modelNum, pu.Cr(), nscccmModelCr, 0, lumaOffset, offsetCr);
+
+          xNSCccmApplyModelOffset(pu, COMPONENT_Cb, nscccmModelCb[0], 0, 0, predCb, lumaOffset, offsetCb);
+          xNSCccmApplyModelOffset(pu, COMPONENT_Cr, nscccmModelCr[0], 0, 0, predCr, lumaOffset, offsetCr);
+        }
+        else
+        {
+          // Multimode case
+          int modelThr = pu.curCand.yThres;
+
+          xGetUpdatedOffsetNSCCCM(pu, COMPONENT_Cb, modelNum, pu.Cb(), nscccmModelCb, modelThr, lumaOffset, offsetCb);
+          xGetUpdatedOffsetNSCCCM(pu, COMPONENT_Cr, modelNum, pu.Cr(), nscccmModelCr, modelThr, lumaOffset, offsetCr);
+
+          xNSCccmApplyModelOffset(pu, COMPONENT_Cb, nscccmModelCb[0], 1, modelThr, predCb, lumaOffset, offsetCb);
+          xNSCccmApplyModelOffset(pu, COMPONENT_Cr, nscccmModelCr[0], 1, modelThr, predCr, lumaOffset, offsetCr);
+
+          xNSCccmApplyModelOffset(pu, COMPONENT_Cb, nscccmModelCb[1], 2, modelThr, predCb, lumaOffset, offsetCb);
+          xNSCccmApplyModelOffset(pu, COMPONENT_Cr, nscccmModelCr[1], 2, modelThr, predCr, lumaOffset, offsetCr);
+        }
+
+        const_cast<PredictionUnit &>(pu).cccmNoSubFlag = 0;
+      }
+#endif
+#if JVET_AD0202_CCCM_MDF
+      else if (pu.curCand.type & CCP_TYPE_MDFCCCM)
+      {
+        const_cast<PredictionUnit&>(pu).cccmMultiFilterIdx = pu.curCand.cccmMultiFilterIdx;
+#if JVET_AB0174_CCCM_DIV_FREE
+        int lumaOffset = m_cccmLumaOffset - pu.curCand.lumaOffset;
+#else
+        int lumaOffset = 0;
+#endif
+        int refSizeX = pu.curCand.corOffX;
+        int refSizeY = pu.curCand.corOffY;
+        int offsetCb[2] = { 0, 0 };
+        int offsetCr[2] = { 0, 0 };
+
+        if (pu.curCand.cccmMultiFilterIdx == 1)
+        {
+          CccmModel<CCCM_MULTI_PRED_FILTER_NUM_PARAMS> mfcccmModelCb[2] = { CccmModel<CCCM_MULTI_PRED_FILTER_NUM_PARAMS>(bitDepth), CccmModel<CCCM_MULTI_PRED_FILTER_NUM_PARAMS>(bitDepth) };
+          CccmModel<CCCM_MULTI_PRED_FILTER_NUM_PARAMS> mfcccmModelCr[2] = { CccmModel<CCCM_MULTI_PRED_FILTER_NUM_PARAMS>(bitDepth), CccmModel<CCCM_MULTI_PRED_FILTER_NUM_PARAMS>(bitDepth) };
+
+          PU::ccpParamsToCccmModel(pu.curCand, mfcccmModelCb, mfcccmModelCr);
+
+          if (!(pu.curCand.type & CCP_TYPE_MMLM))
+          {
+            xGetUpdatedOffsetMFCCCM1(pu, COMPONENT_Cb, modelNum, pu.Cb(), mfcccmModelCb, 0, lumaOffset, offsetCb, pu.curCand.type, refSizeX, refSizeY);
+            xGetUpdatedOffsetMFCCCM1(pu, COMPONENT_Cr, modelNum, pu.Cr(), mfcccmModelCr, 0, lumaOffset, offsetCr, pu.curCand.type, refSizeX, refSizeY);
+
+            xMFCccmApplyModelOffset1(pu, COMPONENT_Cb, mfcccmModelCb[0], 0, 0, predCb, lumaOffset, offsetCb);
+            xMFCccmApplyModelOffset1(pu, COMPONENT_Cr, mfcccmModelCr[0], 0, 0, predCr, lumaOffset, offsetCr);
+          }
+          else
+          {
+            // Multimode case
+            int modelThr = pu.curCand.yThres;
+
+            xGetUpdatedOffsetMFCCCM1(pu, COMPONENT_Cb, modelNum, pu.Cb(), mfcccmModelCb, modelThr, lumaOffset, offsetCb, pu.curCand.type, refSizeX, refSizeY);
+            xGetUpdatedOffsetMFCCCM1(pu, COMPONENT_Cr, modelNum, pu.Cr(), mfcccmModelCr, modelThr, lumaOffset, offsetCr, pu.curCand.type, refSizeX, refSizeY);
+
+            xMFCccmApplyModelOffset1(pu, COMPONENT_Cb, mfcccmModelCb[0], 1, modelThr, predCb, lumaOffset, offsetCb);
+            xMFCccmApplyModelOffset1(pu, COMPONENT_Cr, mfcccmModelCr[0], 1, modelThr, predCr, lumaOffset, offsetCr);
+
+            xMFCccmApplyModelOffset1(pu, COMPONENT_Cb, mfcccmModelCb[1], 2, modelThr, predCb, lumaOffset, offsetCb);
+            xMFCccmApplyModelOffset1(pu, COMPONENT_Cr, mfcccmModelCr[1], 2, modelThr, predCr, lumaOffset, offsetCr);
+          }
+        }
+        else // if (pu.curCand.cccmMultiFilterIdx == 2 || pu.curCand.cccmMultiFilterIdx == 3)
+        {
+          CccmModel<CCCM_MULTI_PRED_FILTER_NUM_PARAMS2> mfcccmModelCb[2] = { CccmModel<CCCM_MULTI_PRED_FILTER_NUM_PARAMS2>(bitDepth), CccmModel<CCCM_MULTI_PRED_FILTER_NUM_PARAMS2>(bitDepth) };
+          CccmModel<CCCM_MULTI_PRED_FILTER_NUM_PARAMS2> mfcccmModelCr[2] = { CccmModel<CCCM_MULTI_PRED_FILTER_NUM_PARAMS2>(bitDepth), CccmModel<CCCM_MULTI_PRED_FILTER_NUM_PARAMS2>(bitDepth) };
+
+          PU::ccpParamsToCccmModel(pu.curCand, mfcccmModelCb, mfcccmModelCr);
+
+          if (!(pu.curCand.type & CCP_TYPE_MMLM))
+          {
+            xGetUpdatedOffsetMFCCCM23(pu, COMPONENT_Cb, modelNum, pu.Cb(), mfcccmModelCb, 0, lumaOffset, offsetCb, pu.curCand.type, refSizeX, refSizeY);
+            xGetUpdatedOffsetMFCCCM23(pu, COMPONENT_Cr, modelNum, pu.Cr(), mfcccmModelCr, 0, lumaOffset, offsetCr, pu.curCand.type, refSizeX, refSizeY);
+
+            xMFCccmApplyModelOffset23(pu, COMPONENT_Cb, mfcccmModelCb[0], 0, 0, predCb, lumaOffset, offsetCb);
+            xMFCccmApplyModelOffset23(pu, COMPONENT_Cr, mfcccmModelCr[0], 0, 0, predCr, lumaOffset, offsetCr);
+          }
+          else
+          {
+            // Multimode case
+            int modelThr = pu.curCand.yThres;
+
+            xGetUpdatedOffsetMFCCCM23(pu, COMPONENT_Cb, modelNum, pu.Cb(), mfcccmModelCb, modelThr, lumaOffset, offsetCb, pu.curCand.type, refSizeX, refSizeY);
+            xGetUpdatedOffsetMFCCCM23(pu, COMPONENT_Cr, modelNum, pu.Cr(), mfcccmModelCr, modelThr, lumaOffset, offsetCr, pu.curCand.type, refSizeX, refSizeY);
+
+            xMFCccmApplyModelOffset23(pu, COMPONENT_Cb, mfcccmModelCb[0], 1, modelThr, predCb, lumaOffset, offsetCb);
+            xMFCccmApplyModelOffset23(pu, COMPONENT_Cr, mfcccmModelCr[0], 1, modelThr, predCr, lumaOffset, offsetCr);
+
+            xMFCccmApplyModelOffset23(pu, COMPONENT_Cb, mfcccmModelCb[1], 2, modelThr, predCb, lumaOffset, offsetCb);
+            xMFCccmApplyModelOffset23(pu, COMPONENT_Cr, mfcccmModelCr[1], 2, modelThr, predCr, lumaOffset, offsetCr);
+          }
+        }
+
+        const_cast<PredictionUnit&>(pu).cccmMultiFilterIdx = 0;
+      }
+#endif
+      else if (pu.curCand.type & (CCP_TYPE_CCLM | CCP_TYPE_GLM0123))
+      {
+        CPelBuf temp;
+        int     lumaStride;
+
+        if (pu.curCand.type & CCP_TYPE_GLM0123)
+        {
+          Pel *glmTemp = m_glmTempCb[pu.curCand.glmIdc];
+          lumaStride   = 2 * MAX_CU_SIZE + 1;
+          temp         = PelBuf(glmTemp + lumaStride + 1, lumaStride, Size(chromaArea));
+        }
+        else
+        {
+          lumaStride = MAX_CU_SIZE + 1;
+          temp       = PelBuf(m_piTemp + lumaStride + 1, lumaStride, Size(chromaArea));
+        }
+
+        CclmModel modelsCb, modelsCr;
+        PU::ccpParamsToCclmModel(COMPONENT_Cb, pu.curCand, modelsCb);
+        PU::ccpParamsToCclmModel(COMPONENT_Cr, pu.curCand, modelsCr);
+
+        if (!(pu.curCand.type & CCP_TYPE_MMLM))
+        {
+          xGetUpdatedOffsetCCLM(pu, COMPONENT_Cb, pu.Cb(), modelsCb, 1, pu.curCand.glmIdc);
+          xGetUpdatedOffsetCCLM(pu, COMPONENT_Cr, pu.Cr(), modelsCr, 1, pu.curCand.glmIdc);
+
+          predCb.copyFrom(temp);
+          predCr.copyFrom(temp);
+
+          predCb.linearTransform(modelsCb.a, modelsCb.shift, modelsCb.b, true, pu.cs->slice->clpRng(COMPONENT_Cb));
+          predCr.linearTransform(modelsCr.a, modelsCr.shift, modelsCr.b, true, pu.cs->slice->clpRng(COMPONENT_Cb));
+        }
+        else
+        {
+          xGetUpdatedOffsetCCLM(pu, COMPONENT_Cb, pu.Cb(), modelsCb, 2, pu.curCand.glmIdc);
+          xGetUpdatedOffsetCCLM(pu, COMPONENT_Cr, pu.Cr(), modelsCr, 2, pu.curCand.glmIdc);
+
+          auto applyMMCM = [&](PelBuf &predBuf, const CclmModel &cclmModel)
+          {
+            Pel       *chromaPred   = predBuf.bufAt(0, 0);
+            const Pel *lumaReco     = temp.bufAt(0, 0);
+            int        chromaStride = predBuf.stride;
+
+            for (int i = 0; i < cHeight; i++)
+            {
+              for (int j = 0; j < cWidth; j++)
+              {
+                if (lumaReco[j] <= cclmModel.yThres)
+                {
+                  chromaPred[j] = (Pel) ClipPel(((cclmModel.a * lumaReco[j]) >> cclmModel.shift) + cclmModel.b, pu.cs->slice->clpRng(COMPONENT_Cb));
+                }
+                else
+                {
+                  chromaPred[j] = (Pel) ClipPel(((cclmModel.a2 * lumaReco[j]) >> cclmModel.shift2) + cclmModel.b2, pu.cs->slice->clpRng(COMPONENT_Cb));
+                }
+              }
+              chromaPred += chromaStride;
+              lumaReco += lumaStride;
+            }
+          };
+          applyMMCM(predCb, modelsCb);
+          applyMMCM(predCr, modelsCr);
+        }
+        PU::cclmModelToCcpParams(COMPONENT_Cb, const_cast<PredictionUnit&>(pu).curCand, modelsCb);
+        PU::cclmModelToCcpParams(COMPONENT_Cr, const_cast<PredictionUnit&>(pu).curCand, modelsCr);
+      }
+      else if (pu.curCand.type & CCP_TYPE_GLM4567)
+      {
+#if JVET_AB0174_CCCM_DIV_FREE
+        int lumaOffset = m_glmLumaOffset - pu.curCand.lumaOffset;
+#else
+        int lumaOffset = 0;
+#endif
+        int glmIdc       = pu.curCand.glmIdc;
+        int chromaOffset = 0;
+        CccmModel<GLM_NUM_PARAMS> glmModel(bitDepth);
+
+        PU::ccpParamsToGlmModel(COMPONENT_Cb, pu.curCand, glmModel);
+        xGetUpdatedOffsetGLM(pu, COMPONENT_Cb, pu.Cb(), glmModel, glmIdc, lumaOffset, chromaOffset);
+        xGlmApplyModelOffset(pu, COMPONENT_Cb, pu.Cb(), glmModel, glmIdc, predCb, lumaOffset, chromaOffset);
+
+        PU::ccpParamsToGlmModel(COMPONENT_Cr, pu.curCand, glmModel);
+        xGetUpdatedOffsetGLM(pu, COMPONENT_Cr, pu.Cr(), glmModel, glmIdc, lumaOffset, chromaOffset);
+        xGlmApplyModelOffset(pu, COMPONENT_Cr, pu.Cr(), glmModel, glmIdc, predCr, lumaOffset, chromaOffset);
+      }
+      else
+      {
+        THROW("Invalid Type");
+      }
+    }
+  }
+}
+#endif
+
+#if JVET_AB0174_CCCM_DIV_FREE
+#define DIV_PREC_BITS       14
+#define DIV_PREC_BITS_POW2  8
+#define DIV_SLOT_BITS       3
+#define DIV_INTR_BITS      (DIV_PREC_BITS - DIV_SLOT_BITS)
+#define DIV_INTR_ROUND     (1 << DIV_INTR_BITS >> 1)
+
+int64_t xDivide(int64_t num, int64_t denom) // Note: assumes positive denominator
+{
+  static const int pow2W[8] = {   214,   153,   113,    86,    67,    53,    43,    35  }; // DIV_PREC_BITS_POW2
+  static const int pow2O[8] = {  4822,  5952,  6624,  6792,  6408,  5424,  3792,  1466  }; // DIV_PREC_BITS
+  static const int pow2B[8] = { 12784, 12054, 11670, 11583, 11764, 12195, 12870, 13782  }; // DIV_PREC_BITS
+
+  int shift     = floorLog2Uint64(denom);
+  int round     = 1 << shift >> 1;
+  int normDiff  = (((denom << DIV_PREC_BITS) + round) >> shift) & ((1 << DIV_PREC_BITS) - 1);
+  int diffFull  = normDiff >> DIV_INTR_BITS;
+  int normDiff2 = normDiff - pow2O[diffFull];
+
+  int scale     = ((pow2W[diffFull] * ((normDiff2 * normDiff2) >> DIV_PREC_BITS)) >> DIV_PREC_BITS_POW2) - (normDiff2 >> 1) + pow2B[diffFull];
+
+  return ( (num << (CCCM_DECIM_BITS - DIV_PREC_BITS)) * scale + round) >> shift;
+}
+
+#if JVET_AC0053_GAUSSIAN_SOLVER
+void xGetDivScaleRoundShift(int64_t denom, int &scale, int &round, int &shift) // Note: assumes positive denominator
+{
+  static const int pow2W[8] = {   214,   153,   113,    86,    67,    53,    43,    35  }; // DIV_PREC_BITS_POW2
+  static const int pow2O[8] = {  4822,  5952,  6624,  6792,  6408,  5424,  3792,  1466  }; // DIV_PREC_BITS
+  static const int pow2B[8] = { 12784, 12054, 11670, 11583, 11764, 12195, 12870, 13782  }; // DIV_PREC_BITS
+
+  shift         = floorLog2Uint64(denom);
+  round         = 1 << shift >> 1;
+  int normDiff  = (((denom << DIV_PREC_BITS) + round) >> shift) & ((1 << DIV_PREC_BITS) - 1);
+  int diffFull  = normDiff >> DIV_INTR_BITS;
+  int normDiff2 = normDiff - pow2O[diffFull];
+
+  scale         = ((pow2W[diffFull] * ((normDiff2 * normDiff2) >> DIV_PREC_BITS)) >> DIV_PREC_BITS_POW2) - (normDiff2 >> 1) + pow2B[diffFull];
+  scale       <<= CCCM_DECIM_BITS - DIV_PREC_BITS;
+}
+#endif
+
+#undef DIV_PREC_BITS
+#undef DIV_PREC_BITS_POW2
+#undef DIV_SLOT_BITS
+#undef DIV_INTR_BITS
+#undef DIV_INTR_ROUND
+
+int xCccmDivideLowPrec(int64_t num, int64_t denom)
+{
+  if ( num < 0 )
+  {
+    return -int(xDivide(-num, denom) >> CCCM_DECIM_BITS);
+  }
+  else
+  {
+    return int(xDivide(num, denom) >> CCCM_DECIM_BITS);
+  }
+}
+
+int64_t xCccmDivide(int64_t num, int64_t denom) // Note: assumes positive denominator
+{
+  return xDivide(num, denom);
+}
+#endif
+
+#if JVET_AA0057_CCCM || JVET_AC0119_LM_CHROMA_FUSION
+#if JVET_AB0174_CCCM_DIV_FREE
+void IntraPrediction::xCccmSetLumaRefValue(const PredictionUnit& pu)
+{
+  int lumaPosX = m_cccmBlkArea.x << getComponentScaleX(COMPONENT_Cb, pu.cu->chromaFormat);
+  int lumaPosY = m_cccmBlkArea.y << getComponentScaleY(COMPONENT_Cb, pu.cu->chromaFormat);
+
+  if (lumaPosX || lumaPosY)
+  {
+    lumaPosX = lumaPosX ? lumaPosX - 1 : 0;
+    lumaPosY = lumaPosY ? lumaPosY - 1 : 0;
+
+    m_cccmLumaOffset = pu.cs->picture->getRecoBuf(COMPONENT_Y).at(lumaPosX, lumaPosY);
+  }
+  else
+  {
+    m_cccmLumaOffset = 1 << (pu.cu->slice->getSPS()->getBitDepth(CHANNEL_TYPE_LUMA) - 1);
+  }
+}
+#endif
+
+// Calculate a single downsampled luma reference value (copied from IntraPrediction::xGetLumaRecPixels)
+Pel IntraPrediction::xCccmGetLumaVal(const PredictionUnit& pu, const CPelBuf pi, const int x, const int y
+#if JVET_AD0202_CCCM_MDF
+  , int downsFilterIdx
+#endif
+) const
+{
+  const Pel* piSrc = pi.buf;
+  const int iRecStride = pi.stride;
+  Pel ypval = 0;
+#if JVET_AC0147_CCCM_NO_SUBSAMPLING
+  if (pu.cccmNoSubFlag || pu.chromaFormat == CHROMA_444)
+#else
+  if (pu.chromaFormat == CHROMA_444)
+#endif
+  {
+    ypval = piSrc[x + iRecStride * y];
+  }
+  else if (pu.chromaFormat == CHROMA_422)
+  {
+    int s = 2;
+    int offLeft = x > 0 ? -1 : 0;
+    s += piSrc[2 * x + iRecStride * y] * 2;
+    s += piSrc[2 * x + offLeft + iRecStride * y];
+    s += piSrc[2 * x + 1 + iRecStride * y];
+    ypval = s >> 2;
+  }
+  else if (pu.cs->sps->getCclmCollocatedChromaFlag())
+  {
+    int s = 4;
+    int offLeft = x > 0 ? -1 : 0;
+    int offAbove = y > 0 ? -1 : 0;
+    s += piSrc[2 * x + iRecStride * 2 * y] * 4;
+    s += piSrc[2 * x + offLeft + iRecStride * 2 * y];
+    s += piSrc[2 * x + 1 + iRecStride * 2 * y];
+    s += piSrc[2 * x + iRecStride * (2 * y + 1)];
+    s += piSrc[2 * x + iRecStride * (2 * y + offAbove)];
+    ypval = s >> 3;
+  }
+  else
+  {
+#if JVET_AD0202_CCCM_MDF
+    const int lumaPosPicX1 = 2 * x;
+    const int lumaPosPicY1 = 2 * y;
+    int lumaPosPicX0 = lumaPosPicX1 - 1; lumaPosPicX0 = lumaPosPicX0 < 0 ? 0 : lumaPosPicX0;
+    const int lumaPosPicX2 = lumaPosPicX1 + 1;
+    const int lumaPosPicY2 = lumaPosPicY1 + 1;
+    const int shift0 = iRecStride * lumaPosPicY1;
+    const int shift1 = iRecStride * lumaPosPicY2;
+
+    if (downsFilterIdx == 0)
+    {
+      int s = 4;
+
+      s += piSrc[lumaPosPicX1 + shift0] * 2;
+      s += piSrc[lumaPosPicX0 + shift0];
+      s += piSrc[lumaPosPicX2 + shift0];
+      s += piSrc[lumaPosPicX1 + shift1] * 2;
+      s += piSrc[lumaPosPicX0 + shift1];
+      s += piSrc[lumaPosPicX2 + shift1];
+      ypval = s >> 3;
+    }
+    else if (downsFilterIdx == 1)
+    {
+      int s = 0;
+
+      s += piSrc[lumaPosPicX0 + shift0];
+      s -= piSrc[lumaPosPicX2 + shift0];
+      s += piSrc[lumaPosPicX0 + shift1];
+      s -= piSrc[lumaPosPicX2 + shift1];
+
+      ypval = s < 0 ? 0 : s;
+    }
+    else if (downsFilterIdx == 2)
+    {
+      int s = 0;
+
+      s += piSrc[lumaPosPicX0 + shift0];
+      s += piSrc[lumaPosPicX1 + shift0] * 2;
+      s += piSrc[lumaPosPicX2 + shift0];
+      s -= piSrc[lumaPosPicX0 + shift1];
+      s -= piSrc[lumaPosPicX1 + shift1] * 2;
+      s -= piSrc[lumaPosPicX2 + shift1];
+
+      ypval = s < 0 ? 0 : s;
+    }
+    else
+    {
+      int s = 0;
+
+      s -= piSrc[lumaPosPicX0 + shift0];
+      s += piSrc[lumaPosPicX1 + shift0];
+      s += piSrc[lumaPosPicX2 + shift0] * 2;
+      s -= piSrc[lumaPosPicX0 + shift1] * 2;
+      s -= piSrc[lumaPosPicX1 + shift1];
+      s += piSrc[lumaPosPicX2 + shift1];
+
+      ypval = s < 0 ? 0 : s;
     }
 #else
     int s = 4;
@@ -12746,8 +15597,15 @@ void IntraPrediction::predIntraCCCM( const PredictionUnit &pu, PelBuf &predCb, P
 #if JVET_AC0147_CCCM_NO_SUBSAMPLING
   if( pu.cccmNoSubFlag )
   {
+#if JVET_AD0188_CCP_MERGE
+    CccmModel<CCCM_NO_SUB_NUM_PARAMS> cccmModelCb[2] = { CccmModel<CCCM_NO_SUB_NUM_PARAMS>(pu.cu->slice->getSPS()->getBitDepth(CHANNEL_TYPE_LUMA)), 
+                                                         CccmModel<CCCM_NO_SUB_NUM_PARAMS>(pu.cu->slice->getSPS()->getBitDepth(CHANNEL_TYPE_LUMA)) };
+    CccmModel<CCCM_NO_SUB_NUM_PARAMS> cccmModelCr[2] = { CccmModel<CCCM_NO_SUB_NUM_PARAMS>(pu.cu->slice->getSPS()->getBitDepth(CHANNEL_TYPE_LUMA)),
+                                                         CccmModel<CCCM_NO_SUB_NUM_PARAMS>(pu.cu->slice->getSPS()->getBitDepth(CHANNEL_TYPE_LUMA)) };
+#else
     CccmModel<CCCM_NO_SUB_NUM_PARAMS> cccmModelCb( pu.cu->slice->getSPS()->getBitDepth( CHANNEL_TYPE_LUMA ) );
     CccmModel<CCCM_NO_SUB_NUM_PARAMS> cccmModelCr( pu.cu->slice->getSPS()->getBitDepth( CHANNEL_TYPE_LUMA ) );
+#endif
 
 #if JVET_AB0143_CCCM_TS
     if( intraDir == LM_CHROMA_IDX || intraDir == MDLM_L_IDX || intraDir == MDLM_T_IDX )
@@ -12755,15 +15613,44 @@ void IntraPrediction::predIntraCCCM( const PredictionUnit &pu, PelBuf &predCb, P
     if( PU::cccmSingleModeAvail( pu, intraDir ) )
 #endif
     {
+#if JVET_AD0188_CCP_MERGE
+      xCccmCalcModels(pu, cccmModelCb[0], cccmModelCr[0], 0, 0);
+      xCccmApplyModel(pu, COMPONENT_Cb,   cccmModelCb[0], 0, 0, predCb);
+      xCccmApplyModel(pu, COMPONENT_Cr,   cccmModelCr[0], 0, 0, predCr);
+
+      const_cast<PredictionUnit &>(pu).curCand.type = CCP_TYPE_NSCCCM;
+      PU::cccmModelToCcpParams(const_cast<PredictionUnit&>(pu).curCand, cccmModelCb, cccmModelCr, 0
+#if JVET_AB0174_CCCM_DIV_FREE
+                               , m_cccmLumaOffset
+#endif
+                               );
+#else
       xCccmCalcModels( pu, cccmModelCb, cccmModelCr, 0, 0 );
       xCccmApplyModel( pu, COMPONENT_Cb, cccmModelCb, 0, 0, predCb );
       xCccmApplyModel( pu, COMPONENT_Cr, cccmModelCr, 0, 0, predCr );
+#endif
     }
     else
     {
       // Multimode case
       int modelThr = xCccmCalcRefAver( pu );
 
+#if JVET_AD0188_CCP_MERGE
+      xCccmCalcModels( pu, cccmModelCb[0], cccmModelCr[0], 1, modelThr );
+      xCccmApplyModel( pu, COMPONENT_Cb,   cccmModelCb[0], 1, modelThr, predCb );
+      xCccmApplyModel( pu, COMPONENT_Cr,   cccmModelCr[0], 1, modelThr, predCr );
+
+      xCccmCalcModels( pu, cccmModelCb[1], cccmModelCr[1], 2, modelThr );
+      xCccmApplyModel( pu, COMPONENT_Cb,   cccmModelCb[1], 2, modelThr, predCb );
+      xCccmApplyModel( pu, COMPONENT_Cr,   cccmModelCr[1], 2, modelThr, predCr );
+
+      const_cast<PredictionUnit &>(pu).curCand.type = (CCP_TYPE_NSCCCM | CCP_TYPE_MMLM);
+      PU::cccmModelToCcpParams(const_cast<PredictionUnit&>(pu).curCand, cccmModelCb, cccmModelCr, modelThr
+#if JVET_AB0174_CCCM_DIV_FREE
+                               , m_cccmLumaOffset
+#endif
+                               );
+#else
       xCccmCalcModels( pu, cccmModelCb, cccmModelCr, 1, modelThr );
       xCccmApplyModel( pu, COMPONENT_Cb, cccmModelCb, 1, modelThr, predCb );
       xCccmApplyModel( pu, COMPONENT_Cr, cccmModelCr, 1, modelThr, predCr );
@@ -12771,6 +15658,7 @@ void IntraPrediction::predIntraCCCM( const PredictionUnit &pu, PelBuf &predCb, P
       xCccmCalcModels( pu, cccmModelCb, cccmModelCr, 2, modelThr );
       xCccmApplyModel( pu, COMPONENT_Cb, cccmModelCb, 2, modelThr, predCb );
       xCccmApplyModel( pu, COMPONENT_Cr, cccmModelCr, 2, modelThr, predCr );
+#endif
     }
   }
   else
@@ -12778,8 +15666,15 @@ void IntraPrediction::predIntraCCCM( const PredictionUnit &pu, PelBuf &predCb, P
 #if JVET_AD0202_CCCM_MDF
   if (pu.cccmMultiFilterIdx == 1)
   {
+#if JVET_AD0188_CCP_MERGE
+    CccmModel<CCCM_MULTI_PRED_FILTER_NUM_PARAMS> cccmModelCb[2] = { CccmModel<CCCM_MULTI_PRED_FILTER_NUM_PARAMS>(pu.cu->slice->getSPS()->getBitDepth(CHANNEL_TYPE_LUMA)),
+                                                                    CccmModel<CCCM_MULTI_PRED_FILTER_NUM_PARAMS>(pu.cu->slice->getSPS()->getBitDepth(CHANNEL_TYPE_LUMA)) };
+    CccmModel<CCCM_MULTI_PRED_FILTER_NUM_PARAMS> cccmModelCr[2] = { CccmModel<CCCM_MULTI_PRED_FILTER_NUM_PARAMS>(pu.cu->slice->getSPS()->getBitDepth(CHANNEL_TYPE_LUMA)),
+                                                                    CccmModel<CCCM_MULTI_PRED_FILTER_NUM_PARAMS>(pu.cu->slice->getSPS()->getBitDepth(CHANNEL_TYPE_LUMA)) };
+#else
     CccmModel<CCCM_MULTI_PRED_FILTER_NUM_PARAMS> cccmModelCb(pu.cu->slice->getSPS()->getBitDepth(CHANNEL_TYPE_LUMA));
     CccmModel<CCCM_MULTI_PRED_FILTER_NUM_PARAMS> cccmModelCr(pu.cu->slice->getSPS()->getBitDepth(CHANNEL_TYPE_LUMA));
+#endif
 
 #if JVET_AB0143_CCCM_TS
     if (intraDir == LM_CHROMA_IDX || intraDir == MDLM_L_IDX || intraDir == MDLM_T_IDX)
@@ -12787,15 +15682,52 @@ void IntraPrediction::predIntraCCCM( const PredictionUnit &pu, PelBuf &predCb, P
     if (PU::cccmSingleModeAvail(pu, intraDir))
 #endif
     {
+#if JVET_AD0188_CCP_MERGE
+      xCccmCalcModels2(pu, cccmModelCb[0], cccmModelCr[0], 0, 0);
+      xCccmApplyModel2(pu, COMPONENT_Cb,   cccmModelCb[0], 0, 0, predCb);
+      xCccmApplyModel2(pu, COMPONENT_Cr,   cccmModelCr[0], 0, 0, predCr);
+
+      const_cast<PredictionUnit&>(pu).curCand.type = CCP_TYPE_MDFCCCM;
+      const_cast<PredictionUnit&>(pu).curCand.cccmMultiFilterIdx = pu.cccmMultiFilterIdx;
+      const_cast<PredictionUnit&>(pu).curCand.corOffX = m_cccmBlkArea.x - m_cccmRefArea.x;
+      const_cast<PredictionUnit&>(pu).curCand.corOffY = m_cccmBlkArea.y - m_cccmRefArea.y;
+
+      PU::cccmModelToCcpParams(const_cast<PredictionUnit&>(pu).curCand, cccmModelCb, cccmModelCr, 0
+#if JVET_AB0174_CCCM_DIV_FREE
+                               , m_cccmLumaOffset
+#endif
+                               );
+#else
       xCccmCalcModels2(pu, cccmModelCb, cccmModelCr, 0, 0);
       xCccmApplyModel2(pu, COMPONENT_Cb, cccmModelCb, 0, 0, predCb);
       xCccmApplyModel2(pu, COMPONENT_Cr, cccmModelCr, 0, 0, predCr);
+#endif
     }
     else
     {
       // Multimode case
       int modelThr = xCccmCalcRefAver(pu);
 
+#if JVET_AD0188_CCP_MERGE
+      xCccmCalcModels2(pu, cccmModelCb[0], cccmModelCr[0], 1, modelThr);
+      xCccmApplyModel2(pu, COMPONENT_Cb,   cccmModelCb[0], 1, modelThr, predCb);
+      xCccmApplyModel2(pu, COMPONENT_Cr,   cccmModelCr[0], 1, modelThr, predCr);
+
+      xCccmCalcModels2(pu, cccmModelCb[1], cccmModelCr[1], 2, modelThr);
+      xCccmApplyModel2(pu, COMPONENT_Cb,   cccmModelCb[1], 2, modelThr, predCb);
+      xCccmApplyModel2(pu, COMPONENT_Cr,   cccmModelCr[1], 2, modelThr, predCr);
+
+      const_cast<PredictionUnit&>(pu).curCand.type = CCP_TYPE_MDFCCCM | CCP_TYPE_MMLM;
+      const_cast<PredictionUnit&>(pu).curCand.cccmMultiFilterIdx = pu.cccmMultiFilterIdx;
+      const_cast<PredictionUnit&>(pu).curCand.corOffX = m_cccmBlkArea.x - m_cccmRefArea.x;
+      const_cast<PredictionUnit&>(pu).curCand.corOffY = m_cccmBlkArea.y - m_cccmRefArea.y;
+
+      PU::cccmModelToCcpParams(const_cast<PredictionUnit&>(pu).curCand, cccmModelCb, cccmModelCr, modelThr
+#if JVET_AB0174_CCCM_DIV_FREE
+                               , m_cccmLumaOffset
+#endif
+                               );
+#else
       xCccmCalcModels2(pu, cccmModelCb, cccmModelCr, 1, modelThr);
       xCccmApplyModel2(pu, COMPONENT_Cb, cccmModelCb, 1, modelThr, predCb);
       xCccmApplyModel2(pu, COMPONENT_Cr, cccmModelCr, 1, modelThr, predCr);
@@ -12803,12 +15735,20 @@ void IntraPrediction::predIntraCCCM( const PredictionUnit &pu, PelBuf &predCb, P
       xCccmCalcModels2(pu, cccmModelCb, cccmModelCr, 2, modelThr);
       xCccmApplyModel2(pu, COMPONENT_Cb, cccmModelCb, 2, modelThr, predCb);
       xCccmApplyModel2(pu, COMPONENT_Cr, cccmModelCr, 2, modelThr, predCr);
+#endif
     }
   }
   else if (pu.cccmMultiFilterIdx > 1)
   {
+#if JVET_AD0188_CCP_MERGE
+    CccmModel<CCCM_MULTI_PRED_FILTER_NUM_PARAMS2> cccmModelCb[2] = { CccmModel<CCCM_MULTI_PRED_FILTER_NUM_PARAMS2>(pu.cu->slice->getSPS()->getBitDepth(CHANNEL_TYPE_LUMA)),
+                                                                     CccmModel<CCCM_MULTI_PRED_FILTER_NUM_PARAMS2>(pu.cu->slice->getSPS()->getBitDepth(CHANNEL_TYPE_LUMA)) };
+    CccmModel<CCCM_MULTI_PRED_FILTER_NUM_PARAMS2> cccmModelCr[2] = { CccmModel<CCCM_MULTI_PRED_FILTER_NUM_PARAMS2>(pu.cu->slice->getSPS()->getBitDepth(CHANNEL_TYPE_LUMA)),
+                                                                     CccmModel<CCCM_MULTI_PRED_FILTER_NUM_PARAMS2>(pu.cu->slice->getSPS()->getBitDepth(CHANNEL_TYPE_LUMA)) };
+#else
     CccmModel<CCCM_MULTI_PRED_FILTER_NUM_PARAMS2> cccmModelCb(pu.cu->slice->getSPS()->getBitDepth(CHANNEL_TYPE_LUMA));
     CccmModel<CCCM_MULTI_PRED_FILTER_NUM_PARAMS2> cccmModelCr(pu.cu->slice->getSPS()->getBitDepth(CHANNEL_TYPE_LUMA));
+#endif
 
 #if JVET_AB0143_CCCM_TS
     if (intraDir == LM_CHROMA_IDX || intraDir == MDLM_L_IDX || intraDir == MDLM_T_IDX)
@@ -12816,15 +15756,52 @@ void IntraPrediction::predIntraCCCM( const PredictionUnit &pu, PelBuf &predCb, P
     if (PU::cccmSingleModeAvail(pu, intraDir))
 #endif
     {
+#if JVET_AD0188_CCP_MERGE
+      xCccmCalcModels3(pu, cccmModelCb[0], cccmModelCr[0], 0, 0);
+      xCccmApplyModel3(pu, COMPONENT_Cb,   cccmModelCb[0], 0, 0, predCb);
+      xCccmApplyModel3(pu, COMPONENT_Cr,   cccmModelCr[0], 0, 0, predCr);
+
+      const_cast<PredictionUnit&>(pu).curCand.type = CCP_TYPE_MDFCCCM;
+      const_cast<PredictionUnit&>(pu).curCand.cccmMultiFilterIdx = pu.cccmMultiFilterIdx;
+      const_cast<PredictionUnit&>(pu).curCand.corOffX = m_cccmBlkArea.x - m_cccmRefArea.x;
+      const_cast<PredictionUnit&>(pu).curCand.corOffY = m_cccmBlkArea.y - m_cccmRefArea.y;
+
+      PU::cccmModelToCcpParams(const_cast<PredictionUnit&>(pu).curCand, cccmModelCb, cccmModelCr, 0
+#if JVET_AB0174_CCCM_DIV_FREE
+                               , m_cccmLumaOffset
+#endif
+                               );
+#else
       xCccmCalcModels3(pu, cccmModelCb, cccmModelCr, 0, 0);
       xCccmApplyModel3(pu, COMPONENT_Cb, cccmModelCb, 0, 0, predCb);
       xCccmApplyModel3(pu, COMPONENT_Cr, cccmModelCr, 0, 0, predCr);
+#endif
     }
     else
     {
       // Multimode case
       int modelThr = xCccmCalcRefAver(pu);
 
+#if JVET_AD0188_CCP_MERGE
+      xCccmCalcModels3(pu, cccmModelCb[0], cccmModelCr[0], 1, modelThr);
+      xCccmApplyModel3(pu, COMPONENT_Cb,   cccmModelCb[0], 1, modelThr, predCb);
+      xCccmApplyModel3(pu, COMPONENT_Cr,   cccmModelCr[0], 1, modelThr, predCr);
+
+      xCccmCalcModels3(pu, cccmModelCb[1], cccmModelCr[1], 2, modelThr);
+      xCccmApplyModel3(pu, COMPONENT_Cb,   cccmModelCb[1], 2, modelThr, predCb);
+      xCccmApplyModel3(pu, COMPONENT_Cr,   cccmModelCr[1], 2, modelThr, predCr);
+
+      const_cast<PredictionUnit&>(pu).curCand.type = CCP_TYPE_MDFCCCM | CCP_TYPE_MMLM;
+      const_cast<PredictionUnit&>(pu).curCand.cccmMultiFilterIdx = pu.cccmMultiFilterIdx;
+      const_cast<PredictionUnit&>(pu).curCand.corOffX = m_cccmBlkArea.x - m_cccmRefArea.x;
+      const_cast<PredictionUnit&>(pu).curCand.corOffY = m_cccmBlkArea.y - m_cccmRefArea.y;
+
+      PU::cccmModelToCcpParams(const_cast<PredictionUnit&>(pu).curCand, cccmModelCb, cccmModelCr, modelThr
+#if JVET_AB0174_CCCM_DIV_FREE
+                               , m_cccmLumaOffset
+#endif
+                               );
+#else
       xCccmCalcModels3(pu, cccmModelCb, cccmModelCr, 1, modelThr);
       xCccmApplyModel3(pu, COMPONENT_Cb, cccmModelCb, 1, modelThr, predCb);
       xCccmApplyModel3(pu, COMPONENT_Cr, cccmModelCr, 1, modelThr, predCr);
@@ -12832,14 +15809,22 @@ void IntraPrediction::predIntraCCCM( const PredictionUnit &pu, PelBuf &predCb, P
       xCccmCalcModels3(pu, cccmModelCb, cccmModelCr, 2, modelThr);
       xCccmApplyModel3(pu, COMPONENT_Cb, cccmModelCb, 2, modelThr, predCb);
       xCccmApplyModel3(pu, COMPONENT_Cr, cccmModelCr, 2, modelThr, predCr);
+#endif
     }
   }
   else
 #endif
   if ( pu.cccmFlag )
   {
+#if JVET_AD0188_CCP_MERGE
+    CccmModel<CCCM_NUM_PARAMS> cccmModelCb[2] = { CccmModel<CCCM_NUM_PARAMS>(pu.cu->slice->getSPS()->getBitDepth(CHANNEL_TYPE_LUMA)), 
+                                                  CccmModel<CCCM_NUM_PARAMS>(pu.cu->slice->getSPS()->getBitDepth(CHANNEL_TYPE_LUMA)) };
+    CccmModel<CCCM_NUM_PARAMS> cccmModelCr[2] = { CccmModel<CCCM_NUM_PARAMS>(pu.cu->slice->getSPS()->getBitDepth(CHANNEL_TYPE_LUMA)),
+                                                  CccmModel<CCCM_NUM_PARAMS>(pu.cu->slice->getSPS()->getBitDepth(CHANNEL_TYPE_LUMA)) };
+#else
     CccmModel<CCCM_NUM_PARAMS> cccmModelCb( pu.cu->slice->getSPS()->getBitDepth(CHANNEL_TYPE_LUMA) );
     CccmModel<CCCM_NUM_PARAMS> cccmModelCr( pu.cu->slice->getSPS()->getBitDepth(CHANNEL_TYPE_LUMA) );
+#endif
 
 #if JVET_AB0143_CCCM_TS
     if ( intraDir == LM_CHROMA_IDX || intraDir == MDLM_L_IDX || intraDir == MDLM_T_IDX )
@@ -12847,15 +15832,55 @@ void IntraPrediction::predIntraCCCM( const PredictionUnit &pu, PelBuf &predCb, P
     if ( PU::cccmSingleModeAvail(pu, intraDir) )
 #endif
     {
+#if JVET_AD0188_CCP_MERGE
+      xCccmCalcModels(pu, cccmModelCb[0], cccmModelCr[0], 0, 0);
+      xCccmApplyModel(pu, COMPONENT_Cb,   cccmModelCb[0], 0, 0, predCb);
+      xCccmApplyModel(pu, COMPONENT_Cr,   cccmModelCr[0], 0, 0, predCr);
+
+#if JVET_AC0054_GLCCCM
+      const_cast<PredictionUnit&>(pu).curCand.type = (pu.glCccmFlag ? CCP_TYPE_GLCCCM : CCP_TYPE_CCCM);
+      const_cast<PredictionUnit&>(pu).curCand.corOffX = m_cccmBlkArea.x - m_cccmRefArea.x;
+      const_cast<PredictionUnit&>(pu).curCand.corOffY = m_cccmBlkArea.y - m_cccmRefArea.y;
+#else
+      const_cast<PredictionUnit&>(pu).curCand.type = CCP_TYPE_CCCM;
+#endif
+      PU::cccmModelToCcpParams(const_cast<PredictionUnit&>(pu).curCand, cccmModelCb, cccmModelCr, 0
+#if JVET_AB0174_CCCM_DIV_FREE
+                               , m_cccmLumaOffset
+#endif
+                               );
+#else
       xCccmCalcModels(pu, cccmModelCb,  cccmModelCr, 0, 0);
       xCccmApplyModel(pu, COMPONENT_Cb, cccmModelCb, 0, 0, predCb);
       xCccmApplyModel(pu, COMPONENT_Cr, cccmModelCr, 0, 0, predCr);
+#endif
     }
     else
     {
       // Multimode case
       int modelThr = xCccmCalcRefAver(pu);
-      
+#if JVET_AD0188_CCP_MERGE
+      xCccmCalcModels(pu, cccmModelCb[0], cccmModelCr[0], 1, modelThr);
+      xCccmApplyModel(pu, COMPONENT_Cb,   cccmModelCb[0], 1, modelThr, predCb);
+      xCccmApplyModel(pu, COMPONENT_Cr,   cccmModelCr[0], 1, modelThr, predCr);
+
+      xCccmCalcModels(pu, cccmModelCb[1], cccmModelCr[1], 2, modelThr);
+      xCccmApplyModel(pu, COMPONENT_Cb,   cccmModelCb[1], 2, modelThr, predCb);
+      xCccmApplyModel(pu, COMPONENT_Cr,   cccmModelCr[1], 2, modelThr, predCr);
+
+#if JVET_AC0054_GLCCCM
+      const_cast<PredictionUnit&>(pu).curCand.type = ((pu.glCccmFlag ? CCP_TYPE_GLCCCM : CCP_TYPE_CCCM) | CCP_TYPE_MMLM);
+      const_cast<PredictionUnit&>(pu).curCand.corOffX = m_cccmBlkArea.x - m_cccmRefArea.x;
+      const_cast<PredictionUnit&>(pu).curCand.corOffY = m_cccmBlkArea.y - m_cccmRefArea.y;
+#else
+      const_cast<PredictionUnit&>(pu).curCand.type = (CCP_TYPE_CCCM | CCP_TYPE_MMLM);
+#endif
+      PU::cccmModelToCcpParams(const_cast<PredictionUnit &>(pu).curCand, cccmModelCb, cccmModelCr, modelThr
+#if JVET_AB0174_CCCM_DIV_FREE
+                               , m_cccmLumaOffset
+#endif
+                               );
+#else
       xCccmCalcModels(pu, cccmModelCb,  cccmModelCr, 1, modelThr);
       xCccmApplyModel(pu, COMPONENT_Cb, cccmModelCb, 1, modelThr, predCb);
       xCccmApplyModel(pu, COMPONENT_Cr, cccmModelCr, 1, modelThr, predCr);
@@ -12863,6 +15888,7 @@ void IntraPrediction::predIntraCCCM( const PredictionUnit &pu, PelBuf &predCb, P
       xCccmCalcModels(pu, cccmModelCb,  cccmModelCr, 2, modelThr);
       xCccmApplyModel(pu, COMPONENT_Cb, cccmModelCb, 2, modelThr, predCb);
       xCccmApplyModel(pu, COMPONENT_Cr, cccmModelCr, 2, modelThr, predCr);
+#endif
     }
   }
 }
diff --git a/source/Lib/CommonLib/IntraPrediction.h b/source/Lib/CommonLib/IntraPrediction.h
index 1eb31b51d..2c70a4050 100644
--- a/source/Lib/CommonLib/IntraPrediction.h
+++ b/source/Lib/CommonLib/IntraPrediction.h
@@ -470,7 +470,7 @@ public:
   void   xCccmApplyModel          (const PredictionUnit& pu, const ComponentID compId, CccmModel<CCCM_NUM_PARAMS> &cccmModel, int modelId, int modelThr, PelBuf &piPred) const;
   void   xCccmCreateLumaRef       (const PredictionUnit& pu, CompArea chromaArea
 #if JVET_AD0202_CCCM_MDF
-    , int downsFilterIdx
+    , int downsFilterIdx = 0
 #endif
   );
   PelBuf xCccmGetLumaRefBuf       (const PredictionUnit& pu, int &areaWidth, int &areaHeight, int &refSizeX, int &refSizeY, int &refPosPicX, int &refPosPicY
@@ -520,6 +520,45 @@ public:
   void   xCflmCalcRefArea         (const PredictionUnit& pu, const CompArea& chromaArea);
 #endif
 
+#if JVET_AD0188_CCP_MERGE
+  void reorderCCPCandidates(const PredictionUnit &pu, CCPModelCandidate candList[], int reorderlistSize);
+  int  xGetOneCCPCandCost(const PredictionUnit &pu, CCPModelCandidate &ccpCand);
+  void predCCPCandidate(const PredictionUnit &pu, PelBuf &predCb, PelBuf &predCr);
+
+  void xCclmApplyModel(const PredictionUnit &pu, const ComponentID compId, CccmModel<CCCM_NUM_PARAMS> &cccmModel, int modelId, int modelThr, PelBuf &piPred) const;
+  void xCccmApplyModelOffset(const PredictionUnit& pu, const ComponentID compId, const CccmModel<CCCM_NUM_PARAMS>& cccmModel, int modelId, int modelThr, PelBuf& piPred, int lumaOffset, int chromaOffset[2], int type, int refSizeX, int refSizeY) const;
+  void xGlmApplyModelOffset(const PredictionUnit& pu, const ComponentID compId, const CompArea& chromaArea, CccmModel<GLM_NUM_PARAMS>& glmModel, int glmIdc, PelBuf& piPred, int lumaOffset, int chromaOffset) const;
+#if JVET_AD0202_CCCM_MDF
+  void xMFCccmApplyModelOffset1(const PredictionUnit& pu, const ComponentID compId, const CccmModel<CCCM_MULTI_PRED_FILTER_NUM_PARAMS>& cccmModel, int modelId, int modelThr, PelBuf& piPred, int lumaOffset, int chromaOffset[2]) const;
+  void xMFCccmApplyModelOffset23(const PredictionUnit& pu, const ComponentID compId, const CccmModel<CCCM_MULTI_PRED_FILTER_NUM_PARAMS2>& cccmModel, int modelId, int modelThr, PelBuf& piPred, int lumaOffset, int chromaOffset[2]) const;
+#endif
+#if JVET_AC0147_CCCM_NO_SUBSAMPLING
+  void xNSCccmApplyModelOffset(const PredictionUnit& pu, const ComponentID compId, const CccmModel<CCCM_NO_SUB_NUM_PARAMS>& cccmModel, int modelId, int modelThr, PelBuf& piPred, int lumaOffset, int chromaOffset[2]) const;
+#endif
+
+  void xGetUpdatedOffsetCCLM(const PredictionUnit &pu, const ComponentID compID, const CompArea &chromaArea, CclmModel &cclmModel, int modelNum, int glmIdc);
+  void xGetUpdatedOffsetCCCM(const PredictionUnit& pu, const ComponentID compID, int modelNum, const CompArea& chromaArea, CccmModel<CCCM_NUM_PARAMS> cccmModel[2], int modelThr, int lumaOffset, int chromaOffset[2], int type, int refSizeX, int refSizeY);
+  void xGetUpdatedOffsetGLM(const PredictionUnit& pu, const ComponentID compID, const CompArea& chromaArea, CccmModel<GLM_NUM_PARAMS>& glmModel, int glmIdc, int lumaOffset, int& chromaOffset);
+#if JVET_AD0202_CCCM_MDF
+  void xGetUpdatedOffsetMFCCCM1(const PredictionUnit& pu, const ComponentID compID, int modelNum, const CompArea& chromaArea, CccmModel<CCCM_MULTI_PRED_FILTER_NUM_PARAMS> cccmModel[2], int modelThr, int lumaOffset, int chromaOffset[2], int type, int refSizeX, int refSizeY);
+  void xGetUpdatedOffsetMFCCCM23(const PredictionUnit& pu, const ComponentID compID, int modelNum, const CompArea& chromaArea, CccmModel<CCCM_MULTI_PRED_FILTER_NUM_PARAMS2> cccmModel[2], int modelThr, int lumaOffset, int chromaOffset[2], int type, int refSizeX, int refSizeY);
+#endif
+#if JVET_AC0147_CCCM_NO_SUBSAMPLING
+  void xGetUpdatedOffsetNSCCCM(const PredictionUnit& pu, const ComponentID compID, int modelNum, const CompArea& chromaArea, CccmModel<CCCM_NO_SUB_NUM_PARAMS> cccmModel[2], int modelThr, int lumaOffset, int chromaOffset[]);
+#endif
+
+  int xGetCostCCLM(const PredictionUnit &pu, const ComponentID compID, const CompArea &chromaArea, CclmModel &cclmModel, int modelNum, int glmIdc);
+  int xGetCostCCCM(const PredictionUnit &pu, const ComponentID compID, int modelNum, const CompArea &chromaArea, CccmModel<CCCM_NUM_PARAMS> cccmModel[2], int modelThr, int lumaOffset, int chromaOffset[2], int type, int refSizeX, int refSizeY);
+  int xGetCostGLM(const PredictionUnit& pu, const ComponentID compID, const CompArea& chromaArea, CccmModel<GLM_NUM_PARAMS>& glmModel, int glmIdc, int lumaOffset, int& chromaOffset);
+#if JVET_AD0202_CCCM_MDF
+  int xGetCostMFCCCM1(const PredictionUnit& pu, const ComponentID compID, int modelNum, const CompArea& chromaArea, CccmModel<CCCM_MULTI_PRED_FILTER_NUM_PARAMS> cccmModel[2], int modelThr, int lumaOffset, int chromaOffset[2], int type, int refSizeX, int refSizeY);
+  int xGetCostMFCCCM23(const PredictionUnit& pu, const ComponentID compID, int modelNum, const CompArea& chromaArea, CccmModel<CCCM_MULTI_PRED_FILTER_NUM_PARAMS2> cccmModel[2], int modelThr, int lumaOffset, int chromaOffset[2], int type, int refSizeX, int refSizeY);
+#endif
+#if JVET_AC0147_CCCM_NO_SUBSAMPLING
+  int xGetCostNSCCCM(const PredictionUnit& pu, const ComponentID compID, int modelNum, const CompArea& chromaArea, CccmModel<CCCM_NO_SUB_NUM_PARAMS> cccmModel[2], int modelThr, int lumaOffset, int chromaOffset[]);
+#endif
+#endif
+
 #if ENABLE_DIMD
 #if JVET_AC0115_INTRA_TMP_DIMD_MTS_LFNST
   static int deriveDimdIntraTmpModePred(const CodingUnit cu, CPelBuf predBuf); // using prediction samples
diff --git a/source/Lib/CommonLib/Slice.h b/source/Lib/CommonLib/Slice.h
index 1c0bb5a6f..317e25736 100644
--- a/source/Lib/CommonLib/Slice.h
+++ b/source/Lib/CommonLib/Slice.h
@@ -1767,6 +1767,9 @@ private:
 #if JVET_AC0147_CCCM_NO_SUBSAMPLING
   int               m_cccm;
 #endif
+#if JVET_AD0188_CCP_MERGE
+  bool              m_ccpMerge;
+#endif
 #if JVET_V0130_INTRA_TMP
   bool              m_intraTMP;                                       ///< intra Template Matching 
   unsigned          m_intraTmpMaxSize;                               ///< max CU size for which intra TMP is allowed
@@ -2380,7 +2383,10 @@ void                    setCCALFEnabledFlag( bool b )
   void      setUseCccm( int i )                                                  { m_cccm = i; }
   int       getUseCccm()                                               const     { return m_cccm; }
 #endif
-
+#if JVET_AD0188_CCP_MERGE
+  void      setUseCcpMerge     ( bool i )                                        { m_ccpMerge = i; }
+  bool      getUseCcpMerge     ()                                      const     { return m_ccpMerge; }
+#endif
 #if ENABLE_OBMC
   void      setUseOBMC         ( bool b )                                        { m_OBMC = b; }
   bool      getUseOBMC         ()                                      const     { return m_OBMC; }
diff --git a/source/Lib/CommonLib/TypeDef.h b/source/Lib/CommonLib/TypeDef.h
index 58efef890..69c4c639e 100644
--- a/source/Lib/CommonLib/TypeDef.h
+++ b/source/Lib/CommonLib/TypeDef.h
@@ -181,6 +181,7 @@
 #define JVET_AC0105_DIRECTIONAL_PLANAR                    1 // JVET-AC0105: Directional planar
 #define JVET_AD0184_REMOVAL_OF_DIVISION_OPERATIONS        1 // JVET-AD0184: Removal of division operations
 #define JVET_AD0085_MPM_SORTING                           1 // JVET-AD0085: Template-based intra MPM list construction
+#define JVET_AD0188_CCP_MERGE                             1 // JVET_AD0188: Non-local cross-component prediction and cross-component merge mode
 
 //IBC
 #define JVET_Y0058_IBC_LIST_MODIFY                        1 // JVET-Y0058: Modifications of IBC merge/AMVP list construction, ARMC-TM-IBC part is included under JVET_W0090_ARMC_TM
diff --git a/source/Lib/CommonLib/Unit.cpp b/source/Lib/CommonLib/Unit.cpp
index 8538bca2b..714499db0 100644
--- a/source/Lib/CommonLib/Unit.cpp
+++ b/source/Lib/CommonLib/Unit.cpp
@@ -887,6 +887,11 @@ void PredictionUnit::initData()
 #if JVET_AD0202_CCCM_MDF
   cccmMultiFilterIdx = 0;
 #endif
+#endif
+#if JVET_AD0188_CCP_MERGE
+  idxNonLocalCCP = 0;
+  curCand = {};
+  curCand.type = CCP_TYPE_NONE;
 #endif
   // inter data
 #if JVET_AC0185_ENHANCED_TEMPORAL_MOTION_DERIVATION
@@ -1044,6 +1049,10 @@ PredictionUnit& PredictionUnit::operator=(const IntraPredictionData& predData)
 #if JVET_AD0202_CCCM_MDF
   cccmMultiFilterIdx = predData.cccmMultiFilterIdx;
 #endif
+#endif
+#if JVET_AD0188_CCP_MERGE
+  idxNonLocalCCP  = predData.idxNonLocalCCP;
+  curCand = predData.curCand;
 #endif
   return *this;
 }
@@ -1201,6 +1210,10 @@ PredictionUnit& PredictionUnit::operator=( const PredictionUnit& other )
 #if JVET_AD0202_CCCM_MDF
   cccmMultiFilterIdx = other.cccmMultiFilterIdx;
 #endif
+#endif
+#if JVET_AD0188_CCP_MERGE
+  idxNonLocalCCP  = other.idxNonLocalCCP;
+  curCand = other.curCand;
 #endif
 
   mergeFlag   = other.mergeFlag;
diff --git a/source/Lib/CommonLib/Unit.h b/source/Lib/CommonLib/Unit.h
index df31788a5..38939c544 100644
--- a/source/Lib/CommonLib/Unit.h
+++ b/source/Lib/CommonLib/Unit.h
@@ -539,6 +539,10 @@ struct IntraPredictionData
   int       cccmMultiFilterIdx;
 #endif
 #endif
+#if JVET_AD0188_CCP_MERGE
+  int       idxNonLocalCCP;
+  CCPModelCandidate curCand;
+#endif
 };
 
 struct InterPredictionData
diff --git a/source/Lib/CommonLib/UnitTools.cpp b/source/Lib/CommonLib/UnitTools.cpp
index d91f1cd15..34f5384bb 100644
--- a/source/Lib/CommonLib/UnitTools.cpp
+++ b/source/Lib/CommonLib/UnitTools.cpp
@@ -2554,6 +2554,468 @@ bool PU::allowMPMSorted(const PredictionUnit& pu)
 }
 #endif
 
+#if JVET_AD0188_CCP_MERGE
+bool PU::hasNonLocalCCP(const PredictionUnit &pu)
+{
+  if (!pu.cs->sps->getUseLMChroma() || !pu.cu->slice->getSPS()->getUseCcpMerge())
+  {
+    return false;
+  }
+  if (pu.cu->ispMode && !CS::isDualITree(*pu.cs))
+  {
+    return false;
+  }
+  if (pu.chromaSize().width * pu.chromaSize().height <= 16)
+  {
+    return false;
+  }
+  return true;
+}
+
+const PredictionUnit *PU::getPUFromPos(const PredictionUnit &pu, const ChannelType &chType, const Position &refPos)
+{
+  const CodingStructure &cs = *pu.cs;
+
+  if (!cs.isDecomp(refPos, chType))
+  {
+    return nullptr;
+  }
+
+  return cs.getPURestricted(refPos, pu, chType);
+}
+
+int PU::getCCPModelCandidateList(const PredictionUnit &pu, CCPModelCandidate candList[], int selIdx)
+{
+  int maxCandIdx = 0;
+  bool    found1stCCLM = false;
+  int64_t scaleCclm[2] = { 0 };
+  int     shiftCclm[2] = { 3 };
+
+  int iW = pu.blocks[1].width;
+  int iH = pu.blocks[1].height;
+
+  auto tryToAddOnePU = [&](const PredictionUnit *puRef)
+  {
+    candList[maxCandIdx] = puRef->curCand;
+    
+    for (int j = 0; j < maxCandIdx; j++)
+    {
+      if (candList[maxCandIdx] == candList[j])
+      {
+        return false;
+      }
+    }
+    return true;
+  };
+
+  const Position posCand[5] = {
+    pu.chromaPos().offset(-1, iH - 1),
+    pu.chromaPos().offset(iW - 1, -1),
+    pu.chromaPos().offset(-1, iH),
+    pu.chromaPos().offset(iW, -1),
+    pu.chromaPos().offset(-1, -1)
+  };
+
+  for (const Position &posLT : posCand)
+  {
+    const PredictionUnit* puRef = getPUFromPos(pu, CHANNEL_TYPE_CHROMA, posLT);
+    if (puRef != nullptr && puRef->curCand.type > 0)
+    {
+      if (!tryToAddOnePU(puRef))
+      {
+        continue;
+      }
+      if (!found1stCCLM && candList[maxCandIdx].type == CCP_TYPE_CCLM)
+      {
+        scaleCclm[0] = candList[maxCandIdx].params[0][0];
+        shiftCclm[0] = candList[maxCandIdx].shift[0];
+        scaleCclm[1] = candList[maxCandIdx].params[1][0];
+        shiftCclm[1] = candList[maxCandIdx].shift[1];
+        found1stCCLM = true;
+      }
+      maxCandIdx++;
+      if (maxCandIdx == MAX_CCP_CAND_LIST_SIZE)
+      {
+        return maxCandIdx;
+      }
+    }
+  }
+
+  int offsetX = 0;  int offsetY = 0;
+  int offsetX0 = 0; int offsetX1 = 0; int offsetX2 = pu.chType == CHANNEL_TYPE_LUMA ? pu.Y().width >> 1 : pu.Cb().width >> 1;
+  int offsetY0 = 0; int offsetY1 = 0; int offsetY2 = pu.chType == CHANNEL_TYPE_LUMA ? pu.Y().height >> 1 : pu.Cb().height >> 1;
+
+  const int horNAInterval = std::max((int)(pu.chType == CHANNEL_TYPE_LUMA ? pu.Y().width * 2 : pu.Cb().width * 2) >> 1, 4);
+  const int verNAInterval = std::max((int)(pu.chType == CHANNEL_TYPE_LUMA ? pu.Y().height * 2 : pu.Cb().height * 2) >> 1, 4);
+  const int numNACandidate[7] = { 5, 9, 9, 9, 9, 9, 9 };
+  const int idxMap[7][9] = {
+    { 0, 1, 2, 3, 4 },
+    { 0, 1, 2, 3, 4, 5, 6, 7, 8 },
+    { 0, 1, 2, 3, 4, 5, 6, 7, 8 },
+    { 0, 1, 2, 3, 4, 5, 6, 7, 8 },
+    { 0, 1, 2, 3, 4, 5, 6, 7, 8 },
+    { 0, 1, 2, 3, 4, 5, 6, 7, 8 },
+    { 0, 1, 2, 3, 4, 5, 6, 7, 8 }
+  };
+
+  for (int iDistanceIndex = 0; iDistanceIndex < 7 && maxCandIdx < MAX_CCP_CAND_LIST_SIZE; iDistanceIndex++)
+  {
+    const int iNADistanceHor = horNAInterval * (iDistanceIndex + 1);
+    const int iNADistanceVer = verNAInterval * (iDistanceIndex + 1);
+
+    for (int naspIdx = 0; naspIdx < numNACandidate[iDistanceIndex] && maxCandIdx < MAX_CCP_CAND_LIST_SIZE; naspIdx++)
+    {
+      switch (idxMap[iDistanceIndex][naspIdx])
+      {
+      case 0: offsetX = offsetX0 = -iNADistanceHor - 1;                  offsetY = offsetY0 = verNAInterval + iNADistanceVer - 1;  break;
+      case 1: offsetX = offsetX1 = horNAInterval + iNADistanceHor - 1;  offsetY = offsetY1 = -iNADistanceVer - 1;                  break;
+      case 2: offsetX = offsetX2;       offsetY = offsetY1;       break;
+      case 3: offsetX = offsetX0;       offsetY = offsetY2;       break;
+      case 4: offsetX = offsetX0;       offsetY = offsetY1;       break;
+      case 5: offsetX = -1;             offsetY = offsetY0;       break;
+      case 6: offsetX = offsetX1;       offsetY = -1;             break;
+      case 7: offsetX = offsetX0 >> 1;  offsetY = offsetY0;       break;
+      case 8: offsetX = offsetX1;       offsetY = offsetY1 >> 1;  break;
+      default: printf("error!"); exit(0); break;
+      }
+
+      Position posLT(pu.chromaPos().x + offsetX, pu.chromaPos().y + offsetY);
+
+      const PredictionUnit *puRef = getPUFromPos(pu, CHANNEL_TYPE_CHROMA, posLT);
+
+      if (puRef != nullptr && puRef->curCand.type > 0)
+      {
+        if (!tryToAddOnePU(puRef))
+        {
+          continue;
+        }
+        if (!found1stCCLM && candList[maxCandIdx].type == CCP_TYPE_CCLM)
+        {
+          scaleCclm[0] = candList[maxCandIdx].params[0][0];
+          shiftCclm[0] = candList[maxCandIdx].shift[0];
+          scaleCclm[1] = candList[maxCandIdx].params[1][0];
+          shiftCclm[1] = candList[maxCandIdx].shift[1];
+          found1stCCLM = true;
+        }
+        maxCandIdx++;
+        if (maxCandIdx == MAX_CCP_CAND_LIST_SIZE)
+        {
+          return maxCandIdx;
+        }
+      }
+    }
+  }
+
+  // Non-adjacent candidates round 2
+  const int numNACandidate2[7] = { 4, 4, 4, 4, 4, 4, 4 };
+  const int idxMap2[7][5]        = { { 0, 1, 2, 3 }, { 0, 1, 2, 3 }, { 0, 1, 2, 3 }, { 0, 1, 2, 3 },
+                                     { 0, 1, 2, 3 }, { 0, 1, 2, 3 }, { 0, 1, 2, 3 } };
+
+  for (int iDistanceIndex = 0; iDistanceIndex < 7 && maxCandIdx < MAX_CCP_CAND_LIST_SIZE; iDistanceIndex++)
+  {
+    const int horNADistance = horNAInterval * (iDistanceIndex + 1);
+    const int verNADistance = verNAInterval * (iDistanceIndex + 1);
+
+    for (int naspIdx = 0; naspIdx < numNACandidate2[iDistanceIndex] && maxCandIdx < MAX_CCP_CAND_LIST_SIZE; naspIdx++)
+    {
+      switch (idxMap2[iDistanceIndex][naspIdx])
+      {
+      case 0: offsetX = offsetX0 = -horNADistance - 1;          offsetY = offsetY2 + ((verNAInterval + verNADistance - 1 - offsetY2) >> 1); break;
+      case 1: offsetX = offsetX2 + ((horNAInterval + horNADistance - 1 - offsetX2) >> 1); offsetY = offsetY0 = -verNADistance - 1; break;
+      case 2: offsetX = offsetX0;                                offsetY = offsetY0 + ((offsetY2 - offsetY0) >> 1); break;
+      case 3: offsetX = offsetX0 + ((offsetX2 - offsetX0) >> 1); offsetY = offsetY0; break;
+      default: printf("error!"); exit(0); break;
+      }
+
+      Position posLT(pu.chromaPos().x + offsetX, pu.chromaPos().y + offsetY);
+
+      const PredictionUnit *puRef = getPUFromPos(pu, CHANNEL_TYPE_CHROMA, posLT);
+
+      if (puRef != nullptr && puRef->curCand.type > 0)
+      {
+        if (!tryToAddOnePU(puRef))
+        {
+          continue;
+        }
+        if (!found1stCCLM && candList[maxCandIdx].type == CCP_TYPE_CCLM)
+        {
+          scaleCclm[0] = candList[maxCandIdx].params[0][0];
+          shiftCclm[0] = candList[maxCandIdx].shift[0];
+          scaleCclm[1] = candList[maxCandIdx].params[1][0];
+          shiftCclm[1] = candList[maxCandIdx].shift[1];
+          found1stCCLM = true;
+        }
+        maxCandIdx++;
+        if (maxCandIdx == MAX_CCP_CAND_LIST_SIZE)
+        {
+          return maxCandIdx;
+        }
+      }
+    }
+  }
+
+  auto tryHistCCP = [&](const LutCCP &ccpLut)
+  {
+    for (int idx = 0; idx < ccpLut.lutCCP.size(); idx++)
+    {
+      CCPModelCandidate curModel;
+      pu.cs->getOneModelFromCCPLut(ccpLut.lutCCP, curModel, idx);
+      candList[maxCandIdx] = curModel;
+      bool duplication = false;
+      for (int j = 0; j < maxCandIdx; j++)
+      {
+        if (candList[maxCandIdx] == candList[j])
+        {
+          duplication = true;
+          // THROW("Should not duplicaten");
+          break;
+        }
+      }
+      if (duplication)
+      {
+        continue;
+      }
+      if (!found1stCCLM && candList[maxCandIdx].type == CCP_TYPE_CCLM)
+      {
+        scaleCclm[0] = candList[maxCandIdx].params[0][0];
+        shiftCclm[0] = candList[maxCandIdx].shift[0];
+        scaleCclm[1] = candList[maxCandIdx].params[1][0];
+        shiftCclm[1] = candList[maxCandIdx].shift[1];
+        found1stCCLM = true;
+      }
+      maxCandIdx++;
+      if (maxCandIdx == MAX_CCP_CAND_LIST_SIZE)
+      {
+        return maxCandIdx;
+      }
+    }
+    return -1;
+  };
+
+  int ret = tryHistCCP(pu.cs->ccpLut);
+  if (ret != -1)
+  {
+    return ret;
+  }
+
+  if (maxCandIdx < MAX_CCP_CAND_LIST_SIZE)
+  {
+    unsigned uiInternalBitDepth = pu.cs->sps->getBitDepth(CHANNEL_TYPE_CHROMA);
+    const int defaultA[MAX_CCP_CAND_LIST_SIZE] = { 0, 1, -1, 2, -2, 3, -3, 4, -4, 5, -5, 6 };
+    const int defaultB = 1 << (uiInternalBitDepth - 1);
+    const int defaultShift = 3;
+
+    for (int posIdx = 0; posIdx < MAX_CCP_CAND_LIST_SIZE; posIdx++)
+    {
+      CCPModelCandidate curModel;
+      curModel.type         = CCP_TYPE_CCLM;
+      curModel.params[0][0] = scaleCclm[0];
+      curModel.params[0][1] = defaultB;
+      curModel.shift[0]     = shiftCclm[0];
+      curModel.params[1][0] = scaleCclm[1];
+      curModel.params[1][1] = defaultB;
+      curModel.shift[1]     = shiftCclm[1];
+
+      if (found1stCCLM && defaultA[posIdx])
+      {
+        int dCb = curModel.params[0][0] > 0 ? -defaultA[posIdx] : defaultA[posIdx];
+        if (curModel.shift[0] < defaultShift)
+        {
+          curModel.params[0][0] <<= (defaultShift - curModel.shift[0]);
+          curModel.shift[0] = defaultShift;
+        }
+        else if (curModel.shift[0] > defaultShift)
+        {
+          dCb <<= (curModel.shift[0] - defaultShift);
+        }
+        curModel.params[0][0] += dCb;
+
+        int dCr = curModel.params[1][0] > 0 ? -defaultA[posIdx] : defaultA[posIdx];
+        if (curModel.shift[1] < defaultShift)
+        {
+          curModel.params[1][0] <<= (defaultShift - curModel.shift[1]);
+          curModel.shift[1] = defaultShift;
+        }
+        else if (curModel.shift[1] > defaultShift)
+        {
+          dCr <<= (curModel.shift[1] - defaultShift);
+        }
+        curModel.params[1][0] += dCr;
+      }
+      else
+      {
+        curModel.params[0][0] = curModel.params[1][0] = defaultA[posIdx];
+        curModel.shift[0] = curModel.shift[1] = defaultShift;
+      }
+
+      bool duplication = false;
+      for (int j = 0; j < maxCandIdx; j++)
+      {
+        if (candList[j] == curModel)
+        {
+          duplication = true;
+          // THROW("Should not duplicaten");
+          break;
+        }
+      }
+      if (duplication)
+      {
+        continue;
+      }
+      candList[maxCandIdx] = curModel;
+      if (selIdx == maxCandIdx)
+      {
+        return maxCandIdx + 1;
+      }
+      maxCandIdx++;
+      if (maxCandIdx == MAX_CCP_CAND_LIST_SIZE)
+      {
+        return maxCandIdx;
+      }
+    }
+  }
+
+  CHECK(maxCandIdx > MAX_CCP_CAND_LIST_SIZE, "Invlid number of non-adj CCCM candidates");
+  return maxCandIdx;
+}
+
+void CU::saveModelsInHCCP(const CodingUnit &cu)
+{
+  bool lumaUsesISP = !CS::isDualITree(*cu.cs) && cu.ispMode;
+  if (cu.chromaFormat == CHROMA_400 || (CS::isDualITree(*cu.cs) && cu.chType == CHANNEL_TYPE_LUMA) || !CU::isIntra(cu))
+  {
+    return;
+  }
+  if (lumaUsesISP)
+  {
+    return;
+  }
+  const PredictionUnit &pu = *cu.firstPU;
+  CodingStructure      &cs = *cu.cs;
+
+  if (PU::isLMCMode(pu.intraDir[1]) && pu.curCand.type != CCP_TYPE_NONE)
+  {
+    cs.addCCPToLut(cs.ccpLut.lutCCP, pu.curCand, -1);
+  }
+}
+
+void PU::ccpParamsToCclmModel(const ComponentID compId, const CCPModelCandidate& params, CclmModel& cclmModel)
+{
+  cclmModel.a = int(params.params[compId - 1][0]);
+  cclmModel.b = int(params.params[compId - 1][1]);
+  cclmModel.shift = params.shift[compId - 1];
+#if MMLM
+  if (params.type & CCP_TYPE_MMLM)
+  {
+    cclmModel.a2 = int(params.params2[compId - 1][0]);
+    cclmModel.b2 = int(params.params2[compId - 1][1]);
+    cclmModel.shift2 = params.shift2[compId - 1];
+    cclmModel.yThres = params.yThres;
+  }
+#endif
+}
+
+void PU::cclmModelToCcpParams(const ComponentID compId, CCPModelCandidate& params, const CclmModel& cclmModel)
+{
+  params.params[compId - 1][0] = cclmModel.a;
+  params.params[compId - 1][1] = cclmModel.b;
+  params.shift[compId - 1] = cclmModel.shift;
+#if MMLM
+  params.params2[compId - 1][0] = cclmModel.a2;
+  params.params2[compId - 1][1] = cclmModel.b2;
+  params.shift2[compId - 1] = cclmModel.shift2;
+  params.yThres = cclmModel.yThres;
+#endif
+}
+
+template <int NUM>
+#if JVET_AB0174_CCCM_DIV_FREE
+void PU::cccmModelToCcpParams(CCPModelCandidate& params, const CccmModel<NUM> cccmModelCb[2], const CccmModel<NUM> cccmModelCr[2], const int yThres, const int cccmLumaOffset)
+#else
+void PU::cccmModelToCcpParams(CCPModelCandidate& params, const CccmModel<NUM> cccmModelCb[2], const CccmModel<NUM> cccmModelCr[2], const int yThres)
+#endif
+{
+  std::memcpy(params.params[0], cccmModelCb[0].params, sizeof(TCccmCoeff) * NUM);
+  std::memcpy(params.params[1], cccmModelCr[0].params, sizeof(TCccmCoeff) * NUM);
+  params.midVal = cccmModelCb[0].midVal;
+  params.bd = cccmModelCb[0].bd;
+#if JVET_AB0174_CCCM_DIV_FREE
+  params.lumaOffset = cccmLumaOffset;
+#endif
+#if MMLM
+  std::memcpy(params.params2[0], cccmModelCb[1].params, sizeof(TCccmCoeff) * NUM);
+  std::memcpy(params.params2[1], cccmModelCr[1].params, sizeof(TCccmCoeff) * NUM);
+  params.yThres = yThres;
+#endif
+}
+
+template<int NUM>
+void PU::ccpParamsToCccmModel(const CCPModelCandidate& params, CccmModel<NUM> cccmModelCb[2], CccmModel<NUM> cccmModelCr[2])
+{
+  std::memcpy(cccmModelCb[0].params, params.params[0], sizeof(TCccmCoeff) * NUM);
+  std::memcpy(cccmModelCr[0].params, params.params[1], sizeof(TCccmCoeff) * NUM);
+  cccmModelCb[0].midVal = cccmModelCr[0].midVal = params.midVal;
+  cccmModelCb[0].bd = cccmModelCr[0].bd = params.bd;
+#if MMLM
+  if (params.type & CCP_TYPE_MMLM)
+  {
+    std::memcpy(cccmModelCb[1].params, params.params2[0], sizeof(TCccmCoeff) * NUM);
+    std::memcpy(cccmModelCr[1].params, params.params2[1], sizeof(TCccmCoeff) * NUM);
+    cccmModelCb[1].midVal = cccmModelCr[1].midVal = params.midVal;
+    cccmModelCb[1].bd = cccmModelCr[1].bd = params.bd;
+  }
+#endif
+}
+
+#if JVET_AB0174_CCCM_DIV_FREE
+template void PU::cccmModelToCcpParams(CCPModelCandidate& params, const CccmModel<CCCM_NUM_PARAMS> cccmModelCb[2], const CccmModel<CCCM_NUM_PARAMS> cccmModelCr[2], const int yThres, const int cccmLumaOffset);
+#else
+template void PU::cccmModelToCcpParams(CCPModelCandidate& params, const CccmModel<CCCM_NUM_PARAMS> cccmModelCb[2], const CccmModel<CCCM_NUM_PARAMS> cccmModelCr[2], const int yThres);
+#endif
+template void PU::ccpParamsToCccmModel(const CCPModelCandidate& params, CccmModel<CCCM_NUM_PARAMS> cccmModelCb[2], CccmModel<CCCM_NUM_PARAMS> cccmModelCr[2]);
+
+#if JVET_AC0147_CCCM_NO_SUBSAMPLING
+#if JVET_AB0174_CCCM_DIV_FREE
+template void PU::cccmModelToCcpParams(CCPModelCandidate& params, const CccmModel<CCCM_NO_SUB_NUM_PARAMS> cccmModelCb[2], const CccmModel<CCCM_NO_SUB_NUM_PARAMS> cccmModelCr[2], const int yThres, const int cccmLumaOffset);
+#else
+template void PU::cccmModelToCcpParams(CCPModelCandidate &params, const CccmModel<CCCM_NO_SUB_NUM_PARAMS> cccmModelCb[2], const CccmModel<CCCM_NO_SUB_NUM_PARAMS> cccmModelCr[2], const int yThres);
+#endif
+template void PU::ccpParamsToCccmModel(const CCPModelCandidate& params, CccmModel<CCCM_NO_SUB_NUM_PARAMS> cccmModelCb[2], CccmModel<CCCM_NO_SUB_NUM_PARAMS> cccmModelCr[2]);
+#endif
+
+#if JVET_AD0202_CCCM_MDF
+#if JVET_AB0174_CCCM_DIV_FREE
+template void PU::cccmModelToCcpParams(CCPModelCandidate& params, const CccmModel<CCCM_MULTI_PRED_FILTER_NUM_PARAMS> cccmModelCb[2], const CccmModel<CCCM_MULTI_PRED_FILTER_NUM_PARAMS> cccmModelCr[2], const int yThres, const int cccmLumaOffset);
+#else
+template void PU::cccmModelToCcpParams(CCPModelCandidate& params, const CccmModel<CCCM_MULTI_PRED_FILTER_NUM_PARAMS> cccmModelCb[2], const CccmModel<CCCM_MULTI_PRED_FILTER_NUM_PARAMS> cccmModelCr[2], const int yThres);
+#endif
+template void PU::ccpParamsToCccmModel(const CCPModelCandidate& params, CccmModel<CCCM_MULTI_PRED_FILTER_NUM_PARAMS> cccmModelCb[2], CccmModel<CCCM_MULTI_PRED_FILTER_NUM_PARAMS> cccmModelCr[2]);
+#endif
+
+#if JVET_AB0092_GLM_WITH_LUMA
+#if JVET_AB0174_CCCM_DIV_FREE
+void PU::glmModelToCcpParams(const ComponentID compId, CCPModelCandidate& params, const CccmModel<GLM_NUM_PARAMS> &glmModel, const int lumaOffset)
+#else
+void PU::glmModelToCcpParams(const ComponentID compId, CCPModelCandidate& params, const CccmModel<GLM_NUM_PARAMS> &glmModel)
+#endif
+{
+  std::memcpy(params.params[compId - 1], glmModel.params, sizeof(TCccmCoeff) * GLM_NUM_PARAMS);
+  params.midVal = glmModel.midVal;
+  params.bd = glmModel.bd;
+#if JVET_AB0174_CCCM_DIV_FREE
+  params.lumaOffset = lumaOffset;
+#endif
+}
+
+void PU::ccpParamsToGlmModel(const ComponentID compId, const CCPModelCandidate& params, CccmModel<GLM_NUM_PARAMS> &glmModel)
+{
+  std::memcpy(glmModel.params, params.params[compId - 1], sizeof(TCccmCoeff) * GLM_NUM_PARAMS);
+  glmModel.midVal = params.midVal;
+  glmModel.bd = params.bd;
+}
+#endif
+#endif
+
 #if JVET_AB0155_SGPM
 bool PU::isSgpm(const PredictionUnit &pu, const ChannelType &chType)
 {
diff --git a/source/Lib/CommonLib/UnitTools.h b/source/Lib/CommonLib/UnitTools.h
index 12d0140ec..8a77ccc3a 100644
--- a/source/Lib/CommonLib/UnitTools.h
+++ b/source/Lib/CommonLib/UnitTools.h
@@ -79,6 +79,9 @@ namespace CU
   void addPUs                         (      CodingUnit& cu);
 
   void saveMotionInHMVP               (const CodingUnit& cu, const bool isToBeDone );
+#if JVET_AD0188_CCP_MERGE
+  void saveModelsInHCCP               (const CodingUnit &cu);
+#endif
 
   PartSplit getSplitAtDepth           (const CodingUnit& cu, const unsigned depth);
 #if !INTRA_RM_SMALL_BLOCK_SIZE_CONSTRAINTS
@@ -673,6 +676,32 @@ namespace PU
 #if JVET_AD0085_MPM_SORTING
   bool allowMPMSorted(const PredictionUnit& pu);
 #endif
+#if JVET_AD0188_CCP_MERGE
+  void ccpParamsToCclmModel(const ComponentID compID, const CCPModelCandidate& params, CclmModel& cclmModel);
+  void cclmModelToCcpParams(const ComponentID compId, CCPModelCandidate& params, const CclmModel& cclmModel);
+
+  template <int NUM>
+#if JVET_AB0174_CCCM_DIV_FREE
+  void cccmModelToCcpParams(CCPModelCandidate& params, const CccmModel<NUM> cccmModelCb[2], const CccmModel<NUM> cccmModelCr[2], const int yThres = 0, const int cccmLumaOffset = 0);
+#else
+  void cccmModelToCcpParams(CCPModelCandidate& params, const CccmModel<NUM> cccmModelCb[2], const CccmModel<NUM> cccmModelCr[2], const int yThres = 0);
+#endif
+  template<int NUM>
+  void ccpParamsToCccmModel(const CCPModelCandidate& params, CccmModel<NUM> cccmModelCb[2], CccmModel<NUM> cccmModelCr[2]);
+
+#if JVET_AB0092_GLM_WITH_LUMA
+#if JVET_AB0174_CCCM_DIV_FREE
+  void glmModelToCcpParams(const ComponentID compId, CCPModelCandidate& params, const CccmModel<GLM_NUM_PARAMS> &glmModel, const int lumaOffset);
+#else
+  void glmModelToCcpParams(const ComponentID compId, CCPModelCandidate& params, const CccmModel<GLM_NUM_PARAMS> &glmModel);
+#endif
+  void ccpParamsToGlmModel(const ComponentID compId, const CCPModelCandidate& params, CccmModel<GLM_NUM_PARAMS> &glmModel);
+#endif
+
+  const PredictionUnit *getPUFromPos(const PredictionUnit &pu, const ChannelType &chType, const Position &refPos);
+  bool  hasNonLocalCCP(const PredictionUnit &pu);
+  int   getCCPModelCandidateList(const PredictionUnit &pu, CCPModelCandidate candList[], int selIdx = -1);
+#endif
 }
 
 // TU tools
diff --git a/source/Lib/DecoderLib/CABACReader.cpp b/source/Lib/DecoderLib/CABACReader.cpp
index 998ecef33..a63eeae09 100644
--- a/source/Lib/DecoderLib/CABACReader.cpp
+++ b/source/Lib/DecoderLib/CABACReader.cpp
@@ -2556,8 +2556,34 @@ void CABACReader::glmIdc(PredictionUnit& pu)
   }
 }
 #endif
+
+#if JVET_AD0188_CCP_MERGE
+void CABACReader::nonLocalCCPIndex(PredictionUnit &pu)
+{
+  pu.idxNonLocalCCP = 0;
+
+  if (PU::hasNonLocalCCP(pu))
+  {
+    pu.idxNonLocalCCP = m_BinDecoder.decodeBin(Ctx::nonLocalCCP(0));
+    if (pu.idxNonLocalCCP)
+    {
+      pu.idxNonLocalCCP += unary_max_eqprob(MAX_CCP_CAND_LIST_SIZE - 1);
+      pu.cccmFlag    = 0;
+      pu.intraDir[1] = LM_CHROMA_IDX;
+    }
+  }
+}
+#endif
+
 bool CABACReader::intra_chroma_lmc_mode(PredictionUnit& pu)
 {
+#if JVET_AD0188_CCP_MERGE
+  nonLocalCCPIndex(pu);
+  if (pu.idxNonLocalCCP)
+  {
+    return true;
+  }
+#endif
 #if MMLM
   int lmModeList[NUM_CHROMA_MODE];
 #else 
diff --git a/source/Lib/DecoderLib/CABACReader.h b/source/Lib/DecoderLib/CABACReader.h
index 6bc5d81a2..55cd6c300 100644
--- a/source/Lib/DecoderLib/CABACReader.h
+++ b/source/Lib/DecoderLib/CABACReader.h
@@ -131,6 +131,9 @@ public:
 #if JVET_AA0057_CCCM
   void        cccmFlag                  ( PredictionUnit&               pu );
 #endif
+#if JVET_AD0188_CCP_MERGE
+  void        nonLocalCCPIndex          ( PredictionUnit&               pu );
+#endif
 #if ENABLE_DIMD
   void        cu_dimd_flag              (CodingUnit&                   cu);
  #endif
diff --git a/source/Lib/DecoderLib/DecCu.cpp b/source/Lib/DecoderLib/DecCu.cpp
index 11335d1a9..18166a2ce 100644
--- a/source/Lib/DecoderLib/DecCu.cpp
+++ b/source/Lib/DecoderLib/DecCu.cpp
@@ -510,6 +510,9 @@ void DecCu::decompressCtu( CodingStructure& cs, const UnitArea& ctuArea )
 #endif
 #endif
         xReconIntraQT( currCU );
+#if JVET_AD0188_CCP_MERGE
+        CU::saveModelsInHCCP(currCU);
+#endif
         break;
       default:
         THROW( "Invalid prediction mode" );
@@ -633,6 +636,99 @@ void DecCu::xIntraRecBlk( TransformUnit& tu, const ComponentID compID )
   }
 
   //===== get prediction signal =====
+#if JVET_AD0188_CCP_MERGE
+  if (compID != COMPONENT_Y && pu.idxNonLocalCCP)
+  {
+    if (compID == COMPONENT_Cb)
+    {
+      PelBuf            predCr = cs.getPredBuf(tu.blocks[COMPONENT_Cr]);
+      CCPModelCandidate candList[MAX_CCP_CAND_LIST_SIZE];
+
+      int candIdx = pu.idxNonLocalCCP - 1;
+
+      int candNum         = PU::getCCPModelCandidateList(pu, candList);
+      bool hasFilteredCCCM   = false;
+      bool hasFilteredCCLM   = false;
+      bool hasFilteredNSCCCM = false;
+      bool hasFilteredGLM[8] = { false, false, false, false, false, false, false, false};
+#if JVET_AD0202_CCCM_MDF
+      bool hasFilteredMFCCCM = false;
+#endif
+
+      for (int i = 0; i < candNum; i++)
+      {
+        if (candList[i].type & (CCP_TYPE_CCCM | CCP_TYPE_GLCCCM))
+        {
+          if (!hasFilteredCCCM)
+          {
+            m_pcIntraPred->xCccmCreateLumaRef(pu, area);
+            hasFilteredCCCM = true;
+          }
+        }
+#if JVET_AD0202_CCCM_MDF
+        else if (candList[i].type & CCP_TYPE_MDFCCCM)
+        {
+          if (!hasFilteredMFCCCM)
+          {
+            pu.cccmMultiFilterIdx = candList[i].cccmMultiFilterIdx;
+            if (!hasFilteredCCCM)
+            {
+              m_pcIntraPred->xCccmCreateLumaRef(pu, area, 0);
+              hasFilteredCCCM = true;
+            }
+            m_pcIntraPred->xCccmCreateLumaRef(pu, area, 1);
+            m_pcIntraPred->xCccmCreateLumaRef(pu, area, 2);
+            m_pcIntraPred->xCccmCreateLumaRef(pu, area, 3);
+            pu.cccmMultiFilterIdx = 0;
+            hasFilteredMFCCCM = true;
+          }
+        }
+#endif
+#if JVET_AC0147_CCCM_NO_SUBSAMPLING
+        else if (candList[i].type & CCP_TYPE_NSCCCM)
+        {
+          if (!hasFilteredNSCCCM)
+          {
+            pu.cccmNoSubFlag = 1;
+            m_pcIntraPred->xCccmCreateLumaNoSubRef(pu, area);
+            pu.cccmNoSubFlag = 0;
+            hasFilteredNSCCCM  = true;
+          }
+        }
+#endif
+        else if (candList[i].type & CCP_TYPE_CCLM)
+        {
+          if (!hasFilteredCCLM)
+          {
+            m_pcIntraPred->xGetLumaRecPixels(pu, area);
+            hasFilteredCCLM = true;
+          }
+        }
+        else if (candList[i].type & (CCP_TYPE_GLM0123 | CCP_TYPE_GLM4567))
+        {
+          int filtertype = candList[i].glmIdc - 1;
+          if (!hasFilteredGLM[filtertype])
+          {
+            pu.glmIdc.cr0 = pu.glmIdc.cb0 = filtertype + 1;
+            m_pcIntraPred->xGetLumaRecPixels(pu, area);
+            pu.glmIdc.cr0 = pu.glmIdc.cb0 = 0;
+            hasFilteredGLM[filtertype] = true;
+          }
+        }
+        else
+        {
+          THROW("Invalid type");
+        }
+      }
+      CHECK(pu.idxNonLocalCCP < 1 || pu.idxNonLocalCCP > MAX_CCP_CAND_LIST_SIZE, " Invalid idxNonLocalCCP index");
+
+      m_pcIntraPred->reorderCCPCandidates(pu, candList, candNum);
+      pu.curCand = candList[candIdx];
+      m_pcIntraPred->predCCPCandidate(pu, piPred, predCr);
+    }
+  }
+  else
+#endif
 #if JVET_AA0057_CCCM
   if( compID != COMPONENT_Y && pu.cccmFlag )
   {
@@ -794,7 +890,11 @@ void DecCu::xIntraRecBlk( TransformUnit& tu, const ComponentID compID )
   }
 #if SIGN_PREDICTION
 #if JVET_AA0057_CCCM
+#if JVET_AD0188_CCP_MERGE
+  if (isJCCR && compID == COMPONENT_Cb && !pu.cccmFlag && !pu.idxNonLocalCCP)   // Cr prediction was done already for CCCM
+#else
   if(isJCCR && compID == COMPONENT_Cb && !pu.cccmFlag) // Cr prediction was done already for CCCM
+#endif
 #else
   if(isJCCR && compID == COMPONENT_Cb)
 #endif
diff --git a/source/Lib/DecoderLib/DecSlice.cpp b/source/Lib/DecoderLib/DecSlice.cpp
index f752af445..60a94adc6 100644
--- a/source/Lib/DecoderLib/DecSlice.cpp
+++ b/source/Lib/DecoderLib/DecSlice.cpp
@@ -233,7 +233,11 @@ void DecSlice::decompressSlice( Slice* slice, InputBitstream* bitstream, int deb
 #if JVET_Z0135_TEMP_CABAC_WIN_WEIGHT
   static Ctx storedCtx;
 #endif
-
+#if JVET_AD0188_CCP_MERGE
+  {
+    cs.ccpLut.lutCCP.resize(0);
+  }
+#endif
   unsigned subStrmId = 0;
   for( unsigned ctuIdx = 0; ctuIdx < slice->getNumCtuInSlice(); ctuIdx++ )
   {
@@ -363,7 +367,12 @@ void DecSlice::decompressSlice( Slice* slice, InputBitstream* bitstream, int deb
       cs.resetIBCBuffer = true;
 #endif
     }
-
+#if JVET_AD0188_CCP_MERGE
+    if (ctuXPosInCtus == tileXPosInCtus)
+    {
+      cs.ccpLut.lutCCP.resize(0);
+    }
+#endif
     if( !cs.slice->isIntra() )
     {
       pic->mctsInfo.init( &cs, getCtuAddr( ctuArea.lumaPos(), *( cs.pcv ) ) );
diff --git a/source/Lib/DecoderLib/VLCReader.cpp b/source/Lib/DecoderLib/VLCReader.cpp
index ef815682b..7de36778e 100644
--- a/source/Lib/DecoderLib/VLCReader.cpp
+++ b/source/Lib/DecoderLib/VLCReader.cpp
@@ -2549,6 +2549,9 @@ void HLSyntaxReader::parseSPS(SPS* pcSPS)
 #endif
 #if JVET_AC0147_CCCM_NO_SUBSAMPLING
   READ_UVLC(uiCode, "sps_cccm_cand");                               pcSPS->setUseCccm(uiCode);
+#endif
+#if JVET_AD0188_CCP_MERGE
+  READ_UVLC(uiCode, "sps_ccp_merge");                               pcSPS->setUseCcpMerge(uiCode);
 #endif
   if( pcSPS->getChromaFormatIdc() != CHROMA_400)
   {
diff --git a/source/Lib/EncoderLib/CABACWriter.cpp b/source/Lib/EncoderLib/CABACWriter.cpp
index ec2a72227..95ad5de11 100644
--- a/source/Lib/EncoderLib/CABACWriter.cpp
+++ b/source/Lib/EncoderLib/CABACWriter.cpp
@@ -2279,6 +2279,13 @@ void CABACWriter::glmIdc(const PredictionUnit& pu)
 #endif
 void CABACWriter::intra_chroma_lmc_mode(const PredictionUnit& pu)
 {
+#if JVET_AD0188_CCP_MERGE
+  nonLocalCCPIndex(pu);
+  if (pu.idxNonLocalCCP)
+  {
+    return;
+  }
+#endif
   const unsigned intraDir = pu.intraDir[1];
 #if MMLM
   int lmModeList[NUM_CHROMA_MODE];
@@ -2337,6 +2344,23 @@ void CABACWriter::intra_chroma_lmc_mode(const PredictionUnit& pu)
 #endif
 }
 
+#if JVET_AD0188_CCP_MERGE
+void CABACWriter::nonLocalCCPIndex(const PredictionUnit &pu)
+{
+  if (PU::hasNonLocalCCP(pu))
+  {
+    CHECK(pu.idxNonLocalCCP < 0 || pu.idxNonLocalCCP > MAX_CCP_CAND_LIST_SIZE, "Invalid idxNonLocalCCP");
+    {
+      m_BinEncoder.encodeBin(pu.idxNonLocalCCP ? 1 : 0, Ctx::nonLocalCCP(0));
+      if (pu.idxNonLocalCCP)
+      {
+        unary_max_eqprob(pu.idxNonLocalCCP - 1, MAX_CCP_CAND_LIST_SIZE - 1);
+      }
+    }
+  }
+}
+#endif
+
 #if JVET_AA0057_CCCM
 void CABACWriter::cccmFlag(const PredictionUnit& pu)
 {
diff --git a/source/Lib/EncoderLib/CABACWriter.h b/source/Lib/EncoderLib/CABACWriter.h
index a188b852c..c240e7d3f 100644
--- a/source/Lib/EncoderLib/CABACWriter.h
+++ b/source/Lib/EncoderLib/CABACWriter.h
@@ -143,6 +143,9 @@ public:
 #endif
 #if JVET_AA0057_CCCM
   void        cccmFlag                  ( const PredictionUnit&         pu );
+#endif
+#if JVET_AD0188_CCP_MERGE
+  void        nonLocalCCPIndex          ( const PredictionUnit&         pu );
 #endif
   void        cu_residual               ( const CodingUnit&             cu,       Partitioner&      pm,         CUCtx& cuCtx );
   void        rqt_root_cbf              ( const CodingUnit&             cu );
diff --git a/source/Lib/EncoderLib/EncCfg.h b/source/Lib/EncoderLib/EncCfg.h
index 71aacf2e3..8e3f9de01 100644
--- a/source/Lib/EncoderLib/EncCfg.h
+++ b/source/Lib/EncoderLib/EncCfg.h
@@ -485,6 +485,9 @@ protected:
 #if JVET_AC0147_CCCM_NO_SUBSAMPLING
   int       m_cccm;
 #endif
+#if JVET_AD0188_CCP_MERGE
+  bool      m_ccpMerge;
+#endif
 #if ENABLE_OBMC
   bool      m_OBMC;
 #endif
@@ -1592,6 +1595,10 @@ public:
   void      setUseCccm                   (int i)          { m_cccm = i; }
   int       getUseCccm                   () const         { return m_cccm; }
 #endif
+#if JVET_AD0188_CCP_MERGE
+  void      setUseCcpMerge               (bool i)         { m_ccpMerge = i; }
+  bool      getUseCcpMerge               ()         const { return m_ccpMerge; }
+#endif
 #if ENABLE_OBMC
   void      setUseObmc                   ( bool b )       { m_OBMC = b; }
   bool      getUseObmc                   ()         const { return m_OBMC; }
diff --git a/source/Lib/EncoderLib/EncCu.cpp b/source/Lib/EncoderLib/EncCu.cpp
index 0c66ffa10..185bca903 100644
--- a/source/Lib/EncoderLib/EncCu.cpp
+++ b/source/Lib/EncoderLib/EncCu.cpp
@@ -1558,6 +1558,20 @@ void EncCu::xCompressCU( CodingStructure*& tempCS, CodingStructure*& bestCS, Par
     bool isIbcSmallBlk = CU::isIBC(cu) && (cu.lwidth() * cu.lheight() <= 16);
     CU::saveMotionInHMVP( cu, isIbcSmallBlk );
   }
+
+#if JVET_AD0188_CCP_MERGE
+  {
+    const CodingUnit &cu          = *bestCS->cus.front();
+    bool              lumaUsesISP = !CS::isDualITree(*bestCS) && cu.ispMode;
+    if (!(cu.chromaFormat == CHROMA_400 || (CS::isDualITree(*bestCS) && cu.chType == CHANNEL_TYPE_LUMA))
+        && CU::isIntra(cu) && !lumaUsesISP && bestCS->cus.size() == 1
+        && bestCS->area.Cb() == (*bestCS->cus.back()).Cb())
+    {
+      CU::saveModelsInHCCP(cu);
+    }
+  }
+#endif
+
   bestCS->picture->getPredBuf(currCsArea).copyFrom(bestCS->getPredBuf(currCsArea));
 #if JVET_Z0118_GDR
   bestCS->updateReconMotIPM(currCsArea); // xcomrpessCU - need 
@@ -1842,6 +1856,9 @@ void EncCu::xCheckModeSplit(CodingStructure *&tempCS, CodingStructure *&bestCS,
   const Slice &slice          = *tempCS->slice;
   const int oldPrevQp         = tempCS->prevQP[partitioner.chType];
   const auto oldMotionLut     = tempCS->motionLut;
+#if JVET_AD0188_CCP_MERGE
+  const auto oldCCPLut        = tempCS->ccpLut;
+#endif
 #if ENABLE_QPA_SUB_CTU
   const PPS &pps              = *tempCS->pps;
   const uint32_t currDepth    = partitioner.currDepth;
@@ -1981,6 +1998,9 @@ void EncCu::xCheckModeSplit(CodingStructure *&tempCS, CodingStructure *&bestCS,
 
 #if JVET_Z0118_GDR
         tempCS->motionLut = oldMotionLut;
+#if JVET_AD0188_CCP_MERGE
+        tempCS->ccpLut  = oldCCPLut;
+#endif
         tempCS->prevPLT = oldPLT;
         tempCS->releaseIntermediateData();
         tempCS->prevQP[partitioner.chType] = oldPrevQp;
@@ -2240,6 +2260,9 @@ void EncCu::xCheckModeSplit(CodingStructure *&tempCS, CodingStructure *&bestCS,
   tempCS->motionLut = oldMotionLut;
 
   tempCS->prevPLT   = oldPLT;
+#if JVET_AD0188_CCP_MERGE
+  tempCS->ccpLut    = oldCCPLut;
+#endif
 
   tempCS->releaseIntermediateData();
 
diff --git a/source/Lib/EncoderLib/EncLib.cpp b/source/Lib/EncoderLib/EncLib.cpp
index 8201529c8..165280278 100644
--- a/source/Lib/EncoderLib/EncLib.cpp
+++ b/source/Lib/EncoderLib/EncLib.cpp
@@ -1854,6 +1854,9 @@ void EncLib::xInitSPS( SPS& sps )
 #if JVET_AC0147_CCCM_NO_SUBSAMPLING
   sps.setUseCccm            ( m_cccm );
 #endif
+#if JVET_AD0188_CCP_MERGE
+  sps.setUseCcpMerge        ( m_ccpMerge );
+#endif
 #if ENABLE_OBMC
   sps.setUseOBMC            ( m_OBMC );
 #endif
diff --git a/source/Lib/EncoderLib/EncSlice.cpp b/source/Lib/EncoderLib/EncSlice.cpp
index 16aca5cd4..3c6964413 100644
--- a/source/Lib/EncoderLib/EncSlice.cpp
+++ b/source/Lib/EncoderLib/EncSlice.cpp
@@ -1842,6 +1842,12 @@ void EncSlice::encodeCtus( Picture* pcPic, const bool bCompressEntireSlice, cons
     }
   }
 #endif
+#if JVET_AD0188_CCP_MERGE
+  if ((pCfg->getSwitchPOC() != pcPic->poc || -1 == pCfg->getDebugCTU()))
+  {
+    cs.ccpLut.lutCCP.resize(0);
+  }
+#endif
 #if JVET_AD0208_IBC_ADAPT_FOR_CAM_CAPTURED_CONTENTS
   if (pcSlice->getUseIBC() && m_pcCuEncoder->getEncCfg()->getIBCHashSearch() && m_pcCuEncoder->getEncCfg()->getIBCFracMode())
   {
@@ -1912,6 +1918,13 @@ void EncSlice::encodeCtus( Picture* pcPic, const bool bCompressEntireSlice, cons
 #endif
     }
 
+#if JVET_AD0188_CCP_MERGE
+    if ((pCfg->getSwitchPOC() != pcPic->poc || -1 == pCfg->getDebugCTU()) && cs.pps->ctuIsTileColBd(ctuXPosInCtus))
+    {
+      cs.ccpLut.lutCCP.resize(0);
+    }
+#endif
+
     const SubPic &curSubPic = pcSlice->getPPS()->getSubPicFromPos(pos);
     // padding/restore at slice level
     if (pcSlice->getPPS()->getNumSubPics() >= 2 && curSubPic.getTreatedAsPicFlag() && ctuIdx == 0)
diff --git a/source/Lib/EncoderLib/IntraSearch.cpp b/source/Lib/EncoderLib/IntraSearch.cpp
index 8dbae2204..4989f9f43 100644
--- a/source/Lib/EncoderLib/IntraSearch.cpp
+++ b/source/Lib/EncoderLib/IntraSearch.cpp
@@ -2844,6 +2844,10 @@ void IntraSearch::estIntraPredChromaQT( CodingUnit &cu, Partitioner &partitioner
 #else
     bool isChromaFusion = false;
 #endif
+#endif
+#if JVET_AD0188_CCP_MERGE
+    int               bestNonAdjCCCM = 0;
+    CCPModelCandidate ccpModelBest;
 #endif
 
     //----- init mode list ----
@@ -3473,12 +3477,21 @@ void IntraSearch::estIntraPredChromaQT( CodingUnit &cu, Partitioner &partitioner
             distParamSadCr.applyWeight = false;
             distParamSatdCr.applyWeight = false;
 
+#if JVET_AD0188_CCP_MERGE
+            pu.curCand = {};
+#endif
 #if JVET_AD0202_CCCM_MDF
             predIntraCCCM(pu, cccmStorage[sub][cccmBufferIdx].Cb(), cccmStorage[sub][cccmBufferIdx].Cr(), mode);
 #else
             predIntraCCCM(pu, cccmStorage[sub][idx].Cb(), cccmStorage[sub][idx].Cr(), mode);
 #endif
-
+#if JVET_AD0188_CCP_MERGE
+#if JVET_AD0202_CCCM_MDF
+            m_ccmParamsStorage[sub][cccmBufferIdx] = pu.curCand;
+#else
+            m_ccmParamsStorage[sub][idx] = pu.curCand;
+#endif
+#endif
             sadCb = distParamSadCb.distFunc(distParamSadCb) * 2;
             satdCb = distParamSatdCb.distFunc(distParamSatdCb);
             sad += std::min(sadCb, satdCb);
@@ -3817,7 +3830,13 @@ void IntraSearch::estIntraPredChromaQT( CodingUnit &cu, Partitioner &partitioner
           distParamSadCr.applyWeight = false;
           distParamSatdCr.applyWeight = false;
 
+#if JVET_AD0188_CCP_MERGE
+            pu.curCand = {};
+#endif
           predIntraCCCM(pu, cccmStorage[idx].Cb(), cccmStorage[idx].Cr(), mode);
+#if JVET_AD0188_CCP_MERGE
+          m_ccmParamsStorage[idx] = pu.curCand;
+#endif
 
           sadCb = distParamSadCb.distFunc(distParamSadCb) * 2;
           satdCb = distParamSatdCb.distFunc(distParamSatdCb);
@@ -3990,6 +4009,9 @@ void IntraSearch::estIntraPredChromaQT( CodingUnit &cu, Partitioner &partitioner
 #endif
 #endif
 #endif
+#if JVET_AD0188_CCP_MERGE
+      pu.curCand = {};
+#endif
 #if JVET_AC0119_LM_CHROMA_FUSION
       int secondNonLmMode = -1;
       double dSecondNonLmCost = MAX_DOUBLE;
@@ -4103,6 +4125,17 @@ void IntraSearch::estIntraPredChromaQT( CodingUnit &cu, Partitioner &partitioner
           uiBestDist = uiDist;
           uiBestMode = chromaIntraMode;
           bestBDPCMMode = cu.bdpcmModeChroma;
+
+#if JVET_AD0188_CCP_MERGE
+          if (PU::isLMCMode(chromaIntraMode))
+          {
+            ccpModelBest = pu.curCand;
+          }
+          else
+          {
+            ccpModelBest.type = CCP_TYPE_NONE;
+          }
+#endif
         }
 #if JVET_Z0050_DIMD_CHROMA_FUSION
 #if JVET_AC0119_LM_CHROMA_FUSION
@@ -4135,6 +4168,9 @@ void IntraSearch::estIntraPredChromaQT( CodingUnit &cu, Partitioner &partitioner
 #endif
 #endif
       }
+#if JVET_AD0188_CCP_MERGE
+      pu.curCand = {};
+#endif
 
 #if JVET_AA0126_GLM
       for (int32_t uiMode = 0; uiMode < NUM_LMC_MODE; uiMode++)
@@ -4206,6 +4242,10 @@ void IntraSearch::estIntraPredChromaQT( CodingUnit &cu, Partitioner &partitioner
               uiBestMode      = chromaIntraMode;
               bestBDPCMMode   = cu.bdpcmModeChroma;
               bestGlmIdc      = pu.glmIdc;
+ #if JVET_AD0188_CCP_MERGE
+              ccpModelBest    = pu.curCand;
+              ccpModelBest.glmIdc = pu.glmIdc.cb0;
+#endif
             }
 #if !JVET_AB0092_GLM_WITH_LUMA
             if ( chromaIntraMode == LM_CHROMA_IDX && !bestGlmIdc.isActive() )
@@ -4218,6 +4258,9 @@ void IntraSearch::estIntraPredChromaQT( CodingUnit &cu, Partitioner &partitioner
       }
       
       pu.glmIdc.setAllZero();
+#if JVET_AD0188_CCP_MERGE
+      pu.curCand = {};
+#endif
 #endif
 
 #if JVET_Z0050_DIMD_CHROMA_FUSION
@@ -4410,6 +4453,16 @@ void IntraSearch::estIntraPredChromaQT( CodingUnit &cu, Partitioner &partitioner
             isChromaFusion = pu.isChromaFusion;
 #if JVET_AA0126_GLM
             bestGlmIdc = pu.glmIdc;
+#endif
+ #if JVET_AD0188_CCP_MERGE
+            if (isChromaFusion == 1)
+            {
+              ccpModelBest = pu.curCand;
+            }
+            else
+            {
+              ccpModelBest.type = CCP_TYPE_NONE;
+            }
 #endif
           }
 #if !JVET_AC0119_LM_CHROMA_FUSION
@@ -4421,6 +4474,9 @@ void IntraSearch::estIntraPredChromaQT( CodingUnit &cu, Partitioner &partitioner
 #else
       pu.isChromaFusion = false;
 #endif
+#if JVET_AD0188_CCP_MERGE
+      pu.curCand = {};
+#endif
 #endif
 
 #if JVET_Z0050_CCLM_SLOPE
@@ -4512,6 +4568,9 @@ void IntraSearch::estIntraPredChromaQT( CodingUnit &cu, Partitioner &partitioner
 #endif
 #if JVET_AA0126_GLM
               bestGlmIdc      = pu.glmIdc;
+#endif
+#if JVET_AD0188_CCP_MERGE
+              ccpModelBest    = pu.curCand;
 #endif
             }
           }
@@ -4519,6 +4578,9 @@ void IntraSearch::estIntraPredChromaQT( CodingUnit &cu, Partitioner &partitioner
       }
       
       pu.cclmOffsets.setAllZero();
+#if JVET_AD0188_CCP_MERGE
+      pu.curCand = {};
+#endif
 #endif
 
 #if JVET_AA0057_CCCM
@@ -4648,6 +4710,9 @@ void IntraSearch::estIntraPredChromaQT( CodingUnit &cu, Partitioner &partitioner
                 continue;
               }
               pu.cccmMultiFilterIdx = filterIdx;
+#if JVET_AD0188_CCP_MERGE
+              pu.curCand = {};
+#endif
 #endif
               pu.cccmNoSubFlag = sub;
 #if JVET_AC0054_GLCCCM
@@ -4694,6 +4759,18 @@ void IntraSearch::estIntraPredChromaQT( CodingUnit &cu, Partitioner &partitioner
 #if JVET_AC0147_CCCM_NO_SUBSAMPLING
 #if JVET_AD0202_CCCM_MDF
           const int cccmBufferIdx = filterIdx * CCCM_NUM_MODES + uiMode;
+#endif
+#if JVET_AD0188_CCP_MERGE
+          if (pu.cs->slice->isIntra() && CS::isDualITree(cs))
+          {
+#if JVET_AD0202_CCCM_MDF
+            pu.curCand = m_ccmParamsStorage[sub][cccmBufferIdx];
+#else
+            pu.curCand = m_ccmParamsStorage[sub][uiMode];
+#endif
+          }
+#endif
+#if JVET_AD0202_CCCM_MDF
           xRecurIntraChromaCodingQT(cs, partitioner, bestCostSoFar, ispType, cccmStorage[sub][cccmBufferIdx]
 #if JVET_AD0208_IBC_ADAPT_FOR_CAM_CAPTURED_CONTENTS
                                   , pcInterPred
@@ -4791,6 +4868,9 @@ void IntraSearch::estIntraPredChromaQT( CodingUnit &cu, Partitioner &partitioner
 #if JVET_AD0202_CCCM_MDF
             cccmMultiFilterIdxBest = pu.cccmMultiFilterIdx;
 #endif
+#if JVET_AD0188_CCP_MERGE
+            ccpModelBest    = pu.curCand;
+#endif
 #if JVET_AC0147_CCCM_NO_SUBSAMPLING 
             cccmNoSubBest  = pu.cccmNoSubFlag;
             }
@@ -4806,6 +4886,196 @@ void IntraSearch::estIntraPredChromaQT( CodingUnit &cu, Partitioner &partitioner
 #if JVET_AD0202_CCCM_MDF
       pu.cccmMultiFilterIdx = 0;
 #endif
+#if JVET_AD0188_CCP_MERGE
+      pu.curCand = {};
+#endif
+#endif
+
+#if JVET_AD0188_CCP_MERGE
+      if (PU::hasNonLocalCCP(pu))
+      {
+        pu.cccmFlag      = 0;
+        pu.cccmNoSubFlag = 0;
+        pu.glCccmFlag    = 0;
+        CCPModelCandidate candList[MAX_CCP_CAND_LIST_SIZE];
+        double            orderedCCPcost[MAX_CCP_CAND_LIST_SIZE];
+        int               orderedCCPCand[MAX_CCP_CAND_LIST_SIZE];
+        PelUnitBuf        ccpCandStorage[MAX_CCP_CAND_LIST_SIZE];
+        int               numPos = PU::getCCPModelCandidateList(pu, candList);
+
+        reorderCCPCandidates(pu, candList, numPos);
+
+        const double sqrtLambdaForFirstPass = m_pcRdCost->getMotionLambda() * FRAC_BITS_SCALE;
+
+        for (int nonAdjCCCMCand = 0; nonAdjCCCMCand < numPos; nonAdjCCCMCand++)
+        {
+          pu.intraDir[1]    = LM_CHROMA_IDX;   // temporary assigned, for SATD checking.
+          pu.idxNonLocalCCP = nonAdjCCCMCand + 1;
+
+          ccpCandStorage[nonAdjCCCMCand] = m_cccmStorage[0][nonAdjCCCMCand].getBuf(localUnitArea);   // Borrow the CCCM storage
+
+          uint64_t         sad    = 0;
+          uint64_t         sadCb  = 0;
+          uint64_t         satdCb = 0;
+          uint64_t         sadCr  = 0;
+          uint64_t         satdCr = 0;
+          CodingStructure &cs     = *(pu.cs);
+
+          CompArea areaCb = pu.Cb();
+          PelBuf   orgCb  = cs.getOrgBuf(areaCb);
+          PelBuf   predCb = ccpCandStorage[nonAdjCCCMCand].Cb();
+
+          CompArea areaCr = pu.Cr();
+          PelBuf   orgCr  = cs.getOrgBuf(areaCr);
+          PelBuf   predCr = ccpCandStorage[nonAdjCCCMCand].Cr();
+          m_pcRdCost->setDistParam(distParamSad, orgCb, predCb, pu.cs->sps->getBitDepth(CHANNEL_TYPE_CHROMA), COMPONENT_Cb, false);
+          m_pcRdCost->setDistParam(distParamSatd, orgCb, predCb, pu.cs->sps->getBitDepth(CHANNEL_TYPE_CHROMA), COMPONENT_Cb, true);
+          distParamSad.applyWeight  = false;
+          distParamSatd.applyWeight = false;
+
+          pu.curCand = candList[nonAdjCCCMCand];
+          predCCPCandidate(pu, predCb, predCr);
+          candList[nonAdjCCCMCand] = pu.curCand;
+
+          sadCb  = distParamSad.distFunc(distParamSad) * 2;
+          satdCb = distParamSatd.distFunc(distParamSatd);
+          sad += std::min(sadCb, satdCb);
+
+          m_pcRdCost->setDistParam(distParamSad, orgCr, predCr, pu.cs->sps->getBitDepth(CHANNEL_TYPE_CHROMA), COMPONENT_Cr, false);
+          m_pcRdCost->setDistParam(distParamSatd, orgCr, predCr, pu.cs->sps->getBitDepth(CHANNEL_TYPE_CHROMA), COMPONENT_Cr, true);
+          distParamSad.applyWeight  = false;
+          distParamSatd.applyWeight = false;
+
+          sadCr  = distParamSad.distFunc(distParamSad) * 2;
+          satdCr = distParamSatd.distFunc(distParamSatd);
+          sad += std::min(sadCr, satdCr);
+
+          m_CABACEstimator->resetBits();
+          m_CABACEstimator->nonLocalCCPIndex(pu);
+          uint64_t estbits               = m_CABACEstimator->getEstFracBits();
+          double   curCost               = (double) sad + sqrtLambdaForFirstPass * (double) estbits;
+          orderedCCPcost[nonAdjCCCMCand] = curCost;
+          orderedCCPCand[nonAdjCCCMCand] = nonAdjCCCMCand;
+
+          for (int i = 0; i < nonAdjCCCMCand; i++)
+          {
+            if (curCost < orderedCCPcost[i])
+            {
+              for (int j = nonAdjCCCMCand; j > i; j--)
+              {
+                orderedCCPcost[j] = orderedCCPcost[j - 1];
+                orderedCCPCand[j] = orderedCCPCand[j - 1];
+              }
+              orderedCCPcost[i] = curCost;
+              orderedCCPCand[i] = nonAdjCCCMCand;
+              break;
+            }
+          }
+        }
+
+        const int numRDtest = std::min(2, numPos);
+        for (int rdIdx = 0; rdIdx < numRDtest; rdIdx++)
+        {
+          int chromaIntraMode = LM_CHROMA_IDX;
+
+          pu.cccmFlag = 0;
+          pu.cccmNoSubFlag = 0;
+          pu.glCccmFlag = 0;
+
+          int nonAdjCCCMCand = orderedCCPCand[rdIdx];
+
+          pu.idxNonLocalCCP = nonAdjCCCMCand + 1;
+          pu.curCand        = candList[nonAdjCCCMCand];
+
+          // Original RD check code replicated from above
+          cs.setDecomp(pu.Cb(), false);
+          cs.dist = baseDist;
+          //----- restore context models -----
+          m_CABACEstimator->getCtx() = ctxStart;
+
+          //----- chroma coding -----
+          pu.intraDir[1] = chromaIntraMode;
+#if JVET_AB0143_CCCM_TS
+          xRecurIntraChromaCodingQT(cs, partitioner, bestCostSoFar, ispType, ccpCandStorage[nonAdjCCCMCand]);
+#else
+          xRecurIntraChromaCodingQT(cs, partitioner, bestCostSoFar, ispType);
+#endif
+          if (lumaUsesISP && cs.dist == MAX_UINT)
+          {
+            continue;
+          }
+
+          if (cs.sps->getTransformSkipEnabledFlag())
+          {
+            m_CABACEstimator->getCtx() = ctxStart;
+          }
+
+          uint64_t   fracBits = xGetIntraFracBitsQT(cs, partitioner, false, true, -1, ispType);
+          Distortion uiDist   = cs.dist;
+          double     dCost    = m_pcRdCost->calcRdCost(fracBits, uiDist - baseDist);
+
+          //----- compare -----
+          if (dCost < dBestCost)
+          {
+            if (lumaUsesISP && dCost < bestCostSoFar)
+            {
+              bestCostSoFar = dCost;
+            }
+            for( uint32_t i = getFirstComponentOfChannel( CHANNEL_TYPE_CHROMA ); i < numberValidComponents; i++ )
+            {
+              const CompArea &area = pu.blocks[i];
+
+              saveCS.getRecoBuf(area).copyFrom(cs.getRecoBuf(area));
+#if KEEP_PRED_AND_RESI_SIGNALS
+              saveCS.getPredBuf(area).copyFrom(cs.getPredBuf(area));
+              saveCS.getResiBuf(area).copyFrom(cs.getResiBuf(area));
+#endif
+              saveCS.getPredBuf(area).copyFrom(cs.getPredBuf(area));
+              cs.picture->getPredBuf(area).copyFrom(cs.getPredBuf(area));
+#if !JVET_AB0143_CCCM_TS
+              cs.picture->getRecoBuf(area).copyFrom(cs.getRecoBuf(area));
+#endif
+
+              for (uint32_t j = 0; j < saveCS.tus.size(); j++)
+              {
+                saveCS.tus[j]->copyComponentFrom(*orgTUs[j], area.compID);
+              }
+            }
+
+            dBestCost  = dCost;
+            uiBestDist = uiDist;
+
+            uiBestMode = chromaIntraMode;
+
+            bestBDPCMMode = cu.bdpcmModeChroma;
+#if JVET_Z0050_DIMD_CHROMA_FUSION
+            isChromaFusion = pu.isChromaFusion;
+#endif
+#if JVET_Z0050_CCLM_SLOPE
+            bestCclmOffsets = pu.cclmOffsets;
+#endif
+            cccmModeBest = pu.cccmFlag;
+#if JVET_AA0126_GLM
+            bestGlmIdc = pu.glmIdc;
+#endif
+#if JVET_AC0147_CCCM_NO_SUBSAMPLING 
+            cccmNoSubBest  = pu.cccmNoSubFlag;
+#endif
+#if JVET_AC0054_GLCCCM
+            glCccmBest     = pu.glCccmFlag;
+#endif
+#if JVET_AD0202_CCCM_MDF
+            cccmMultiFilterIdxBest = pu.cccmMultiFilterIdx;
+#endif
+            bestNonAdjCCCM = pu.idxNonLocalCCP;
+            ccpModelBest   = pu.curCand;
+          }
+        }
+      }
+      pu.idxNonLocalCCP = 0;
+#if JVET_AD0188_CCP_MERGE
+      pu.curCand = {};
+#endif
 #endif
       for( uint32_t i = getFirstComponentOfChannel( CHANNEL_TYPE_CHROMA ); i < numberValidComponents; i++ )
       {
@@ -4855,6 +5125,10 @@ void IntraSearch::estIntraPredChromaQT( CodingUnit &cu, Partitioner &partitioner
 #endif
 #if JVET_AA0126_GLM
     pu.glmIdc          = bestGlmIdc;
+#endif
+#if JVET_AD0188_CCP_MERGE
+    pu.idxNonLocalCCP  = bestNonAdjCCCM;
+    pu.curCand         = ccpModelBest;
 #endif
   }
 
@@ -9359,6 +9633,21 @@ ChromaCbfs IntraSearch::xRecurIntraChromaCodingQT( CodingStructure &cs, Partitio
     initIntraPatternChType( *currTU.cu, cbArea);
     initIntraPatternChType( *currTU.cu, crArea);
 
+ #if JVET_AD0188_CCP_MERGE
+    if (pu.idxNonLocalCCP)
+    {
+      if (!predStorage.bufs.empty())
+      {
+        piPredCb.copyFrom(predStorage.Cb());
+        piPredCr.copyFrom(predStorage.Cr());
+      }
+      else
+      {
+        predCCPCandidate(pu, piPredCb, piPredCr);
+      }
+    }
+    else
+#endif
 #if JVET_AA0057_CCCM
     if( pu.cccmFlag )
     {
diff --git a/source/Lib/EncoderLib/IntraSearch.h b/source/Lib/EncoderLib/IntraSearch.h
index 0806cc02d..4b1932cc1 100644
--- a/source/Lib/EncoderLib/IntraSearch.h
+++ b/source/Lib/EncoderLib/IntraSearch.h
@@ -553,6 +553,18 @@ private:
 #endif
 #endif
 
+#if JVET_AD0188_CCP_MERGE
+#if JVET_AC0147_CCCM_NO_SUBSAMPLING
+#if JVET_AD0202_CCCM_MDF
+  CCPModelCandidate m_ccmParamsStorage[2][TOTAL_NUM_CCCM_MODES];
+#else
+  CCPModelCandidate m_ccmParamsStorage[2][CCCM_NUM_MODES];
+#endif
+#else
+  CCPModelCandidate m_ccmParamsStorage[CCCM_NUM_MODES];
+#endif
+#endif
+
 #if JVET_AC0119_LM_CHROMA_FUSION
   PelStorage      m_predStorage[2];
   PelStorage      m_fusionStorage[6];
diff --git a/source/Lib/EncoderLib/VLCWriter.cpp b/source/Lib/EncoderLib/VLCWriter.cpp
index 39cb0b9d4..7aee90c5c 100644
--- a/source/Lib/EncoderLib/VLCWriter.cpp
+++ b/source/Lib/EncoderLib/VLCWriter.cpp
@@ -1611,6 +1611,10 @@ void HLSWriter::codeSPS( const SPS* pcSPS )
 #if JVET_AC0147_CCCM_NO_SUBSAMPLING
   WRITE_UVLC(pcSPS->getUseCccm() , "sps_cccm_cand");
 #endif
+#if JVET_AD0188_CCP_MERGE
+  WRITE_UVLC(pcSPS->getUseCcpMerge(), "sps_ccp_merge");
+#endif
+
   if( pcSPS->getChromaFormatIdc() != CHROMA_400)
   {
     WRITE_FLAG( pcSPS->getUseLMChroma() ? 1 : 0,                                      "sps_cclm_enabled_flag");
-- 
GitLab