diff --git a/source/Lib/CommonLib/Common.h b/source/Lib/CommonLib/Common.h
index 9bdb2562..4dba3fc3 100644
--- a/source/Lib/CommonLib/Common.h
+++ b/source/Lib/CommonLib/Common.h
@@ -635,7 +635,19 @@ struct EipModelCandidate
 {
   int64_t params[EIP_FILTER_TAP] = { 0 };
   int     filterShape            = 0;
+#if EIP_DIMD_OPT
+  int     eipDimdMode            = -1;
+#else
   int     eipDimdMode            = PLANAR_IDX;
+#endif
+#if MM_EIP
+  bool    bMm                     = false;
+  int64_t params1[EIP_FILTER_TAP] = { 0 };
+  Pel     eipMmThrd               = 0;
+#endif
+#if BV_EIP
+  bool    isBvEip = false;
+#endif
 
   inline bool isTheSameParams(const EipModelCandidate& p) const
   {
@@ -643,6 +655,26 @@ struct EipModelCandidate
     {
       return false;
     }
+#if MM_EIP
+    if (bMm != p.bMm)
+    {
+      return false;
+    }
+    if (bMm)
+    {
+      if (eipMmThrd != p.eipMmThrd)
+      {
+        return false;
+      }
+      for (int i = 0; i < EIP_FILTER_TAP; ++i)
+      {
+        if (params1[i] != p.params1[i])
+        {
+          return false;
+        }
+      }
+    }
+#endif
     for (int i = 0; i < EIP_FILTER_TAP; ++i)
     {
       if (params[i] != p.params[i])
diff --git a/source/Lib/CommonLib/CommonDef.h b/source/Lib/CommonLib/CommonDef.h
index 207e85c6..afbb3dbc 100644
--- a/source/Lib/CommonLib/CommonDef.h
+++ b/source/Lib/CommonLib/CommonDef.h
@@ -717,8 +717,39 @@ static const int MAX_MERGE_EIP =                                   12;
 static const int NUM_EIP_MERGE_SIGNAL =                             6;
 static const int EIP_TPL_SIZE =                                     1;
 static const int NUM_EIP_BASE_RECOTYPE =                            3;
+#if MM_EIP
+static const int NUM_EIP_MM = 9;
+static const int NUM_EIP_COMB = NUM_EIP_SHAPE * NUM_EIP_BASE_RECOTYPE;
+static const int NUM_DERIVED_EIP                  = (NUM_EIP_SHAPE * 3) + NUM_EIP_MM;
+
+static const int L2_MM_EIP_REG[7] = { 320, 288, 256, 224, 192, 128, 64 };
+static const int L2_MM_SAMPLE_THR[7] = { 128, 256, 512, 1024, 2048, 2147483647, 4096 };
+static const int MAX_EIP_DERIVED_SIZE      = 8;
+#else
 static const int NUM_EIP_COMB = NUM_EIP_SHAPE * NUM_EIP_BASE_RECOTYPE;
 static const int NUM_DERIVED_EIP                  = NUM_EIP_SHAPE * 3;
+#endif  
+
+#if BV_EIP
+static const int NUM_DERIVED_BVEIP = 3;
+static const int NUM_MERGE_BV_EIP = 3;
+static const int MAX_BV_LIST = 1;
+static const int TMP_EIP_TEMP_SIZE = 4;
+static const int MIN_BV_EIP_BLK = 32; // this number is not allowed
+static const int MIN_BV_MRG_EIP_BLK = 32;  // this number is allowed
+static const int MAX_BV_MRG_EIP_BLK = 512; // this number is allowed
+
+static const double ENC_SKIP_EIP_ACCOR_TMP = 2.0;
+#if TEST_2_19_c
+static const double ENC_FLM_SAD_CHK_RATE = 1.1;
+static const double ENC_SKIP_SATD_BVEIP_FACTOR = 2.5;
+static const double ENC_SKIP_SATD_MRGEIP_FACTOR = 8.0;
+#else
+static const double ENC_FLM_SAD_CHK_RATE = 1.5;
+static const double ENC_SKIP_SATD_BVEIP_FACTOR = 3.0;
+static const double ENC_SKIP_SATD_MRGEIP_FACTOR = 10.0;
+#endif
+#endif
 #endif
 #if MMLM
 static const int MMLM_CHROMA_IDX = LM_CHROMA_IDX + 1; ///< MDLM_L
diff --git a/source/Lib/CommonLib/Contexts_ecm14.0.inl b/source/Lib/CommonLib/Contexts_ecm14.0.inl
index 5b9830d9..f67b39ad 100644
--- a/source/Lib/CommonLib/Contexts_ecm14.0.inl
+++ b/source/Lib/CommonLib/Contexts_ecm14.0.inl
@@ -6372,6 +6372,51 @@ const CtxSet ContextSetCfg::InterCcpMergeZeroRootCbfIdc = ContextSetCfg::addCtxS
 const CtxSet ContextSetCfg::EipFlag = ContextSetCfg::addCtxSet
   ({
 // ctx 1594 1595
+#if BV_EIP
+     {  40,  11,  11,  11, CNU},
+     {  25,  18,  18,  18, CNU},
+     {  17,  27,  27,  27, CNU},
+     {  25,  11,  11,  11, CNU},
+     {   6,   8,   8,   8, DWS},
+     {   6,   8,   8,   8, DWS},
+     {   9,   5,   5,   5, DWS},
+     {   6,   9,   9,   9, DWS},
+     {  25,  32,  32,  32, DWE},
+     {  25,  32,  32,  32, DWE},
+     {  25,   4,   4,   4, DWE},
+     {  25,  32,  32,  32, DWE},
+     { 100, 115, 115, 115, DWO},
+     { 130, 116, 116, 116, DWO},
+     { 102, 163, 163, 163, DWO},
+     { 225,  99,  99,  99, DWO},
+     { 105, 134, 134, 134, DWO},
+     { 241, 132, 132, 132, DWO},
+     { 103, 226, 226, 226, DWO},
+     { 241, 100, 100, 100, DWO},
+    });
+#elif MM_EIP
+     {  40,  11, CNU},
+     {  25,  18, CNU},
+     {  17,  27, CNU},
+	   {  25,  11, CNU},
+     {   6,   8, DWS},
+     {   6,   8, DWS},
+     {   9,   5, DWS},
+	   {   6,   9, DWS},
+     {  25,  32, DWE},
+     {  25,  32, DWE},
+     {  25,   4, DWE},
+	   {  25,  32, DWE},
+     { 100, 115, DWO},
+     { 130, 116, DWO},
+     { 102, 163, DWO},
+     { 225,  99, DWO},
+     { 105, 134, DWO},
+     { 241, 132, DWO},
+	   { 103, 226, DWO},
+     { 241, 100, DWO},
+     });
+#else
      {  40,  11 },
      {  25,  18 },
      {  17,  27 },
@@ -6394,6 +6439,7 @@ const CtxSet ContextSetCfg::EipFlag = ContextSetCfg::addCtxSet
      { 241, 100 },
      });
 #endif
+#endif
 
 #if JVET_AG0059_CCP_MERGE_ENHANCEMENT
 const CtxSet ContextSetCfg::CCPMergeFusionFlag = ContextSetCfg::addCtxSet({
diff --git a/source/Lib/CommonLib/InterPrediction.cpp b/source/Lib/CommonLib/InterPrediction.cpp
index 99362f48..4e9f38f7 100644
--- a/source/Lib/CommonLib/InterPrediction.cpp
+++ b/source/Lib/CommonLib/InterPrediction.cpp
@@ -11437,13 +11437,21 @@ void  InterPrediction::sortIbcAdaptiveMergeMbvdCandidates(PredictionUnit &pu, Me
 
       if (m_bAMLTemplateAvailabe[0])
       {
+#if TEST_3_10_A
+        m_pcRdCost->setDistParam(cDistParam, pcBufPredCurTop.Y(), pcBufPredRefTop.Y(), pu.cs->sps->getBitDepth(CHANNEL_TYPE_LUMA), COMPONENT_Y, true);
+#else
         m_pcRdCost->setDistParam(cDistParam, pcBufPredCurTop.Y(), pcBufPredRefTop.Y(), pu.cs->sps->getBitDepth(CHANNEL_TYPE_LUMA), COMPONENT_Y, false);
+#endif
         uiCost += cDistParam.distFunc(cDistParam);
       }
 
       if (uiCost < m_mbvdCandCostList[endEncIdx - 1] && m_bAMLTemplateAvailabe[1])
       {
+#if TEST_3_10_A
+        m_pcRdCost->setDistParam(cDistParam, pcBufPredCurLeft.Y(), pcBufPredRefLeft.Y(), pu.cs->sps->getBitDepth(CHANNEL_TYPE_LUMA), COMPONENT_Y, true);
+#else
         m_pcRdCost->setDistParam(cDistParam, pcBufPredCurLeft.Y(), pcBufPredRefLeft.Y(), pu.cs->sps->getBitDepth(CHANNEL_TYPE_LUMA), COMPONENT_Y, false);
+#endif
         uiCost += cDistParam.distFunc(cDistParam);
       }
 
@@ -11591,14 +11599,22 @@ void  InterPrediction::sortIbcMergeMbvdCandidates(PredictionUnit &pu, MergeCtx&
 
     if (m_bAMLTemplateAvailabe[0])
     {
+#if TEST_3_10_A
+      m_pcRdCost->setDistParam(cDistParam, pcBufPredCurTop.Y(), pcBufPredRefTop.Y(), pu.cs->sps->getBitDepth(CHANNEL_TYPE_LUMA), COMPONENT_Y, true);
+#else
       m_pcRdCost->setDistParam(cDistParam, pcBufPredCurTop.Y(), pcBufPredRefTop.Y(), pu.cs->sps->getBitDepth(CHANNEL_TYPE_LUMA), COMPONENT_Y, false);
+#endif
 
       uiCost += cDistParam.distFunc(cDistParam);
     }
 
     if (m_bAMLTemplateAvailabe[1])
     {
+#if TEST_3_10_A
+      m_pcRdCost->setDistParam(cDistParam, pcBufPredCurLeft.Y(), pcBufPredRefLeft.Y(), pu.cs->sps->getBitDepth(CHANNEL_TYPE_LUMA), COMPONENT_Y, true);
+#else
       m_pcRdCost->setDistParam(cDistParam, pcBufPredCurLeft.Y(), pcBufPredRefLeft.Y(), pu.cs->sps->getBitDepth(CHANNEL_TYPE_LUMA), COMPONENT_Y, false);
+#endif
 
       uiCost += cDistParam.distFunc(cDistParam);
     }
@@ -19641,14 +19657,22 @@ void  InterPrediction::adjustIBCMergeCandidates(PredictionUnit &pu, MergeCtx& mr
 
     if (m_bAMLTemplateAvailabe[0])
     {
+#if TEST_3_10_A
+      m_pcRdCost->setDistParam(cDistParam, pcBufPredCurTop, pcBufPredRefTop, pu.cs->sps->getBitDepth(CHANNEL_TYPE_LUMA), COMPONENT_Y, true);
+#else
       m_pcRdCost->setDistParam(cDistParam, pcBufPredCurTop, pcBufPredRefTop, pu.cs->sps->getBitDepth(CHANNEL_TYPE_LUMA), COMPONENT_Y, false);
+#endif
 
       uiCost += cDistParam.distFunc(cDistParam);
     }
 
     if (m_bAMLTemplateAvailabe[1])
     {
+#if TEST_3_10_A
+      m_pcRdCost->setDistParam(cDistParam, pcBufPredCurLeft, pcBufPredRefLeft, pu.cs->sps->getBitDepth(CHANNEL_TYPE_LUMA), COMPONENT_Y, true);
+#else
       m_pcRdCost->setDistParam(cDistParam, pcBufPredCurLeft, pcBufPredRefLeft, pu.cs->sps->getBitDepth(CHANNEL_TYPE_LUMA), COMPONENT_Y, false);
+#endif
 
       uiCost += cDistParam.distFunc(cDistParam);
     }
@@ -19825,14 +19849,22 @@ void  InterPrediction::adjustIBCMergeCandidates(PredictionUnit &pu, MergeCtx& mr
 
     if (m_bAMLTemplateAvailabe[0])
     {
+#if TEST_3_10_A
+      m_pcRdCost->setDistParam(cDistParam, pcBufPredCurTop, pcBufPredRefTop, pu.cs->sps->getBitDepth(CHANNEL_TYPE_LUMA), COMPONENT_Y, true);
+#else
       m_pcRdCost->setDistParam(cDistParam, pcBufPredCurTop, pcBufPredRefTop, pu.cs->sps->getBitDepth(CHANNEL_TYPE_LUMA), COMPONENT_Y, false);
+#endif
 
       uiCost += cDistParam.distFunc(cDistParam);
     }
 
     if (m_bAMLTemplateAvailabe[1])
     {
+#if TEST_3_10_A
+      m_pcRdCost->setDistParam(cDistParam, pcBufPredCurLeft, pcBufPredRefLeft, pu.cs->sps->getBitDepth(CHANNEL_TYPE_LUMA), COMPONENT_Y, true);
+#else
       m_pcRdCost->setDistParam(cDistParam, pcBufPredCurLeft, pcBufPredRefLeft, pu.cs->sps->getBitDepth(CHANNEL_TYPE_LUMA), COMPONENT_Y, false);
+#endif
 
       uiCost += cDistParam.distFunc(cDistParam);
     }
@@ -20140,7 +20172,11 @@ Distortion InterPrediction::getTempCost(const PredictionUnit &pu, const PelBuf &
   Distortion uiCost;
   DistParam  cDistParam;
   cDistParam.applyWeight = false;
+#if TEST_3_10_A
+  m_pcRdCost->setDistParam(cDistParam, cur, org, pu.cs->sps->getBitDepth(CHANNEL_TYPE_LUMA), COMPONENT_Y, true);
+#else
   m_pcRdCost->setDistParam(cDistParam, cur, org, pu.cs->sps->getBitDepth(CHANNEL_TYPE_LUMA), COMPONENT_Y, false);
+#endif
   uiCost = cDistParam.distFunc(cDistParam);
   return uiCost;
 }
diff --git a/source/Lib/CommonLib/IntraPrediction.cpp b/source/Lib/CommonLib/IntraPrediction.cpp
index f800a2da..b9a7348b 100644
--- a/source/Lib/CommonLib/IntraPrediction.cpp
+++ b/source/Lib/CommonLib/IntraPrediction.cpp
@@ -3025,13 +3025,21 @@ Mv IntraPrediction::refineChromaBv(const ComponentID compId, const PredictionUni
     if (topCanUse)
     {
       PelBuf tempRef = PelBuf(refPix + 1, uiWidth, Size(uiWidth, DBV_TEMPLATE_SIZE));
+#if TEST_3_10_A
+      m_dbvSadCost->setDistParam(cDistParam, tempCurTop, tempRef, pu.cs->sps->getBitDepth(CHANNEL_TYPE_CHROMA), compId, uiWidth >= 4 && uiHeight >= 4 ? true : false);
+#else
       m_dbvSadCost->setDistParam(cDistParam, tempCurTop, tempRef, pu.cs->sps->getBitDepth(CHANNEL_TYPE_CHROMA), compId, false);
+#endif
       uiCost += cDistParam.distFunc(cDistParam);
     }
     if (leftCanUse)
     {
       PelBuf tempRef = PelBuf(refPix + 1 + stride, uiHeight, Size(uiHeight, DBV_TEMPLATE_SIZE));
+#if TEST_3_10_A
+      m_dbvSadCost->setDistParam(cDistParam, tempCurLeft, tempRef, pu.cs->sps->getBitDepth(CHANNEL_TYPE_CHROMA), compId, uiWidth >= 4 && uiHeight >= 4 ? true : false);
+#else
       m_dbvSadCost->setDistParam(cDistParam, tempCurLeft, tempRef, pu.cs->sps->getBitDepth(CHANNEL_TYPE_CHROMA), compId, false);
+#endif
       uiCost += cDistParam.distFunc(cDistParam);
     }
     aBvCostVec.push_back(std::pair<Mv, Distortion>(*it, uiCost));
@@ -4793,7 +4801,11 @@ void IntraPrediction::geneChromaFusionPred(const ComponentID compId, PelBuf &piP
     if (numSample > 0)
     {
       int bestSAD = cccmSAD < cclmSAD ? cccmSAD : cclmSAD;
+#if TEST_3_10_A
+      if (bestSAD > 144 * numSample)
+#else
       if (bestSAD > 64 * numSample)
+#endif
       {
         w0 = 3;
         w1 = 1;
@@ -7906,21 +7918,39 @@ void IntraPrediction::deriveMPMSorted(const PredictionUnit& pu, uint8_t* mpm, in
   distParamSad[1].useMR = false;
   if (eTempType == LEFT_ABOVE_NEIGHBOR)
   {
+#if TEST_3_10_A
+    m_timdSatdCost->setTimdDistParam(distParamSad[0], piOrg + iTempWidth, piPred + iTempWidth, iOrgStride,
+      uiPredStride, channelBitDepth, COMPONENT_Y, uiWidth, iTempHeight, 0, 1, true);   // Use HAD (SATD) cost
+    m_timdSatdCost->setTimdDistParam(distParamSad[1], piOrg + iTempHeight * iOrgStride,
+      piPred + iTempHeight * uiPredStride, iOrgStride, uiPredStride, channelBitDepth,
+      COMPONENT_Y, iTempWidth, uiHeight, 0, 1, true);
+#else
     m_timdSatdCost->setTimdDistParam(distParamSad[0], piOrg + iTempWidth, piPred + iTempWidth, iOrgStride,
       uiPredStride, channelBitDepth, COMPONENT_Y, uiWidth, iTempHeight, 0, 1, false);   // Use HAD (SATD) cost
     m_timdSatdCost->setTimdDistParam(distParamSad[1], piOrg + iTempHeight * iOrgStride,
       piPred + iTempHeight * uiPredStride, iOrgStride, uiPredStride, channelBitDepth,
       COMPONENT_Y, iTempWidth, uiHeight, 0, 1, false);
+#endif
   }
   else if (eTempType == LEFT_NEIGHBOR)
   {
+#if TEST_3_10_A
+    m_timdSatdCost->setTimdDistParam(distParamSad[1], piOrg, piPred, iOrgStride, uiPredStride, channelBitDepth,
+      COMPONENT_Y, iTempWidth, uiHeight, 0, 1, true);
+#else
     m_timdSatdCost->setTimdDistParam(distParamSad[1], piOrg, piPred, iOrgStride, uiPredStride, channelBitDepth,
       COMPONENT_Y, iTempWidth, uiHeight, 0, 1, false);
+#endif
   }
   else if (eTempType == ABOVE_NEIGHBOR)
   {
+#if TEST_3_10_A
+    m_timdSatdCost->setTimdDistParam(distParamSad[0], piOrg, piPred, iOrgStride, uiPredStride, channelBitDepth,
+      COMPONENT_Y, uiWidth, iTempHeight, 0, 1, true);
+#else
     m_timdSatdCost->setTimdDistParam(distParamSad[0], piOrg, piPred, iOrgStride, uiPredStride, channelBitDepth,
       COMPONENT_Y, uiWidth, iTempHeight, 0, 1, false);
+#endif
   }
   initTimdIntraPatternLuma(*pu.cu, area, eTempType != ABOVE_NEIGHBOR ? iTempWidth : 0,
     eTempType != LEFT_NEIGHBOR ? iTempHeight : 0, uiRefWidth, uiRefHeight);
@@ -15008,6 +15038,13 @@ void IntraPrediction::searchCandidateFromOnePicIntra( CodingUnit* pcCU, Pel** ta
 #else
   int                                         mtmpNumSparse[3] = { MTMP_NUM_SPARSE, TL_NUM_SPARSE, TL_NUM_SPARSE };
 #endif
+#endif
+#if BV_EIP_QUICK_SEARCH
+  if (!pcCU->cs->pcv->isEncoder && pcCU->bvEip)
+  {
+    mtmpNumSparse[0] = 1;
+    mtmpNumSparse[1] = mtmpNumSparse[2] = 0;
+  }
 #endif
 
   const ComponentID compID      = COMPONENT_Y;
@@ -15546,7 +15583,11 @@ void IntraPrediction::searchCandidateFromOnePicIntra( CodingUnit* pcCU, Pel** ta
       {
         const PredictionUnit* puCascaded = pu.cs->getPURestricted(posCand[n].offset(offsetX, offsetY), pu, pu.chType);
 
-        if (!puCascaded || ((puCascaded->cu->predMode != MODE_IBC) && (!puCascaded->cu->tmpFlag)))
+        if (!puCascaded || ((puCascaded->cu->predMode != MODE_IBC) && (!puCascaded->cu->tmpFlag)
+#if BV_EIP
+          && !PU::hasEipBv(puCascaded)
+#endif
+          ))
         {
           continue;
         }
@@ -15622,6 +15663,27 @@ void IntraPrediction::searchCandidateFromOnePicIntra( CodingUnit* pcCU, Pel** ta
     }
   } while (bvBasedMergeCandidatesITMP.size() > end && bvBasedMergeCandidatesITMP.size() < totalNum);
 #endif
+
+#if BV_EIP_QUICK_SEARCH
+  if (!sparseMtmpCandList[0].empty())
+  {
+    m_tmpXdispQuick = sparseMtmpCandList[0][0].m_pX;
+    m_tmpYdispQuick = sparseMtmpCandList[0][0].m_pY;
+    m_tmpQuickCand = !sparseMtmpCandList[0].empty();
+    if (!pcCU->cs->pcv->isEncoder && pcCU->bvEip)
+    {
+      pcCU->tmpXdisp = sparseMtmpCandList[0][0].m_pX;
+      pcCU->tmpYdisp = sparseMtmpCandList[0][0].m_pY;
+      return;
+    }
+  }
+  else
+  {
+    m_tmpQuickCand = false;
+  }
+#endif
+
+
 #if JVET_AI0129_INTRA_TMP_OVERLAPPING_REFINEMENT
   // Add ARBVPs based on sparse candidates
   std::vector<Mv> bvBasedMergeCandidatesITMPSupp;
@@ -15868,6 +15930,14 @@ void IntraPrediction::searchCandidateFromOnePicIntra( CodingUnit* pcCU, Pel** ta
   static_vector<TempLibFast, MTMP_NUM> refineMtmpCandList[3];
   static_vector<uint64_t, MTMP_NUM>    refineMtmpCostList[3];
   int                                  mtmpNumRefine[3] = { MTMP_NUM, TL_NUM, TL_NUM };
+#if BV_EIP
+  if (!pcCU->cs->pcv->isEncoder && pcCU->bvEip)
+  {
+    mtmpNumRefine[0] = 1;
+    mtmpNumRefine[1] = mtmpNumRefine[2] = 0;
+  }
+#endif
+
   if (!needTopLeft)
   {
     mtmpNumRefine[0] = pcCU->tmpFusionFlag ? (((pcCU->tmpIdx % TMP_GROUP_IDX) + 1) * TMP_FUSION_NUM) : (pcCU->tmpIdx + 1); 
@@ -21746,6 +21816,19 @@ uint32_t IntraPrediction::xCalculateCCLMcost(const PredictionUnit &pu, const Com
 
   const SizeType cWidth  = chromaArea.width;
   const SizeType cHeight = chromaArea.height;
+#if TEST_3_10_A
+  const ChannelType chType = toChannelType(compID);
+  DistParam cDistParam;
+  cDistParam.applyWeight = false;
+  static Pel predChromaA[MAX_CU_SIZE];
+  static Pel predChromaL[MAX_CU_SIZE];
+  PelBuf predTop(predChromaA, cWidth, 1);
+  PelBuf predLeft(predChromaL, 1, cHeight);
+  static Pel reconChromaA[MAX_CU_SIZE];
+  static Pel reconChromaL[MAX_CU_SIZE];
+  PelBuf reconTop(reconChromaA, cWidth, 1);
+  PelBuf reconLeft(reconChromaL, 1, cHeight);
+#endif
 
   CodingStructure  &cs = *(pu.cs);
   const CodingUnit &cu = *(pu.cu);
@@ -21780,8 +21863,17 @@ uint32_t IntraPrediction::xCalculateCCLMcost(const PredictionUnit &pu, const Com
         {
           predChroma = ClipPel(rightShift(cclmModel.a2 * src[pos], cclmModel.shift2) + cclmModel.b2, pu.cs->slice->clpRng(compID));
         }
+#if TEST_3_10_A
+        predTop.at(pos, 0) = predChroma;
+        reconTop.at(pos, 0) = curChroma0[pos];
+#else
         totalSAD += abs(predChroma - curChroma0[pos]);
+#endif
       }
+#if TEST_3_10_A
+      m_dbvSadCost->setDistParam(cDistParam, predTop, reconTop, pu.cs->sps->getBitDepth(chType), compID, cWidth >= 4 && cHeight >= 4 ? true : false);
+      totalSAD += (int)cDistParam.distFunc(cDistParam);
+#endif
     }
     else
 #endif
@@ -21789,8 +21881,17 @@ uint32_t IntraPrediction::xCalculateCCLMcost(const PredictionUnit &pu, const Com
       for (int pos = 0; pos < cWidth; pos++)
       {
         Pel predChroma = ClipPel(rightShift(cclmModel.a * src[pos], cclmModel.shift) + cclmModel.b, pu.cs->slice->clpRng(compID));
+#if TEST_3_10_A
+        predTop.at(pos, 0) = predChroma;
+        reconTop.at(pos, 0) = curChroma0[pos];
+#else
         totalSAD += abs(predChroma - curChroma0[pos]);
+#endif
       }
+#if TEST_3_10_A
+      m_dbvSadCost->setDistParam(cDistParam, predTop, reconTop, pu.cs->sps->getBitDepth(chType), compID, cWidth >= 4 && cHeight >= 4 ? true : false);
+      totalSAD += (int)cDistParam.distFunc(cDistParam);
+#endif
     }
   }
 
@@ -21814,8 +21915,17 @@ uint32_t IntraPrediction::xCalculateCCLMcost(const PredictionUnit &pu, const Com
         {
           predChroma = ClipPel(rightShift(cclmModel.a2 * src[pos * srcStride], cclmModel.shift2) + cclmModel.b2, pu.cs->slice->clpRng(compID));
         }
+#if TEST_3_10_A
+        predLeft.at(0, pos) = predChroma;
+        reconLeft.at(0, pos) = curChroma0[pos * curStride];
+#else
         totalSAD += abs(predChroma - curChroma0[pos * curStride]);
+#endif
       }
+#if TEST_3_10_A
+      m_dbvSadCost->setDistParam(cDistParam, predLeft, reconLeft, pu.cs->sps->getBitDepth(chType), compID, cWidth >= 4 && cHeight >= 4 ? true : false);
+      totalSAD += (int)cDistParam.distFunc(cDistParam);
+#endif
     }
     else
 #endif
@@ -21823,8 +21933,17 @@ uint32_t IntraPrediction::xCalculateCCLMcost(const PredictionUnit &pu, const Com
       for (int pos = 0; pos < cHeight; pos++)
       {
         Pel predChroma = ClipPel(rightShift(cclmModel.a * src[pos * srcStride], cclmModel.shift) + cclmModel.b, pu.cs->slice->clpRng(compID));
+#if TEST_3_10_A
+        predLeft.at(0, pos) = predChroma;
+        reconLeft.at(0, pos) = curChroma0[pos * curStride];
+#else
         totalSAD += abs(predChroma - curChroma0[pos * curStride]);
+#endif
       }
+#if TEST_3_10_A
+      m_dbvSadCost->setDistParam(cDistParam, predLeft, reconLeft, pu.cs->sps->getBitDepth(chType), compID, cWidth >= 4 && cHeight >= 4 ? true : false);
+      totalSAD += (int)cDistParam.distFunc(cDistParam);
+#endif
     }
   }
 
@@ -21902,6 +22021,19 @@ uint32_t IntraPrediction::xCalculateCCCMcost(const PredictionUnit &pu, const Com
 
   const SizeType cWidth  = chromaArea.width;
   const SizeType cHeight = chromaArea.height;
+#if TEST_3_10_A
+  const ChannelType chType = toChannelType(compID);
+  DistParam cDistParam;
+  cDistParam.applyWeight = false;
+  static Pel predChromaA[MAX_CU_SIZE];
+  static Pel predChromaL[MAX_CU_SIZE];
+  PelBuf predTop(predChromaA, cWidth, 1);
+  PelBuf predLeft(predChromaL, 1, cHeight);
+  static Pel reconChromaA[MAX_CU_SIZE];
+  static Pel reconChromaL[MAX_CU_SIZE];
+  PelBuf reconTop(reconChromaA, cWidth, 1);
+  PelBuf reconLeft(reconChromaL, 1, cHeight);
+#endif
 
   CodingStructure  &cs = *(pu.cs);
   const CodingUnit &cu = *(pu.cu);
@@ -21953,8 +22085,17 @@ uint32_t IntraPrediction::xCalculateCCCMcost(const PredictionUnit &pu, const Com
 #endif
         predChroma = ClipPel<Pel>(cccmModel[0].convolve(samples), clpRng);
 
+#if TEST_3_10_A
+      predTop.at(pos, 0) = predChroma;
+      reconTop.at(pos, 0) = curChroma0[pos];
+#else
       totalSAD += abs(predChroma - curChroma0[pos]);
+#endif
     }
+#if TEST_3_10_A
+    m_dbvSadCost->setDistParam(cDistParam, predTop, reconTop, pu.cs->sps->getBitDepth(chType), compID, cWidth >= 4 && cHeight >= 4 ? true : false);
+    totalSAD += (int)cDistParam.distFunc(cDistParam);
+#endif
   }
 
   if (checkLeft)
@@ -21989,8 +22130,17 @@ uint32_t IntraPrediction::xCalculateCCCMcost(const PredictionUnit &pu, const Com
       else
 #endif
         predChroma = ClipPel<Pel>(cccmModel[0].convolve(samples), clpRng);
+#if TEST_3_10_A
+      predLeft.at(0, pos) = predChroma;
+      reconLeft.at(0, pos) = curChroma0[pos * curStride];
+#else
       totalSAD += abs(predChroma - curChroma0[pos * curStride]);
+#endif
     }
+#if TEST_3_10_A
+    m_dbvSadCost->setDistParam(cDistParam, predLeft, reconLeft, pu.cs->sps->getBitDepth(chType), compID, cWidth >= 4 && cHeight >= 4 ? true : false);
+    totalSAD += (int)cDistParam.distFunc(cDistParam);
+#endif
   }
 
   return totalSAD;
@@ -27274,12 +27424,22 @@ void IntraPrediction::getTmrlList(CodingUnit& cu)
   distParamSad[1].applyWeight = false;
   distParamSad[1].useMR = false;
 
+#if TEST_3_10_A
+  m_timdSatdCost->setTimdDistParam(distParamSad[0], piOrg + tmrlInfo.uiTemplateLeft, piPred + tmrlInfo.uiTemplateLeft, iOrgStride, uiPredStride,
+    channelBitDepth, COMPONENT_Y, uiWidth, tmrlInfo.uiTemplateAbove, 0, 1, true);
+#else
   m_timdSatdCost->setTimdDistParam(distParamSad[0], piOrg + tmrlInfo.uiTemplateLeft, piPred + tmrlInfo.uiTemplateLeft, iOrgStride, uiPredStride,
     channelBitDepth, COMPONENT_Y, uiWidth, tmrlInfo.uiTemplateAbove, 0, 1, false);
+#endif
   if (cu.lx())
   {
+#if TEST_3_10_A
+    m_timdSatdCost->setTimdDistParam(distParamSad[1], piOrg + tmrlInfo.uiTemplateAbove * iOrgStride, piPred + tmrlInfo.uiTemplateAbove * uiPredStride,
+      iOrgStride, uiPredStride, channelBitDepth, COMPONENT_Y, tmrlInfo.uiTemplateLeft, uiHeight, 0, 1, true);
+#else
     m_timdSatdCost->setTimdDistParam(distParamSad[1], piOrg + tmrlInfo.uiTemplateAbove * iOrgStride, piPred + tmrlInfo.uiTemplateAbove * uiPredStride,
       iOrgStride, uiPredStride, channelBitDepth, COMPONENT_Y, tmrlInfo.uiTemplateLeft, uiHeight, 0, 1, false);
+#endif
   }
 
   // step-2. define search range.
@@ -27354,7 +27514,9 @@ int64_t IntraPrediction::calcAeipGroupSum(const Pel* src1, const Pel* src2, cons
   return sum;
 }
 
-#if JVET_AB0174_CCCM_DIV_FREE
+#if MM_EIP
+void CccmCovariance::solveEip(const TCccmCoeff* A, const TCccmCoeff* Y, const int sampleNum, const int lumaOffset, CccmModel& model, bool bMm)
+#elif JVET_AB0174_CCCM_DIV_FREE
 void CccmCovariance::solveEip(const TCccmCoeff* A, const TCccmCoeff* Y, const int sampleNum, const int lumaOffset, CccmModel& model)
 #else
 void CccmCovariance::solveEip(const TCccmCoeff* A, const TCccmCoeff* Y, const int sampleNum, CccmModel& model)
@@ -27376,8 +27538,42 @@ void CccmCovariance::solveEip(const TCccmCoeff* A, const TCccmCoeff* Y, const in
 
 #if JVET_AI0066_REGULARIZED_EIP
   // Regularization parameter: L2 multiplier * number of parameters
+#if MM_EIP
+  int regularizationParam;
+  if (bMm)
+  {
+    if (sampleNum <= L2_MM_SAMPLE_THR[0])
+    {
+      regularizationParam = L2_MM_EIP_REG[0] * numParams;
+    }
+    else if (sampleNum <= L2_MM_SAMPLE_THR[1])
+    {
+      regularizationParam = L2_MM_EIP_REG[1] * numParams;
+    }
+    else if (sampleNum <= L2_MM_SAMPLE_THR[2])
+    {
+      regularizationParam = L2_MM_EIP_REG[2] * numParams;
+    }
+    else if (sampleNum <= L2_MM_SAMPLE_THR[3])
+    {
+      regularizationParam = L2_MM_EIP_REG[3] * numParams;
+    }
+    else if (sampleNum <= L2_MM_SAMPLE_THR[4])
+    {
+      regularizationParam = L2_MM_EIP_REG[4] * numParams;
+    }
+    else
+    {
+      regularizationParam = L2_MM_EIP_REG[5] * numParams;
+    }
+  }
+  else
+  {
+    regularizationParam = (sampleNum <= REGULARIZED_EIP_L2_SAMPLE_THRESHOLD) ? REGULARIZED_EIP_L2_SMALL * numParams : REGULARIZED_EIP_L2_LARGE * numParams;
+  }
+#else
   const int regularizationParam = (sampleNum <= REGULARIZED_EIP_L2_SAMPLE_THRESHOLD) ? REGULARIZED_EIP_L2_SMALL * numParams : REGULARIZED_EIP_L2_LARGE * numParams;
-  
+#endif
   for (int coli0 = 0; coli0 < numParams - 1; coli0++) // The last term (bias) is not regularized.
   {
     ATA[coli0][coli0] += regularizationParam;
@@ -27504,6 +27700,10 @@ void IntraPrediction::initEipParams(const PredictionUnit& pu, const ComponentID
   int max = 0;
   int startY = refPosPicY < 0 ? -refPosPicY : 0;
   const int startX = refPosPicX < 0 ? -refPosPicX : 0;
+#if MM_EIP
+  double sum = 0;
+  int samplesNum = 0;
+#endif
   for(int y=startY;y<refTopAndTopLeft.height;y++)
   {
     for(int x=startX;x<refTopAndTopLeft.width;x++)
@@ -27511,6 +27711,13 @@ void IntraPrediction::initEipParams(const PredictionUnit& pu, const ComponentID
       int sample = refTopAndTopLeft.at(x,y);
       min = sample < min ? sample : min;
       max = sample > max ? sample : max;
+#if MM_EIP
+      if(y >= (refTopAndTopLeft.height >> 1) && x >= (refSizeX >> 1))
+      {
+        sum += sample;
+        samplesNum++;
+      }
+#endif
     }
   }
   startY = (refPosPicY + refSizeY) < 0 ? -(refPosPicY + refSizeY) : 0;
@@ -27521,11 +27728,21 @@ void IntraPrediction::initEipParams(const PredictionUnit& pu, const ComponentID
       int sample = refLeft.at(x,y);
       min = sample < min ? sample : min;
       max = sample > max ? sample : max;
+#if MM_EIP
+      if(x >= (refLeft.width >> 1))
+      {
+        sum += sample;
+        samplesNum++;
+      }
+#endif
     }
   }
   m_eipClipMin = min;
   m_eipClipMax = max;
   m_eipBias    = 1 << (pu.cu->slice->getSPS()->getBitDepth(chType) - 1);
+#if MM_EIP
+  m_eipAvg = Pel( sum / samplesNum);
+#endif
 #endif
 }
 
@@ -27568,7 +27785,25 @@ void IntraPrediction::getCurEipCands(const PredictionUnit& pu, static_vector<Eip
   const int sizeAtaBuf = ((EIP_FILTER_TAP + 1) * EIP_FILTER_TAP) >> 1;
   const int numInputs = EIP_FILTER_TAP;
   static_vector<EIPInfo, NUM_DERIVED_EIP> eipInfoList;
+#if MM_EIP
+#if MM_EIP_REFACTOR
+  if (pu.cs->pcv->isEncoder)
+  {
+    m_numSigEip = getAllowedCurEip(*pu.cu, compId, eipInfoList, false);
+    getAllowedCurEip(*pu.cu, compId, eipInfoList, true);
+  }
+  else
+  {
+    getAllowedCurEip(*pu.cu, compId, eipInfoList, pu.cu->eipMmFlag);
+  }
+#else
+  getAllowedCurEip(*pu.cu, compId, eipInfoList, m_numSigEip);
+#endif
+  memset(mmEipATABuf, 0, sizeof(TCccmCoeff) * 2 * NUM_EIP_COMB * sizeAtaBuf);
+  memset(mmEipATYBuf, 0, sizeof(TCccmCoeff) * 2 * NUM_EIP_COMB * EIP_FILTER_TAP);
+#else
   getAllowedCurEip(*pu.cu, compId, eipInfoList);
+#endif
   memset(ATABuf[0], 0, sizeof(TCccmCoeff) * NUM_EIP_COMB * sizeAtaBuf);
   memset(ATYBuf[0], 0, sizeof(TCccmCoeff) * NUM_EIP_COMB * EIP_FILTER_TAP);
   for (int i = 0; i < NUM_EIP_SHAPE * NUM_EIP_BASE_RECOTYPE; i++)
@@ -27587,13 +27822,35 @@ void IntraPrediction::getCurEipCands(const PredictionUnit& pu, static_vector<Eip
     eipInfoList.push_back(eipInfoBackup);
   }
   static_vector<int, NUM_EIP_BASE_RECOTYPE> refIdxList;
+#if MM_EIP
+#if MM_EIP_REFACTOR
+  bool bMmMode = pu.cs->pcv->isEncoder || pu.cu->eipMmFlag;
+#else
+  bool bMmMode = pu.cs->pcv->isEncoder || eipInfoList[0].bMm;
+#endif
+  int thrd = m_eipAvg;
+#endif
+#if MM_EIP_REFACTOR
+  for(int i = 0; i < eipInfoList.size(); i++)
+  {
+    EIPInfo mode = eipInfoList[i];
+    EipModelCandidate cand;
+    cand.bMm = pu.cs->pcv->isEncoder ? (i >= m_numSigEip): pu.cu->eipMmFlag;
+#else
   for (auto mode : eipInfoList)
   {
+#endif
     int recoType = mode.recoType, filterShape = mode.filterShape;
     CccmModel model(EIP_FILTER_TAP, bd);
     TCccmCoeff ATA[sizeAtaBuf]{ 0 };
     TCccmCoeff ATY[EIP_FILTER_TAP]{ 0 };
     int totalSamples = 0;
+#if MM_EIP
+    CccmModel model1(EIP_FILTER_TAP, bd);
+    TCccmCoeff ATA1[sizeAtaBuf]{ 0 };
+    TCccmCoeff ATY1[EIP_FILTER_TAP]{ 0 };
+    int totalSamples1 = 0;
+#endif
     // push needed refIdx for current mode.
     refIdxList.clear();
     if (recoType == EIP_AL || recoType == EIP_AL_A || recoType == EIP_AL_L || recoType == EIP_AL_A_L) 
@@ -27608,6 +27865,240 @@ void IntraPrediction::getCurEipCands(const PredictionUnit& pu, static_vector<Eip
     {
       refIdxList.push_back(EIP_L);
     }
+#if MM_EIP
+    for (auto refIdx: refIdxList)
+    {
+      const int srcBufIdx = filterShape + refIdx * NUM_EIP_BASE_RECOTYPE;
+      const int mmIdx0    = srcBufIdx << 1;
+      const int mmIdx1    = mmIdx0 + 1;
+
+      // fill src buffer
+      if (!bSrcBufFilled[srcBufIdx])
+      {
+        bSrcBufFilled[srcBufIdx] = true;
+
+        int startX, startY, endX, endY;
+
+        if (refIdx == EIP_AL)
+        {
+          startX = refSizeX - tplSize.x;
+          startY = refSizeY - tplSize.y;
+          endX   = refSizeX;
+          endY   = refSizeY;
+        }
+        else if (refIdx == EIP_A)
+        {
+          startX = refSizeX;
+          startY = refSizeY - tplSize.y;
+          endX   = refWidth;
+          endY   = refSizeY;
+        }
+        else   // EIP_L
+        {
+          startX = refSizeX - tplSize.x;
+          startY = refSizeY;
+          endX   = refSizeX;
+          endY   = refHeight;
+        }
+
+        if (bMmMode)
+        {
+          int numMmSamples[2] = {};
+
+          for (int y = startY; y < endY; y++)
+          {
+            for (int x = startX; x < endX; x++)
+            {
+              m_mmEipInputs[0][numInputs - 1][numMmSamples[0]] =  m_mmEipInputs[1][numInputs - 1][numMmSamples[1]] = m_eipBias;
+
+              for (int inputIdx = 0; inputIdx < numInputs - 1; inputIdx++)
+              {
+                m_mmEipInputs[0][inputIdx][numMmSamples[0]] =  m_mmEipInputs[1][inputIdx][numMmSamples[1]] = 
+                    refBuf.at(x + g_eipFilter[filterShape][inputIdx].x, y + g_eipFilter[filterShape][inputIdx].y);
+              }
+
+              m_mmEipYBuffer[mmIdx0][numMmSamples[0]] = m_mmEipYBuffer[mmIdx1][numMmSamples[1]] = refBuf.at(x, y);
+
+              const int ref =  filterShape == 0 ? ((m_mmEipInputs[0][0][numMmSamples[0]] + m_mmEipInputs[0][3][numMmSamples[0]] + 1) >> 1): m_mmEipInputs[0][1][numMmSamples[0]];
+
+              numMmSamples[0] += ref <= thrd ? 1: 0;
+              numMmSamples[1] += ref > thrd ? 1: 0;
+            }
+          }
+
+          for (int part = 0; part < 2; part++)
+          {
+            const int numSamples                      = numMmSamples[part];
+            numMmSamplesBuf[part][srcBufIdx]          = numSamples;
+            const int samplesInBatches                = (numSamples >> log2BatchSize) << log2BatchSize;
+            int       i                               = 0;
+            for (int coli0 = 0; coli0 < numInputs; coli0++)
+            {
+              for (int coli1 = coli0; coli1 < numInputs; coli1++)
+              {
+                for (int offset = 0; offset < samplesInBatches; offset += batchSize)
+                {
+                  mmEipATABuf[part][srcBufIdx][i] +=
+                    m_calcAeipGroupSum(&m_mmEipInputs[part][coli0][offset],
+                                        &m_mmEipInputs[part][coli1][offset], batchSize);
+                }
+                mmEipATABuf[part][srcBufIdx][i] += m_calcAeipGroupSum(
+                  &m_mmEipInputs[part][coli0][samplesInBatches],
+                  &m_mmEipInputs[part][coli1][samplesInBatches], numSamples - samplesInBatches);
+
+                i++;
+              }
+            }
+
+            for (int coli = 0; coli < numInputs; coli++)
+            {
+              for (int offset = 0; offset < samplesInBatches; offset += batchSize)
+              {
+                mmEipATYBuf[part][srcBufIdx][coli] +=
+                  m_calcAeipGroupSum(&m_mmEipInputs[part][coli][offset],
+                                      &m_mmEipYBuffer[mmIdx0 + part][offset], batchSize);
+              }
+              mmEipATYBuf[part][srcBufIdx][coli] += m_calcAeipGroupSum(
+                &m_mmEipInputs[part][coli][samplesInBatches],
+                &m_mmEipYBuffer[mmIdx0 + part][samplesInBatches], numSamples - samplesInBatches);
+            }
+          }
+        }
+        else
+        {
+          int numSamples = 0;
+          for (int y = startY; y < endY; y++)
+          {
+            for (int x = startX; x < endX; x++)
+            {
+#if JVET_AH0086_EIP_BIAS_AND_CLIP
+              m_a[numInputs - 1][numSamples] = m_eipBias;
+            
+              for (int inputIdx = 0; inputIdx < numInputs - 1; inputIdx++)
+#else
+              for (int inputIdx = 0; inputIdx < numInputs; inputIdx++)
+#endif
+              {
+                m_a[inputIdx][numSamples] = refBuf.at(x + g_eipFilter[filterShape][inputIdx].x, y + g_eipFilter[filterShape][inputIdx].y);
+              }
+              numSamples++;
+            }
+          }
+          numSamplesBuf[refIdx] = numSamples;
+
+          if (!bDstBufFilled[refIdx])
+          {
+            bDstBufFilled[refIdx] = true;
+            numSamples            = 0;
+            for (int y = startY; y < endY; y++)
+            {
+              for (int x = startX; x < endX; x++)
+              {
+                m_eipYBuffer[refIdx][numSamples++] = refBuf.at(x, y);
+              }
+            }
+          }
+
+          const int samplesInBatches = (numSamples >> log2BatchSize) << log2BatchSize;
+          int       i                = 0;
+          for (int coli0 = 0; coli0 < numInputs; coli0++)
+          {
+            for (int coli1 = coli0; coli1 < numInputs; coli1++)
+            {
+              for (int offset = 0; offset < samplesInBatches; offset += batchSize)
+              {
+                ATABuf[srcBufIdx][i] += m_calcAeipGroupSum(&m_a[coli0][offset], &m_a[coli1][offset], batchSize);
+              }
+              ATABuf[srcBufIdx][i] += m_calcAeipGroupSum(&m_a[coli0][samplesInBatches], &m_a[coli1][samplesInBatches],
+                                                          numSamples - samplesInBatches);
+              i++;
+            }
+          }
+
+          for (int coli = 0; coli < numInputs; coli++)
+          {
+            for (int offset = 0; offset < samplesInBatches; offset += batchSize)
+            {
+              ATYBuf[srcBufIdx][coli] +=
+                m_calcAeipGroupSum(&m_a[coli][offset], &m_eipYBuffer[refIdx][offset], batchSize);
+            }
+            ATYBuf[srcBufIdx][coli] += m_calcAeipGroupSum(
+              &m_a[coli][samplesInBatches], &m_eipYBuffer[refIdx][samplesInBatches], numSamples - samplesInBatches);
+          }
+        }
+      }
+#if MM_EIP_REFACTOR
+      if (cand.bMm)
+#else
+      if (mode.bMm)
+#endif
+      {
+        for (int i = 0; i < sizeAtaBuf; i++)
+        {
+          ATA[i] += mmEipATABuf[0][srcBufIdx][i];
+          ATA1[i] += mmEipATABuf[1][srcBufIdx][i];
+        }
+
+        for (int coli = 0; coli < numInputs; coli++)
+        {
+          ATY[coli] += mmEipATYBuf[0][srcBufIdx][coli];
+          ATY1[coli] += mmEipATYBuf[1][srcBufIdx][coli];
+        }
+        totalSamples += numMmSamplesBuf[0][srcBufIdx];
+        totalSamples1 += numMmSamplesBuf[1][srcBufIdx];
+      }
+      else if(pu.cs->pcv->isEncoder)
+      {
+        for (int i = 0; i < sizeAtaBuf; i++)
+        {
+          ATA[i] += (mmEipATABuf[0][srcBufIdx][i] + mmEipATABuf[1][srcBufIdx][i]);
+        }
+
+        for (int coli = 0; coli < numInputs; coli++)
+        {
+          ATY[coli] += (mmEipATYBuf[0][srcBufIdx][coli] + mmEipATYBuf[1][srcBufIdx][coli]);
+        }
+        totalSamples += (numMmSamplesBuf[0][srcBufIdx] + numMmSamplesBuf[1][srcBufIdx]);
+      }
+      else
+      {
+        for (int i = 0; i < sizeAtaBuf; i++)
+        {
+          ATA[i] += ATABuf[srcBufIdx][i];
+        }
+
+        for (int coli = 0; coli < numInputs; coli++)
+        {
+          ATY[coli] += ATYBuf[srcBufIdx][coli];
+        }
+        totalSamples += numSamplesBuf[refIdx];
+      }
+    }
+#if MM_EIP_REFACTOR
+    m_cccmSolver.solveEip(ATA, ATY, totalSamples, m_cccmLumaOffset, model, cand.bMm);
+#else
+    m_cccmSolver.solveEip(ATA, ATY, totalSamples, m_cccmLumaOffset, model, mode.bMm);
+    EipModelCandidate cand;
+#endif
+    cand.filterShape = filterShape;
+    memcpy(cand.params, model.params.data(), sizeof(cand.params));
+
+#if MM_EIP_REFACTOR
+    if(cand.bMm)
+#else
+    if(mode.bMm)
+#endif
+    {
+#if !MM_EIP_REFACTOR
+      cand.bMm = true;
+#endif  
+      cand.eipMmThrd = thrd;
+      m_cccmSolver.solveEip(ATA1, ATY1, totalSamples1, m_cccmLumaOffset, model1, true);
+      memcpy(cand.params1, model1.params.data(), sizeof(cand.params1));
+    }
+
+    candList.push_back(cand);
+#else
     for (auto refIdx : refIdxList)
     {
       const int srcBufIdx = filterShape + refIdx * NUM_EIP_BASE_RECOTYPE;
@@ -27716,6 +28207,7 @@ void IntraPrediction::getCurEipCands(const PredictionUnit& pu, static_vector<Eip
     cand.filterShape = filterShape;
     memcpy(cand.params, model.params.data(), sizeof(cand.params));
     candList.push_back(cand);
+#endif
   }
 }
 
@@ -27741,13 +28233,38 @@ void IntraPrediction::eipPred(const PredictionUnit& pu, PelBuf& piPred, const Co
   clipRng.max = std::min( clipRng.max, m_eipClipMax);
 #else
   const ClpRng clipRng = pu.cu->slice->clpRngs().comp[compId];
+#endif
+#if MM_EIP
+  if(model.bMm)
+  {
+    CccmModel cand1(EIP_FILTER_TAP, pu.cu->slice->getSPS()->getBitDepth(toChannelType(compId)));
+    memcpy(cand1.params.data(), model.params1, sizeof(model.params1));
+    const int thrd = model.eipMmThrd;
+    for (int scanIdx = 0; scanIdx < num; scanIdx++)
+    {
+      setInputsVec(inputs, predBuf, scan[scanIdx].x, scan[scanIdx].y, model.filterShape);
+
+      if (getEipInputsAvg(inputs, model.filterShape) <= thrd)
+      {
+        predBuf.at(scan[scanIdx].x, scan[scanIdx].y) = ClipPel(cand.convolve(inputs), clipRng);
+      }
+      else
+      {
+        predBuf.at(scan[scanIdx].x, scan[scanIdx].y) = ClipPel(cand1.convolve(inputs), clipRng);
+      }
+    }
+  }
+  else
+  {
 #endif
   for (int scanIdx = 0; scanIdx < num; scanIdx++)
   {
     setInputsVec(inputs, predBuf, scan[scanIdx].x, scan[scanIdx].y, model.filterShape);
     predBuf.at(scan[scanIdx].x, scan[scanIdx].y) = ClipPel(cand.convolve(inputs), clipRng);
   }
-
+#if MM_EIP
+  }
+#endif
   piPred.copyFrom(predBuf);
 }
 
@@ -28205,14 +28722,20 @@ void IntraPrediction::getNeiEipCands(const PredictionUnit& pu, static_vector<Eip
 #endif
 }
 
+#if BV_EIP_MRG
+void IntraPrediction::reorderEipCands(const PredictionUnit& pu, static_vector<EipModelCandidate, MAX_MERGE_EIP + NUM_DERIVED_BVEIP>& candList, const ComponentID compId)
+#else
 void IntraPrediction::reorderEipCands(const PredictionUnit& pu, static_vector<EipModelCandidate, MAX_MERGE_EIP>& candList, const ComponentID compId)
+#endif
 {
   if (candList.size() <= 1)
   {
     return;
   }
 
+#if !BV_EIP_MRG
   CHECK(candList.size() > MAX_MERGE_EIP, "candlist size is error.");
+#endif
   const int refSizeY = m_cccmBlkArea.y;
   const int refSizeX = m_cccmBlkArea.x;
   const int stride = m_cccmBlkArea.width;
@@ -28228,8 +28751,13 @@ void IntraPrediction::reorderEipCands(const PredictionUnit& pu, static_vector<Ei
   DistParam cDistParam;
   cDistParam.applyWeight = false;
   Distortion uiCost;
+#if BV_EIP_MRG
+  static_vector<Distortion, MAX_MERGE_EIP + NUM_DERIVED_BVEIP> candCostList;
+  static_vector<EipModelCandidate, MAX_MERGE_EIP + NUM_DERIVED_BVEIP> tmpCandList;
+#else
   static_vector<Distortion, MAX_MERGE_EIP> candCostList;
   static_vector<EipModelCandidate, MAX_MERGE_EIP> tmpCandList;
+#endif
   Pel inputs[EIP_FILTER_TAP];
   const ChannelType chType = toChannelType(compId);
 #if JVET_AH0086_EIP_BIAS_AND_CLIP
@@ -28243,17 +28771,36 @@ void IntraPrediction::reorderEipCands(const PredictionUnit& pu, static_vector<Ei
   {
     CccmModel cand(EIP_FILTER_TAP, pu.cu->slice->getSPS()->getBitDepth(toChannelType(compId)));
     memcpy(cand.params.data(), model.params, sizeof(model.params));
-
+#if MM_EIP
+    CccmModel cand1(EIP_FILTER_TAP, pu.cu->slice->getSPS()->getBitDepth(toChannelType(compId)));
+    Pel thrd = model.eipMmThrd;
+    memcpy(cand1.params.data(), model.params1, sizeof(model.params1));
+#endif
     uiCost = 0;
     for (int h = 0; h < EIP_TPL_SIZE; h++)
     {
       for (int w = 0; w < blockWidth; w++)
       {
         setInputsVec(inputs, recoTop, w, h, model.filterShape);
+#if MM_EIP
+        if (model.bMm && getEipInputsAvg(inputs, model.filterShape) > thrd)
+        {
+          predTop.at(w, h) = ClipPel(cand1.convolve(inputs), clipRng);
+        }
+        else
+        {
+          predTop.at(w, h) = ClipPel(cand.convolve(inputs), clipRng);
+        }
+#else
         predTop.at(w, h) = ClipPel(cand.convolve(inputs), clipRng);
+#endif
       }
     }
+#if TEST_3_10_A
+    m_dbvSadCost->setDistParam(cDistParam, predTop, recoTop, pu.cs->sps->getBitDepth(chType), compId, true);
+#else
     m_dbvSadCost->setDistParam(cDistParam, predTop, recoTop, pu.cs->sps->getBitDepth(chType), compId, false);
+#endif
     uiCost += cDistParam.distFunc(cDistParam);
 
     for (int h = 0; h < blockHeight; h++)
@@ -28261,10 +28808,25 @@ void IntraPrediction::reorderEipCands(const PredictionUnit& pu, static_vector<Ei
       for (int w = 0; w < EIP_TPL_SIZE; w++)
       {
         setInputsVec(inputs, recoLeft, w, h, model.filterShape);
+#if MM_EIP
+        if (model.bMm && getEipInputsAvg(inputs, model.filterShape) > thrd)
+        {
+          predLeft.at(w, h) = ClipPel(cand1.convolve(inputs), clipRng);
+        }
+        else
+        {
+          predLeft.at(w, h) = ClipPel(cand.convolve(inputs), clipRng);
+        }
+#else
         predLeft.at(w, h) = ClipPel(cand.convolve(inputs), clipRng);
+#endif
       }
     }
+#if TEST_3_10_A
+    m_dbvSadCost->setDistParam(cDistParam, predLeft, recoLeft, pu.cs->sps->getBitDepth(chType), compId, true);
+#else
     m_dbvSadCost->setDistParam(cDistParam, predLeft, recoLeft, pu.cs->sps->getBitDepth(chType), compId, false);
+#endif
     uiCost += cDistParam.distFunc(cDistParam);
 
     updateCandList(model, uiCost, tmpCandList, candCostList, NUM_EIP_MERGE_SIGNAL);
@@ -28275,5 +28837,345 @@ void IntraPrediction::reorderEipCands(const PredictionUnit& pu, static_vector<Ei
     candList.push_back(model);
   }
 }
+
+#if BV_EIP
+void IntraPrediction::initBvEipParams(const PredictionUnit& pu, const RefTemplateType templateType)
+{
+
+  const CodingUnit& cu = *pu.cu;
+  const CodingStructure& cs = *cu.cs;
+  const auto& area = pu.blocks[COMPONENT_Y];
+  const int height = area.height;
+  const int width = area.width;
+  const int tempOffsetX = templateType == ABOVE_TEMPLATE ? 0 : -TMP_EIP_TEMP_SIZE;
+  const int tempOffsetY = templateType == LEFT_TEMPLATE ? 0 : -TMP_EIP_TEMP_SIZE;
+  m_tempTEWidth = abs(tempOffsetX);
+  m_tempTEHeight = abs(tempOffsetY);
+
+  for (int i = 0; i < MAX_BV_LIST; i++)
+  {
+#if BV_EIP_QUICK_SEARCH
+    if (!m_tmpQuickCand || (!pu.cs->pcv->isEncoder && pu.cu->eipMerge))
+    {
+      break;
+    }
+
+    const int bvX = m_tmpXdispQuick;
+    const int bvY = m_tmpYdispQuick;
+#else
+    if (!m_tmpNumCand || (!pu.cs->pcv->isEncoder && pu.cu->eipMerge))
+    {
+      break;
+    }
+
+    const int bvX = m_tmpXdisp[i];
+    const int bvY = m_tmpYdisp[i];
+#endif
+
+    const int offsetX = pu.blocks[COMPONENT_Y].x + bvX;
+    const int offsetY = pu.blocks[COMPONENT_Y].y + bvY;
+    const Pel* piReco = cs.picture->getRecoBuf(COMPONENT_Y).buf;
+    const int picStride = cs.picture->getRecoBuf(COMPONENT_Y).stride;
+    CPelBuf refArea(piReco + offsetX + tempOffsetX + (offsetY + tempOffsetY) * picStride, picStride, width + m_tempTEWidth, height + m_tempTEHeight);
+    PelBuf target(m_bvEipBuffer[i], (width + m_tempTEWidth), width + m_tempTEWidth, height + m_tempTEHeight);
+    target.copyFrom(refArea);
+
+    m_bvEipLumaOffset[i] = pu.cs->picture->getRecoBuf(pu.blocks[COMPONENT_Y]).at(bvX, bvY);
+  }
+
+#if BV_EIP_MRG
+  for (int i = MAX_BV_LIST; i <= MAX_BV_LIST; i++)
+  {
+    if (m_bvBasedMergeCandidates.empty() || (!pu.cs->pcv->isEncoder && !pu.cu->eipMerge))
+    {
+      break;
+    }
+
+    const int bvX = m_bvBasedMergeCandidates[0].getHor();
+    const int bvY = m_bvBasedMergeCandidates[0].getVer();
+
+    const int offsetX = pu.blocks[COMPONENT_Y].x + bvX;
+    const int offsetY = pu.blocks[COMPONENT_Y].y + bvY;
+    const Pel* piReco = cs.picture->getRecoBuf(COMPONENT_Y).buf;
+    const int picStride = cs.picture->getRecoBuf(COMPONENT_Y).stride;
+    CPelBuf refArea(piReco + offsetX + tempOffsetX + (offsetY + tempOffsetY) * picStride, picStride, width + m_tempTEWidth, height + m_tempTEHeight);
+    PelBuf target(m_bvEipBuffer[i], (width + m_tempTEWidth), width + m_tempTEWidth, height + m_tempTEHeight);
+    target.copyFrom(refArea);
+
+    m_bvEipLumaOffset[i] = pu.cs->picture->getRecoBuf(pu.blocks[COMPONENT_Y]).at(bvX, bvY);
+  }
+#endif
+}
+
+
+void IntraPrediction::getBvEipCands(const PredictionUnit& pu, static_vector<EipModelCandidate, NUM_DERIVED_BVEIP>& candList, const ComponentID compId)
+{
+  if (!allowBvEip(*pu.cu, compId))
+  {
+    return;
+  }
+  const int width = pu.lwidth();
+  const int height = pu.lheight();
+  const int log2BatchSize = 10;
+  const int batchSize = (1 << log2BatchSize);
+  const int numInputs = EIP_FILTER_TAP;
+  const int sizeAtaBuf = ((EIP_FILTER_TAP + 1) * EIP_FILTER_TAP) >> 1;
+  const int bd = pu.cu->slice->getSPS()->getBitDepth(CHANNEL_TYPE_LUMA);
+  static_vector<EIP_FILTER_TYPE, NUM_DERIVED_BVEIP> eipFilterList;
+  getAllowedBvEip(*pu.cu, compId, eipFilterList);
+
+  if (!pu.cs->pcv->isEncoder) // decoder derives one set of coefficients.
+  {
+    const auto eipInfoBackup = eipFilterList[pu.intraDir[0]];
+    eipFilterList.clear();
+    eipFilterList.push_back(eipInfoBackup);
+  }
+
+  for (int bvIdx = 0; bvIdx < MAX_BV_LIST; bvIdx++)
+  {
+    memset(ATABuf[0], 0, sizeof(TCccmCoeff) * int(NUM_EIP_SHAPE) * sizeAtaBuf);
+    memset(ATYBuf[0], 0, sizeof(TCccmCoeff) * int(NUM_EIP_SHAPE) * EIP_FILTER_TAP);
+    PelBuf refBuf(m_bvEipBuffer[bvIdx], width + m_tempTEWidth, height + m_tempTEHeight);
+    for (const auto& filter : eipFilterList)
+    {
+      int totalSamples = 0;
+      CccmModel model(EIP_FILTER_TAP, bd);
+      TCccmCoeff ATA[sizeAtaBuf]{ 0 };
+      TCccmCoeff ATY[EIP_FILTER_TAP]{ 0 };
+
+      int startX, startY, endX, endY;
+      if (filter == EIP_FILTER_S)
+      {
+        startX = 3;
+        startY = 3;
+        endX = width + m_tempTEWidth;
+        endY = height + m_tempTEHeight;
+      }
+      else if (filter == EIP_FILTER_H)
+      {
+        startX = 7;
+        startY = 1;
+        endX = width + m_tempTEWidth;
+        endY = height + m_tempTEHeight;
+      }
+      else // EIP_FILTER_V
+      {
+        startX = 1;
+        startY = 7;
+        endX = width + m_tempTEWidth;
+        endY = height + m_tempTEHeight;
+      }
+      int numSamples = 0;
+      for (int y = startY; y < endY; y++)
+      {
+        for (int x = startX; x < endX; x++)
+        {
+#if JVET_AH0086_EIP_BIAS_AND_CLIP
+          m_a[numInputs - 1][numSamples] = m_eipBias;
+          for (int inputIdx = 0; inputIdx < numInputs - 1; inputIdx++)
+#else
+          for (int inputIdx = 0; inputIdx < numInputs; inputIdx++)
+#endif
+          {
+            m_a[inputIdx][numSamples] = refBuf.at(x + g_eipFilter[filter][inputIdx].x, y + g_eipFilter[filter][inputIdx].y);
+          }
+          numSamples++;
+        }
+      }
+      numSamplesBuf[filter] = numSamples;
+
+      numSamples = 0;
+      for (int y = startY; y < endY; y++)
+      {
+        for (int x = startX; x < endX; x++)
+        {
+          m_bvEipYBuffer[filter][numSamples++] = refBuf.at(x, y);
+        }
+      }
+
+      const int samplesInBatches = (numSamples >> log2BatchSize) << log2BatchSize;
+      int i = 0;
+      for (int coli0 = 0; coli0 < numInputs; coli0++)
+      {
+        for (int coli1 = coli0; coli1 < numInputs; coli1++)
+        {
+          for (int offset = 0; offset < samplesInBatches; offset += batchSize)
+          {
+            ATABuf[filter][i] += m_calcAeipGroupSum(&m_a[coli0][offset], &m_a[coli1][offset], batchSize);
+          }
+          ATABuf[filter][i] += m_calcAeipGroupSum(&m_a[coli0][samplesInBatches], &m_a[coli1][samplesInBatches], numSamples - samplesInBatches);
+          i++;
+        }
+      }
+
+      for (int coli = 0; coli < numInputs; coli++)
+      {
+        for (int offset = 0; offset < samplesInBatches; offset += batchSize)
+        {
+          ATYBuf[filter][coli] += m_calcAeipGroupSum(&m_a[coli][offset], &m_bvEipYBuffer[filter][offset], batchSize);
+        }
+        ATYBuf[filter][coli] += m_calcAeipGroupSum(&m_a[coli][samplesInBatches], &m_bvEipYBuffer[filter][samplesInBatches], numSamples - samplesInBatches);
+      }
+
+      for (int i = 0; i < sizeAtaBuf; i++)
+      {
+        ATA[i] += ATABuf[filter][i];
+      }
+
+      for (int coli = 0; coli < numInputs; coli++)
+      {
+        ATY[coli] += ATYBuf[filter][coli];
+      }
+      totalSamples += numSamplesBuf[filter];
+
+      m_cccmSolver.solveEip(ATA, ATY, totalSamples, m_bvEipLumaOffset[bvIdx], model);
+
+      EipModelCandidate cand;
+      cand.filterShape = filter;
+      memcpy(cand.params, model.params.data(), sizeof(cand.params));
+
+      candList.push_back(cand);
+    }
+  }
+}
+
+#if BV_EIP_MRG
+void IntraPrediction::getBvEipMergeCands(const PredictionUnit& pu, static_vector<EipModelCandidate, NUM_DERIVED_BVEIP>& candList, const ComponentID compId)
+{
+  if (!getAllowedEipMerge(*pu.cu, compId) || m_bvBasedMergeCandidates.empty())
+  {
+    return;
+  }
+
+  if (pu.Y().area() > MAX_BV_MRG_EIP_BLK || pu.Y().area() < MIN_BV_MRG_EIP_BLK)
+  {
+    return;
+  }
+
+  const int width = pu.lwidth();
+  const int height = pu.lheight();
+  const int log2BatchSize = 10;
+  const int batchSize = (1 << log2BatchSize);
+  const int numInputs = EIP_FILTER_TAP;
+  const int sizeAtaBuf = ((EIP_FILTER_TAP + 1) * EIP_FILTER_TAP) >> 1;
+  const int bd = pu.cu->slice->getSPS()->getBitDepth(CHANNEL_TYPE_LUMA);
+  static_vector<EIP_FILTER_TYPE, NUM_DERIVED_BVEIP> eipFilterList;
+#if BV_EIP_MRG_ONLY_1_CAND
+  eipFilterList.push_back(EIP_FILTER_S);
+#else
+  getAllowedBvEip(*pu.cu, compId, eipFilterList);
+#endif
+
+  for (int bvIdx = MAX_BV_LIST; bvIdx <= MAX_BV_LIST; bvIdx++)
+  {
+    memset(ATABuf[0], 0, sizeof(TCccmCoeff) * int(NUM_EIP_SHAPE) * sizeAtaBuf);
+    memset(ATYBuf[0], 0, sizeof(TCccmCoeff) * int(NUM_EIP_SHAPE) * EIP_FILTER_TAP);
+    PelBuf refBuf(m_bvEipBuffer[bvIdx], width + m_tempTEWidth, height + m_tempTEHeight);
+    for (const auto& filter : eipFilterList)
+    {
+      int totalSamples = 0;
+      CccmModel model(EIP_FILTER_TAP, bd);
+      TCccmCoeff ATA[sizeAtaBuf]{ 0 };
+      TCccmCoeff ATY[EIP_FILTER_TAP]{ 0 };
+
+      int startX, startY, endX, endY;
+      if (filter == EIP_FILTER_S)
+      {
+        startX = 3;
+        startY = 3;
+        endX = width + m_tempTEWidth;
+        endY = height + m_tempTEHeight;
+      }
+      else if (filter == EIP_FILTER_H)
+      {
+        startX = 7;
+        startY = 1;
+        endX = width + m_tempTEWidth;
+        endY = height + m_tempTEHeight;
+      }
+      else // EIP_FILTER_V
+      {
+        startX = 1;
+        startY = 7;
+        endX = width + m_tempTEWidth;
+        endY = height + m_tempTEHeight;
+      }
+      int numSamples = 0;
+      for (int y = startY; y < endY; y++)
+      {
+        for (int x = startX; x < endX; x++)
+        {
+#if JVET_AH0086_EIP_BIAS_AND_CLIP
+          m_a[numInputs - 1][numSamples] = m_eipBias;
+          for (int inputIdx = 0; inputIdx < numInputs - 1; inputIdx++)
+#else
+          for (int inputIdx = 0; inputIdx < numInputs; inputIdx++)
+#endif
+          {
+            m_a[inputIdx][numSamples] = refBuf.at(x + g_eipFilter[filter][inputIdx].x, y + g_eipFilter[filter][inputIdx].y);
+          }
+          numSamples++;
+        }
+      }
+      numSamplesBuf[filter] = numSamples;
+
+      numSamples = 0;
+      for (int y = startY; y < endY; y++)
+      {
+        for (int x = startX; x < endX; x++)
+        {
+          m_bvEipYBuffer[filter][numSamples++] = refBuf.at(x, y);
+        }
+      }
+
+      const int samplesInBatches = (numSamples >> log2BatchSize) << log2BatchSize;
+      int i = 0;
+      for (int coli0 = 0; coli0 < numInputs; coli0++)
+      {
+        for (int coli1 = coli0; coli1 < numInputs; coli1++)
+        {
+          for (int offset = 0; offset < samplesInBatches; offset += batchSize)
+          {
+            ATABuf[filter][i] += m_calcAeipGroupSum(&m_a[coli0][offset], &m_a[coli1][offset], batchSize);
+          }
+          ATABuf[filter][i] += m_calcAeipGroupSum(&m_a[coli0][samplesInBatches], &m_a[coli1][samplesInBatches], numSamples - samplesInBatches);
+          i++;
+        }
+      }
+
+      for (int coli = 0; coli < numInputs; coli++)
+      {
+        for (int offset = 0; offset < samplesInBatches; offset += batchSize)
+        {
+          ATYBuf[filter][coli] += m_calcAeipGroupSum(&m_a[coli][offset], &m_bvEipYBuffer[filter][offset], batchSize);
+        }
+        ATYBuf[filter][coli] += m_calcAeipGroupSum(&m_a[coli][samplesInBatches], &m_bvEipYBuffer[filter][samplesInBatches], numSamples - samplesInBatches);
+      }
+
+      for (int i = 0; i < sizeAtaBuf; i++)
+      {
+        ATA[i] += ATABuf[filter][i];
+      }
+
+      for (int coli = 0; coli < numInputs; coli++)
+      {
+        ATY[coli] += ATYBuf[filter][coli];
+      }
+      totalSamples += numSamplesBuf[filter];
+
+      m_cccmSolver.solveEip(ATA, ATY, totalSamples, m_bvEipLumaOffset[bvIdx], model);
+
+      EipModelCandidate cand;
+      cand.filterShape = filter;
+      //cand.isBvEip = pu.cs->pcv->isEncoder ? true : false;
+      cand.isBvEip = true;
+      memcpy(cand.params, model.params.data(), sizeof(cand.params));
+
+      candList.push_back(cand);
+    }
+  }
+}
+#endif
+#endif
 #endif
 //! \}
diff --git a/source/Lib/CommonLib/IntraPrediction.h b/source/Lib/CommonLib/IntraPrediction.h
index 486b1f6e..538ea900 100644
--- a/source/Lib/CommonLib/IntraPrediction.h
+++ b/source/Lib/CommonLib/IntraPrediction.h
@@ -223,7 +223,9 @@ struct CccmCovariance
   );
 #endif
 #if JVET_AG0058_EIP
-#if JVET_AB0174_CCCM_DIV_FREE
+#if MM_EIP
+  void solveEip                    ( const TCccmCoeff* A, const TCccmCoeff* Y, const int sampleNum, const int lumaOffset, CccmModel& model, bool bMm = false);
+#elif JVET_AB0174_CCCM_DIV_FREE
   void solveEip                    ( const TCccmCoeff* A, const TCccmCoeff* Y, const int sampleNum, const int lumaOffset, CccmModel& model );
 #else
   void solveEip                    ( const TCccmCoeff* A, const TCccmCoeff* Y, const int sampleNum, CccmModel& model );
@@ -320,6 +322,22 @@ protected:
   int        m_eipClipMin;
   int        m_eipClipMax;
 #endif
+#if MM_EIP
+  Pel        m_mmEipInputs[2][CCCM_NUM_PARAMS_MAX][CCCM_REF_SAMPLES_MAX];
+  Pel        m_eipAvg;
+  TCccmCoeff mmEipATABuf[2][NUM_EIP_COMB][((EIP_FILTER_TAP + 1) * EIP_FILTER_TAP) >> 1];
+  TCccmCoeff mmEipATYBuf[2][NUM_EIP_COMB][EIP_FILTER_TAP];
+  Pel        m_mmEipYBuffer[(NUM_EIP_SHAPE * NUM_EIP_BASE_RECOTYPE) << 1][MAX_EIP_SIZE * MAX_EIP_SIZE * 2]; 
+  int        numMmSamplesBuf[2][NUM_EIP_SHAPE * NUM_EIP_BASE_RECOTYPE];
+  int        m_numSigEip;
+#endif 
+#if BV_EIP
+  int        m_bvEipLumaOffset[MAX_BV_LIST * 2];
+  Pel        m_bvEipBuffer[MAX_BV_LIST * 2][(MAX_EIP_SIZE + TMP_TEMPLATE_SIZE) * (MAX_EIP_SIZE + TMP_TEMPLATE_SIZE)];
+  Pel        m_bvEipYBuffer[NUM_EIP_SHAPE][MAX_EIP_SIZE * MAX_EIP_SIZE];
+  int        m_tempTEWidth;
+  int        m_tempTEHeight;
+#endif
 #endif
 private:
 #if JVET_AG0136_INTRA_TMP_LIC
@@ -600,6 +618,11 @@ protected:
 #if JVET_AE0078_IBC_LIC_EXTENSION
 public:
 #endif
+#if BV_EIP_QUICK_SEARCH
+  bool               m_tmpQuickCand;
+  int                m_tmpXdispQuick;
+  int                m_tmpYdispQuick;
+#endif
 #if (JVET_AG0146_DIMD_ITMP_IBC || JVET_AG0152_SGPM_ITMP_IBC || JVET_AG0151_INTRA_TMP_MERGE_MODE)
   std::vector<Mv> m_bvBasedMergeCandidates;
 #if JVET_AH0200_INTRA_TMP_BV_REORDER
@@ -942,6 +965,13 @@ public:
   void getTmrlList(CodingUnit& cu);
 #endif
 #if JVET_AG0058_EIP
+#if BV_EIP
+  void initBvEipParams(const PredictionUnit& pu, const RefTemplateType templateType);
+  void getBvEipCands(const PredictionUnit& pu, static_vector<EipModelCandidate, NUM_DERIVED_BVEIP>& candList, const ComponentID compId = COMPONENT_Y);
+#if BV_EIP_MRG
+  void getBvEipMergeCands(const PredictionUnit& pu, static_vector<EipModelCandidate, NUM_DERIVED_BVEIP>& candList, const ComponentID compId = COMPONENT_Y);
+#endif
+#endif
   void initEipParams(const PredictionUnit& pu, const ComponentID compId);
   void eipPred(const PredictionUnit& pu, PelBuf& piPred, const ComponentID compId = COMPONENT_Y);
   void getCurEipCands(const PredictionUnit& pu, static_vector<EipModelCandidate, NUM_DERIVED_EIP>& candList, const ComponentID compId = COMPONENT_Y, const bool fastTest = true); 
@@ -949,10 +979,27 @@ public:
   static int64_t calcAeipGroupSum(const Pel* src1, const Pel* src2, const int numSamples);
 
   void getNeiEipCands(const PredictionUnit &pu, static_vector<EipModelCandidate, MAX_MERGE_EIP> &candList, const ComponentID compId = COMPONENT_Y);
+#if BV_EIP_MRG
+  void reorderEipCands(const PredictionUnit& pu, static_vector<EipModelCandidate, MAX_MERGE_EIP + NUM_DERIVED_BVEIP>& candList, const ComponentID compId = COMPONENT_Y);
+#else 
   void reorderEipCands(const PredictionUnit &pu, static_vector<EipModelCandidate, MAX_MERGE_EIP> &candList, const ComponentID compId = COMPONENT_Y);
+#endif
 #if JVET_AH0086_EIP_BIAS_AND_CLIP
   void setInputsVec(Pel *inputs, PelBuf &reco, int w, int h, int filterShape);
 #endif
+#if MM_EIP
+  inline Pel getEipInputsAvg(Pel *inputs, int filterShape)
+  {
+    if (filterShape == 0)
+    {
+      return (inputs[0] + inputs[3] + 1) >> 1;
+    }
+    else
+    {
+      return inputs[1];
+    }
+  }
+#endif
 #endif
 #if JVET_Z0056_GPM_SPLIT_MODE_REORDERING && JVET_Y0065_GPM_INTRA
 protected:
@@ -1307,7 +1354,9 @@ public:
     int getTmpNumCand() { return m_tmpNumCand; }
 #endif
 #endif
-
+#if BV_EIP_QUICK_SEARCH
+    int getTmpQuickCand() const { return m_tmpQuickCand; }
+#endif
 #ifdef TARGET_SIMD_X86
   void    initIntraX86();
   template <X86_VEXT vext>
diff --git a/source/Lib/CommonLib/RdCost.cpp b/source/Lib/CommonLib/RdCost.cpp
index 608a946e..277ec9e6 100644
--- a/source/Lib/CommonLib/RdCost.cpp
+++ b/source/Lib/CommonLib/RdCost.cpp
@@ -2346,6 +2346,142 @@ Distortion RdCost::xCalcHADs1xN(const Pel* piOrg, const Pel* piCur, int iStrideO
   return satd;
 }
 #endif
+
+#if TEST_3_10_A
+Distortion RdCost::xCalcHADs1x16( const Pel *piOrg, const Pel *piCur, int iStrideOrg, int iStrideCur, int iRows, int iCols)
+{
+  int j, sad = 0;
+  int diff[16], m1[16], m2[16];
+  memset(diff, 0, 16 * sizeof(int));
+  if (iRows == 1)
+  {
+    diff[0] = piOrg[0] - piCur[0];
+    diff[1] = piOrg[1] - piCur[1];
+    diff[2] = piOrg[2] - piCur[2];
+    diff[3] = piOrg[3] - piCur[3];
+    diff[4] = piOrg[4] - piCur[4];
+    diff[5] = piOrg[5] - piCur[5];
+    diff[6] = piOrg[6] - piCur[6];
+    diff[7] = piOrg[7] - piCur[7];
+
+    diff[8] = piOrg[8] - piCur[8];
+    diff[9] = piOrg[9] - piCur[9];
+    diff[10] = piOrg[10] - piCur[10];
+    diff[11] = piOrg[11] - piCur[11];
+    diff[12] = piOrg[12] - piCur[12];
+    diff[13] = piOrg[13] - piCur[13];
+    diff[14] = piOrg[14] - piCur[14];
+    diff[15] = piOrg[15] - piCur[15];
+  }
+  else if (iCols == 1)
+  {
+    diff[0] = piOrg[0] - piCur[0];
+    diff[1] = piOrg[1 * iStrideOrg] - piCur[1 * iStrideCur];
+    diff[2] = piOrg[2 * iStrideOrg] - piCur[2 * iStrideCur];
+    diff[3] = piOrg[3 * iStrideOrg] - piCur[3 * iStrideCur];
+    diff[4] = piOrg[4 * iStrideOrg] - piCur[4 * iStrideCur];
+    diff[5] = piOrg[5 * iStrideOrg] - piCur[5 * iStrideCur];
+    diff[6] = piOrg[6 * iStrideOrg] - piCur[6 * iStrideCur];
+    diff[7] = piOrg[7 * iStrideOrg] - piCur[7 * iStrideCur];
+    
+    diff[8] = piOrg[8 * iStrideOrg] - piCur[8 * iStrideCur];
+    diff[9] = piOrg[9 * iStrideOrg] - piCur[9 * iStrideCur];
+    diff[10] = piOrg[10 * iStrideOrg] - piCur[10 * iStrideCur];
+    diff[11] = piOrg[11 * iStrideOrg] - piCur[11 * iStrideCur];
+    diff[12] = piOrg[12 * iStrideOrg] - piCur[12 * iStrideCur];
+    diff[13] = piOrg[13 * iStrideOrg] - piCur[13 * iStrideCur];
+    diff[14] = piOrg[14 * iStrideOrg] - piCur[14 * iStrideCur];
+    diff[15] = piOrg[15 * iStrideOrg] - piCur[15 * iStrideCur];
+  }
+  else
+  {
+    CHECK(1, "shall not be here");
+  }
+
+  //horizontal
+  m2[0] = diff[0] + diff[8];
+  m2[1] = diff[1] + diff[9];
+  m2[2] = diff[2] + diff[10];
+  m2[3] = diff[3] + diff[11];
+  m2[4] = diff[4] + diff[12];
+  m2[5] = diff[5] + diff[13];
+  m2[6] = diff[6] + diff[14];
+  m2[7] = diff[7] + diff[15];
+  m2[8] = diff[0] - diff[8];
+  m2[9] = diff[1] - diff[9];
+  m2[10] = diff[2] - diff[10];
+  m2[11] = diff[3] - diff[11];
+  m2[12] = diff[4] - diff[12];
+  m2[13] = diff[5] - diff[13];
+  m2[14] = diff[6] - diff[14];
+  m2[15] = diff[7] - diff[15];
+
+  m1[0] = m2[0] + m2[4];
+  m1[1] = m2[1] + m2[5];
+  m1[2] = m2[2] + m2[6];
+  m1[3] = m2[3] + m2[7];
+  m1[4] = m2[0] - m2[4];
+  m1[5] = m2[1] - m2[5];
+  m1[6] = m2[2] - m2[6];
+  m1[7] = m2[3] - m2[7];
+  m1[8] = m2[8] + m2[12];
+  m1[9] = m2[9] + m2[13];
+  m1[10] = m2[10] + m2[14];
+  m1[11] = m2[11] + m2[15];
+  m1[12] = m2[8] - m2[12];
+  m1[13] = m2[9] - m2[13];
+  m1[14] = m2[10] - m2[14];
+  m1[15] = m2[11] - m2[15];
+
+  m2[0] = m1[0] + m1[2];
+  m2[1] = m1[1] + m1[3];
+  m2[2] = m1[0] - m1[2];
+  m2[3] = m1[1] - m1[3];
+  m2[4] = m1[4] + m1[6];
+  m2[5] = m1[5] + m1[7];
+  m2[6] = m1[4] - m1[6];
+  m2[7] = m1[5] - m1[7];
+  m2[8] = m1[8] + m1[10];
+  m2[9] = m1[9] + m1[11];
+  m2[10] = m1[8] - m1[10];
+  m2[11] = m1[9] - m1[11];
+  m2[12] = m1[12] + m1[14];
+  m2[13] = m1[13] + m1[15];
+  m2[14] = m1[12] - m1[14];
+  m2[15] = m1[13] - m1[15];
+
+  m1[0] = m2[0] + m2[1];
+  m1[1] = m2[0] - m2[1];
+  m1[2] = m2[2] + m2[3];
+  m1[3] = m2[2] - m2[3];
+  m1[4] = m2[4] + m2[5];
+  m1[5] = m2[4] - m2[5];
+  m1[6] = m2[6] + m2[7];
+  m1[7] = m2[6] - m2[7];
+  m1[8] = m2[8] + m2[9];
+  m1[9] = m2[8] - m2[9];
+  m1[10] = m2[10] + m2[11];
+  m1[11] = m2[10] - m2[11];
+  m1[12] = m2[12] + m2[13];
+  m1[13] = m2[12] - m2[13];
+  m1[14] = m2[14] + m2[15];
+  m1[15] = m2[14] - m2[15];
+
+  for( j = 0; j < 16; j++ )
+  {
+    sad += abs( m2[j] );
+  }
+
+#if JVET_R0164_MEAN_SCALED_SATD && !SATD_FOR_ARMC_RM_MEAN_SCALE
+  sad -= abs(m2[0]);
+  sad += abs(m2[0]) >> 2;
+#endif
+  sad  = ( int ) ( sad / sqrt( 16.0 * 1 ) * 2 );
+
+  return sad;
+}
+#endif
+
 Distortion RdCost::xCalcHADs2x2( const Pel *piOrg, const Pel *piCur, int iStrideOrg, int iStrideCur, int iStep )
 {
   Distortion satd = 0;
@@ -3134,6 +3270,24 @@ Distortion RdCost::xGetHADs( const DistParam &rcDtParam )
       piCur += iOffsetCur;
     }
   }
+#if TEST_3_10_A
+  else if (iRows == 1 && iCols % 16 == 0)
+  {
+    for( x = 0; x < iCols; x += 16 )
+    {
+      uiSum += xCalcHADs1x16(&piOrg[x], &piCur[x], iStrideOrg, iStrideCur, iRows, 16);
+    }
+  }
+  else if (iCols == 1 && iRows % 16 == 0)
+  {
+    for( y = 0; y < iRows; y += 16 )
+    {
+      uiSum += xCalcHADs1x16( &piOrg[0], &piCur[0], iStrideOrg, iStrideCur, 16, iCols );
+      piOrg += (iStrideOrg << 4);
+      piCur += (iStrideCur << 4);
+    }
+  }
+#endif
 #if JVET_AI0185_ADAPTIVE_COST_IN_MERGE_MODE
   else if (iRows == 1 || iCols == 1)
   {
diff --git a/source/Lib/CommonLib/RdCost.h b/source/Lib/CommonLib/RdCost.h
index 57dce9fb..aa760188 100644
--- a/source/Lib/CommonLib/RdCost.h
+++ b/source/Lib/CommonLib/RdCost.h
@@ -1097,6 +1097,9 @@ private:
   static Distortion xGetHADs          ( const DistParam& pcDtParam );
 #if JVET_AI0185_ADAPTIVE_COST_IN_MERGE_MODE
   static Distortion xCalcHADs1xN      ( const Pel* piOrg, const Pel* piCurr, int iStrideOrg, int iStrideCur, int iRows, int iCols);
+#endif
+#if TEST_3_10_A
+  static Distortion xCalcHADs1x16     ( const Pel *piOrg, const Pel *piCur, int iStrideOrg, int iStrideCur, int iRows, int iCols);
 #endif
   static Distortion xCalcHADs2x2      ( const Pel *piOrg, const Pel *piCurr, int iStrideOrg, int iStrideCur, int iStep );
   static Distortion xCalcHADs4x4      ( const Pel *piOrg, const Pel *piCurr, int iStrideOrg, int iStrideCur, int iStep );
diff --git a/source/Lib/CommonLib/Rom.cpp b/source/Lib/CommonLib/Rom.cpp
index af927378..67f85bc1 100644
--- a/source/Lib/CommonLib/Rom.cpp
+++ b/source/Lib/CommonLib/Rom.cpp
@@ -4843,6 +4843,62 @@ const Position g_eipFilter[NUM_EIP_SHAPE][EIP_FILTER_TAP] =
   { Position( 0, -1), Position(-1,  0), Position(-1, -1), Position(-2,  0), Position(-2, -1), Position(-3,  0), Position(-3, -1), Position(-4,  0), Position(-4, -1), Position(-5,  0), Position(-5, -1), Position(-6,  0), Position(-6, -1), Position(-7,  0), Position(-7, -1) },
 };
 #endif
+#if MM_EIP
+const EIPInfo g_eipInfoLut[4][4][9] =
+{
+  {
+    { EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo() }, // 4x4, 0modes
+    { EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo() }, // 4x8, 0modes
+    { EIPInfo(EIP_AL_A_L, EIP_FILTER_S), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo() },// 4x16, 1modes
+    { EIPInfo(EIP_AL_A_L, EIP_FILTER_S), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo() },// 4x32, 1modes
+  },
+  {
+    { EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo() }, // 8x4, 0modes
+    { EIPInfo(EIP_AL_A_L, EIP_FILTER_S), EIPInfo(EIP_AL_A_L, EIP_FILTER_H), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo() }, // 8x8, 1modes
+    { EIPInfo(EIP_AL_A_L, EIP_FILTER_S), EIPInfo(EIP_AL_A_L, EIP_FILTER_H), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo() }, // 8x16, 1modes
+    { EIPInfo(EIP_AL_A_L, EIP_FILTER_S), EIPInfo(EIP_AL_A_L, EIP_FILTER_H), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo() }, // 8x32, 5modes
+  },
+  {
+    { EIPInfo(EIP_AL_A_L, EIP_FILTER_S), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo() }, // 16x4, 1modes
+    { EIPInfo(EIP_AL_A_L, EIP_FILTER_S), EIPInfo(EIP_AL_A_L, EIP_FILTER_V), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo()}, // 16x8, 1modes
+    { EIPInfo(EIP_AL_A_L, EIP_FILTER_S), EIPInfo(EIP_AL_A_L, EIP_FILTER_H), EIPInfo(EIP_AL_A_L, EIP_FILTER_V), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo()}, // 16x16, 9modes
+    { EIPInfo(EIP_AL_A_L, EIP_FILTER_S), EIPInfo(EIP_AL_A_L, EIP_FILTER_H), EIPInfo(EIP_AL_A_L, EIP_FILTER_V), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo()}, // 16x32, 9modes
+  },
+  {
+    { EIPInfo(EIP_AL_A_L, EIP_FILTER_S), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo() }, 
+    { EIPInfo(EIP_AL_A_L, EIP_FILTER_S), EIPInfo(EIP_AL_A_L, EIP_FILTER_V), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo()}, 
+    { EIPInfo(EIP_AL_A_L, EIP_FILTER_S), EIPInfo(EIP_AL_A_L, EIP_FILTER_V), EIPInfo(EIP_AL_A_L, EIP_FILTER_H), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo()}, 
+    { EIPInfo(EIP_AL_A_L, EIP_FILTER_S), EIPInfo(EIP_AL_A_L, EIP_FILTER_H), EIPInfo(EIP_AL_A_L, EIP_FILTER_V), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo()}, 
+  },
+};
+const EIPInfo g_mmEipInfoLut[4][4][9] =
+{
+  {
+    { EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo() }, 
+    { EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo() }, 
+    { EIPInfo(EIP_AL_A_L, EIP_FILTER_S), EIPInfo(EIP_AL_A_L, EIP_FILTER_H), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo() },
+    { EIPInfo(EIP_AL_A_L, EIP_FILTER_S), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo() },
+  },
+  {
+    { EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo() }, // 8x4, 0modes
+    { EIPInfo(EIP_AL_A_L, EIP_FILTER_S), EIPInfo(EIP_AL_A_L, EIP_FILTER_H), EIPInfo(EIP_AL_A_L, EIP_FILTER_V), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo() },
+    { EIPInfo(EIP_AL_A_L, EIP_FILTER_S), EIPInfo(EIP_AL_A_L, EIP_FILTER_H), EIPInfo(EIP_AL_A_L, EIP_FILTER_V), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo() },
+    { EIPInfo(EIP_AL_A_L, EIP_FILTER_S), EIPInfo(EIP_AL_A_L, EIP_FILTER_H), EIPInfo(EIP_AL_A_L, EIP_FILTER_V), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo() },
+  },
+  {
+    { EIPInfo(EIP_AL_A_L, EIP_FILTER_S), EIPInfo(EIP_AL_A_L, EIP_FILTER_V), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo() },
+    { EIPInfo(EIP_AL_A_L, EIP_FILTER_S), EIPInfo(EIP_AL_A_L, EIP_FILTER_V), EIPInfo(EIP_AL_A_L, EIP_FILTER_H), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo()},
+    { EIPInfo(EIP_AL_A_L, EIP_FILTER_S), EIPInfo(EIP_AL_A_L, EIP_FILTER_V), EIPInfo(EIP_AL_A_L, EIP_FILTER_H), EIPInfo(EIP_AL_L, EIP_FILTER_S), EIPInfo(EIP_AL_A, EIP_FILTER_S), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo()},
+    { EIPInfo(EIP_AL_A_L, EIP_FILTER_S), EIPInfo(EIP_AL_A_L, EIP_FILTER_V), EIPInfo(EIP_AL_A_L, EIP_FILTER_H), EIPInfo(EIP_AL_L, EIP_FILTER_S), EIPInfo(EIP_AL_A, EIP_FILTER_S), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo()},
+  },
+  {
+    { EIPInfo(EIP_AL_A_L, EIP_FILTER_S), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo() },
+    { EIPInfo(EIP_AL_A_L, EIP_FILTER_S), EIPInfo(EIP_AL_A_L, EIP_FILTER_V), EIPInfo(EIP_AL_A_L, EIP_FILTER_H), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo() },
+    { EIPInfo(EIP_AL_A_L, EIP_FILTER_S), EIPInfo(EIP_AL_A_L, EIP_FILTER_H), EIPInfo(EIP_AL_A_L, EIP_FILTER_V), EIPInfo(EIP_AL_A, EIP_FILTER_S), EIPInfo(EIP_AL_L, EIP_FILTER_S), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo()},
+    { EIPInfo(EIP_AL_A_L, EIP_FILTER_S), EIPInfo(EIP_AL_A_L, EIP_FILTER_H), EIPInfo(EIP_AL_A_L, EIP_FILTER_V), EIPInfo(EIP_AL_A, EIP_FILTER_S), EIPInfo(EIP_AL_L, EIP_FILTER_S), EIPInfo(), EIPInfo(), EIPInfo(), EIPInfo()},
+  },
+};
+#else
 const EIPInfo g_eipInfoLut[4][4][9] =
 {
   {
@@ -4871,6 +4927,7 @@ const EIPInfo g_eipInfoLut[4][4][9] =
   },
 };
 #endif
+#endif
 
 // initialize ROM variables
 void initROM()
diff --git a/source/Lib/CommonLib/Rom.h b/source/Lib/CommonLib/Rom.h
index 17e84598..251f3446 100644
--- a/source/Lib/CommonLib/Rom.h
+++ b/source/Lib/CommonLib/Rom.h
@@ -498,6 +498,9 @@ extern const Position g_eipFilter[NUM_EIP_SHAPE][EIP_FILTER_TAP - 1];
 extern const Position g_eipFilter[NUM_EIP_SHAPE][EIP_FILTER_TAP];
 #endif
 extern const EIPInfo  g_eipInfoLut[4][4][9];
+#if MM_EIP
+extern const EIPInfo  g_mmEipInfoLut[4][4][9];
+#endif
 #endif
 #if JVET_AG0276_LIC_SLOPE_ADJUST
 extern const int g_licSlopeDeltaSet[LIC_SLOPE_MAX_NUM_DELTA + 1];
diff --git a/source/Lib/CommonLib/TypeDef.h b/source/Lib/CommonLib/TypeDef.h
index 8d1d8515..b83db353 100644
--- a/source/Lib/CommonLib/TypeDef.h
+++ b/source/Lib/CommonLib/TypeDef.h
@@ -52,6 +52,45 @@
 #include <cstdint>
 
 
+#define TEST_2_19_a                                       0
+#define TEST_2_19_b                                       0
+#define TEST_2_19_c                                       1
+
+
+#define MM_EIP                                           (TEST_2_19_a || TEST_2_19_c)
+#if MM_EIP
+#define MM_EIP_REFACTOR                                   1
+#define EIP_CU_CHK_SAD                                    1
+#endif  
+static const double EIP_LNFST_MTS_RATE = 1.5;
+static const double ENC_EIP_SAD_CHK_RATE = 1.1;
+
+
+#define BV_EIP                                           (TEST_2_19_b || TEST_2_19_c)
+
+#if BV_EIP
+#define BV_EIP_FAST_ENC                                   1
+#define BV_EIP_FAST_ENC2                                  1
+#define BV_EIP_FAST_ENC3                                  1
+#define BV_EIP_QUICK_SEARCH                               1
+#define BV_EIP_MRG                                        1
+#define BV_EIP_MRG_ONLY_1_CAND                            1
+#endif
+
+#if MM_EIP || BV_EIP
+#define EIP_DIMD_OPT                                      1
+#define EIP_LFNST_FAST                                    1 // skip lfnst
+#define EIP_RA_ENC                                        1
+#define BV_EIP_RA_ENC                                     0
+#endif
+
+#if TEST_2_19_c
+#define BV_EIP_ENC                                        1
+#define BV_EIP_FAST_SKIP_FLM_LFNST                        1
+#define TEST_2_19_c_SIGNALLING                           (1 && TEST_2_19_c)
+#endif
+
+#define TEST_3_10_A                                       1
 
 #define BASE_ENCODER                                      1
 #define BASE_NORMATIVE                                    1
@@ -1907,8 +1946,14 @@ struct EIPInfo
   int recoType;
   int filterShape;
 
+#if MM_EIP && !MM_EIP_REFACTOR
+  bool bMm;
+  EIPInfo() : recoType(0), filterShape(0), bMm(false) {}
+  EIPInfo(int _recoType, int _filterShape, bool _bMm = false) : recoType(_recoType), filterShape(_filterShape), bMm(_bMm) {}
+#else
   EIPInfo() : recoType(0), filterShape(0) {}
   EIPInfo(int _recoType, int _filterShape) : recoType(_recoType), filterShape(_filterShape) {}
+#endif
 };
 #endif
 
diff --git a/source/Lib/CommonLib/Unit.cpp b/source/Lib/CommonLib/Unit.cpp
index 07188641..86efa858 100644
--- a/source/Lib/CommonLib/Unit.cpp
+++ b/source/Lib/CommonLib/Unit.cpp
@@ -425,6 +425,12 @@ CodingUnit& CodingUnit::operator=( const CodingUnit& other )
   eipFlag = other.eipFlag;
   eipMerge = other.eipMerge;
   eipModel = other.eipModel;
+#if MM_EIP
+  eipMmFlag = other.eipMmFlag;
+#endif
+#if BV_EIP
+  bvEip = other.bvEip;
+#endif
 #endif
 #if ENABLE_OBMC
   obmcFlag          = other.obmcFlag;
@@ -724,6 +730,12 @@ void CodingUnit::initData()
 #if JVET_AG0058_EIP
   eipFlag = false;
   eipMerge = false;
+#if MM_EIP
+  eipMmFlag = false;
+#endif
+#if BV_EIP
+  bvEip = false;
+#endif
 #endif
 #if ENABLE_OBMC
   obmcFlag          = true;
diff --git a/source/Lib/CommonLib/Unit.h b/source/Lib/CommonLib/Unit.h
index fb1d0386..35f60945 100644
--- a/source/Lib/CommonLib/Unit.h
+++ b/source/Lib/CommonLib/Unit.h
@@ -424,8 +424,14 @@ struct CodingUnit : public UnitArea
 #endif
 #if JVET_AG0058_EIP
   bool eipFlag;
+#if BV_EIP
+  bool bvEip;
+#endif
   bool              eipMerge;
   EipModelCandidate eipModel;
+#if MM_EIP
+  bool              eipMmFlag;
+#endif
 #endif
 #if ENABLE_OBMC
   bool           obmcFlag;
diff --git a/source/Lib/CommonLib/UnitTools.cpp b/source/Lib/CommonLib/UnitTools.cpp
index fd87b46c..01a48f22 100644
--- a/source/Lib/CommonLib/UnitTools.cpp
+++ b/source/Lib/CommonLib/UnitTools.cpp
@@ -1370,7 +1370,11 @@ void getNeighBv(const PredictionUnit& puOrg, const PredictionUnit* pu, std::vect
 )
 {
 #if JVET_AI0082_GPM_WITH_INTER_IBC
+#if BV_EIP
+  if (!pu || ((pu->cu->predMode != MODE_IBC) && (!pu->cu->tmpFlag) && (!pu->cu->geoFlag) && !PU::hasEipBv(pu)))
+#else
   if (!pu || ((pu->cu->predMode != MODE_IBC) && (!pu->cu->tmpFlag) && (!pu->cu->geoFlag)))
+#endif
 #else
   if (!pu || ((pu->cu->predMode != MODE_IBC) && (!pu->cu->tmpFlag)))
 #endif
@@ -1437,7 +1441,11 @@ void getNeighBv(const PredictionUnit& puOrg, const PredictionUnit* pu, std::vect
     return;
   }
 
+#if BV_EIP
+  if (pu && (pu->cu->tmpFlag || PU::hasEipBv(pu)))
+#else
   if (pu && pu->cu->tmpFlag)
+#endif
   {
     if (PU::validItmpBv(puOrg, pu->bv.hor, pu->bv.ver))
     {
@@ -1468,6 +1476,9 @@ void getNeighBv(const PredictionUnit& puOrg, const PredictionUnit* pu, std::vect
     if (pu->cu->tmpIdx > 0
 #if JVET_AG0136_INTRA_TMP_LIC
       && !pu->cu->tmpLicFlag
+#endif
+#if BV_EIP
+      && !PU::hasEipBv(pu)
 #endif
       )
     {
@@ -1562,7 +1573,11 @@ void PU::getSparseArBvMergeCandidate(const PredictionUnit& pu, std::vector<Mv>&
     for (int n = 0; n < 5 && pBvs.size() < totalNum; n++)
     {
       const PredictionUnit* puCascaded = pu.cs->getPURestricted(posCand[n].offset(offsetX, offsetY), pu, pu.chType);
-      if (!puCascaded || ((puCascaded->cu->predMode != MODE_IBC) && (!puCascaded->cu->tmpFlag)))
+      if (!puCascaded || ((puCascaded->cu->predMode != MODE_IBC) && (!puCascaded->cu->tmpFlag)
+#if BV_EIP
+        && !hasEipBv(puCascaded)
+#endif
+        ))
       {
         continue;
       }
@@ -5629,7 +5644,11 @@ bool PU::dbvModeAvail(const PredictionUnit &pu)
 #endif
   {
     const PredictionUnit &lumaPU = PU::getCoLocatedLumaPU(pu);
+#if BV_EIP
+    return lumaPU.cu->tmpFlag || CU::isIBC(*lumaPU.cu) || hasEipBv(lumaPU);
+#else
     return lumaPU.cu->tmpFlag || CU::isIBC(*lumaPU.cu);
+#endif
   }
 #endif
   CompArea lumaArea = CompArea(COMPONENT_Y, pu.chromaFormat, pu.Cb().lumaPos(), recalcSize(pu.chromaFormat, CHANNEL_TYPE_CHROMA, CHANNEL_TYPE_LUMA, pu.Cb().size()));
@@ -5645,7 +5664,11 @@ bool PU::dbvModeAvail(const PredictionUnit &pu)
     const PredictionUnit *lumaPU = pu.cs->picture->cs->getPU(posList[n], CHANNEL_TYPE_LUMA);
 #endif
 #if JVET_AB0061_ITMP_BV_FOR_IBC
+#if BV_EIP
+    if (CU::isIBC(*lumaPU->cu) || isTmp(*lumaPU) || hasEipBv(lumaPU))
+#else
     if (CU::isIBC(*lumaPU->cu) || isTmp(*lumaPU))
+#endif
 #else
     if (CU::isIBC(*lumaPU->cu))
 #endif
@@ -5705,7 +5728,11 @@ void PU::deriveChromaBv(PredictionUnit &pu)
     const PredictionUnit *lumaPU = pu.cs->picture->cs->getPU(posList[n], CHANNEL_TYPE_LUMA);
 #endif
 #if JVET_AB0061_ITMP_BV_FOR_IBC
+#if BV_EIP
+    if (CU::isIBC(*lumaPU->cu) || isTmp(*lumaPU) || hasEipBv(lumaPU))
+#else
     if (CU::isIBC(*lumaPU->cu) || isTmp(*lumaPU))
+#endif
 #else
     if (CU::isIBC(*lumaPU->cu))
 #endif
@@ -6804,7 +6831,11 @@ void PU::getIBCMergeCandidates(const PredictionUnit &pu, MergeCtx& mrgCtx, const
   }
 #endif
 #if JVET_AB0061_ITMP_BV_FOR_IBC
-  const bool isAvailableA1 = puLeft && pu.cu != puLeft->cu && (CU::isIBC(*puLeft->cu) || puLeft->cu->tmpFlag);
+  const bool isAvailableA1 = puLeft && pu.cu != puLeft->cu && (CU::isIBC(*puLeft->cu) || puLeft->cu->tmpFlag
+#if BV_EIP
+    || hasEipBv(puLeft)
+#endif
+    );
 #else
   const bool isAvailableA1 = puLeft && pu.cu != puLeft->cu && CU::isIBC(*puLeft->cu);
 #endif
@@ -6966,7 +6997,11 @@ void PU::getIBCMergeCandidates(const PredictionUnit &pu, MergeCtx& mrgCtx, const
   // above
   const PredictionUnit *puAbove = cs.getPURestricted(posRT.offset(0, -1), pu, pu.chType);
 #if JVET_AB0061_ITMP_BV_FOR_IBC
-  bool isAvailableB1 = puAbove && pu.cu != puAbove->cu && (CU::isIBC(*puAbove->cu) || puAbove->cu->tmpFlag);
+  bool isAvailableB1 = puAbove && pu.cu != puAbove->cu && (CU::isIBC(*puAbove->cu) || puAbove->cu->tmpFlag
+#if BV_EIP
+    || hasEipBv(puAbove)
+#endif
+    );
 #else
   bool isAvailableB1 = puAbove && pu.cu != puAbove->cu && CU::isIBC(*puAbove->cu);
 #endif
@@ -7144,7 +7179,11 @@ void PU::getIBCMergeCandidates(const PredictionUnit &pu, MergeCtx& mrgCtx, const
   // above right
   const PredictionUnit *puAboveRight = cs.getPURestricted(posRT.offset(1, -1), pu, pu.chType);
 #if JVET_AB0061_ITMP_BV_FOR_IBC
-  bool isAvailableB0 = puAboveRight && pu.cu != puAboveRight->cu && (CU::isIBC(*puAboveRight->cu) || puAboveRight->cu->tmpFlag);
+  bool isAvailableB0 = puAboveRight && pu.cu != puAboveRight->cu && (CU::isIBC(*puAboveRight->cu) || puAboveRight->cu->tmpFlag
+#if BV_EIP
+    || hasEipBv(puAboveRight)
+#endif
+    );
 #else
   bool isAvailableB0 = puAboveRight && pu.cu != puAboveRight->cu && CU::isIBC(*puAboveRight->cu);
 #endif
@@ -7313,7 +7352,11 @@ void PU::getIBCMergeCandidates(const PredictionUnit &pu, MergeCtx& mrgCtx, const
   //left bottom
   const PredictionUnit *puLeftBottom = cs.getPURestricted(posLB.offset(-1, 1), pu, pu.chType);
 #if JVET_AB0061_ITMP_BV_FOR_IBC
-  bool isAvailableA0 = puLeftBottom && pu.cu != puLeftBottom->cu && (CU::isIBC(*puLeftBottom->cu) || puLeftBottom->cu->tmpFlag);
+  bool isAvailableA0 = puLeftBottom && pu.cu != puLeftBottom->cu && (CU::isIBC(*puLeftBottom->cu) || puLeftBottom->cu->tmpFlag
+#if BV_EIP
+    || hasEipBv(puLeftBottom)
+#endif
+    );
 #else
   bool isAvailableA0 = puLeftBottom && pu.cu != puLeftBottom->cu && CU::isIBC(*puLeftBottom->cu);
 #endif
@@ -7492,7 +7535,11 @@ void PU::getIBCMergeCandidates(const PredictionUnit &pu, MergeCtx& mrgCtx, const
   {
     const PredictionUnit *puAboveLeft = cs.getPURestricted(posLT.offset(-1, -1), pu, pu.chType);
 #if JVET_AB0061_ITMP_BV_FOR_IBC
-    bool isAvailableB2 = puAboveLeft && pu.cu != puAboveLeft->cu && (CU::isIBC(*puAboveLeft->cu) || puAboveLeft->cu->tmpFlag);
+    bool isAvailableB2 = puAboveLeft && pu.cu != puAboveLeft->cu && (CU::isIBC(*puAboveLeft->cu) || puAboveLeft->cu->tmpFlag
+#if BV_EIP
+      || hasEipBv(puAboveLeft)
+#endif
+      );
 #else
     bool isAvailableB2 = puAboveLeft && pu.cu != puAboveLeft->cu && CU::isIBC(*puAboveLeft->cu);
 #endif
@@ -7735,7 +7782,11 @@ void PU::getIBCMergeCandidates(const PredictionUnit &pu, MergeCtx& mrgCtx, const
         }
 
         const PredictionUnit *puNonAdjacent = cs.getPURestricted(posLT.offset(offsetX, offsetY), pu, pu.chType);
-        bool isAvailableNonAdjacent = puNonAdjacent && pu.cu != puNonAdjacent->cu && (CU::isIBC(*puNonAdjacent->cu) || puNonAdjacent->cu->tmpFlag);
+        bool isAvailableNonAdjacent = puNonAdjacent && pu.cu != puNonAdjacent->cu && (CU::isIBC(*puNonAdjacent->cu) || puNonAdjacent->cu->tmpFlag
+#if BV_EIP
+          || hasEipBv(puNonAdjacent)
+#endif
+          );
 
 #if JVET_AI0082_GPM_WITH_INTER_IBC
         bool isAvailableNonAdjacentGpmIbc = puNonAdjacent && pu.cu != puNonAdjacent->cu && CU::isInter(*puNonAdjacent->cu) && puNonAdjacent->cu->geoFlag;
@@ -7954,7 +8005,11 @@ void PU::getIBCMergeCandidates(const PredictionUnit &pu, MergeCtx& mrgCtx, const
         }
 
         const PredictionUnit *puNonAdjacent = cs.getPURestricted(posLT.offset(offsetX, offsetY), pu, pu.chType);
-        bool isAvailableNonAdjacent = puNonAdjacent && pu.cu != puNonAdjacent->cu && (CU::isIBC(*puNonAdjacent->cu) || puNonAdjacent->cu->tmpFlag);
+        bool isAvailableNonAdjacent = puNonAdjacent && pu.cu != puNonAdjacent->cu && (CU::isIBC(*puNonAdjacent->cu) || puNonAdjacent->cu->tmpFlag
+#if BV_EIP
+          || hasEipBv(puNonAdjacent)
+#endif
+          );
 
 #if JVET_AI0082_GPM_WITH_INTER_IBC
         bool isAvailableNonAdjacentGpmIbc = puNonAdjacent && pu.cu != puNonAdjacent->cu && CU::isInter(*puNonAdjacent->cu) && puNonAdjacent->cu->geoFlag;
@@ -8254,7 +8309,11 @@ void PU::getIBCMergeCandidates(const PredictionUnit &pu, MergeCtx& mrgCtx, const
       {
         const PredictionUnit* puCascaded = cs.getPURestricted(posCand[n].offset(offsetX, offsetY), pu, pu.chType);
         bool                  isAvailableCascaded =
-          puCascaded && pu.cu != puCascaded->cu && (CU::isIBC(*puCascaded->cu) || puCascaded->cu->tmpFlag);
+          puCascaded && pu.cu != puCascaded->cu && (CU::isIBC(*puCascaded->cu) || puCascaded->cu->tmpFlag
+#if BV_EIP
+            || hasEipBv(puCascaded)
+#endif
+            );
 #if JVET_AI0082_GPM_WITH_INTER_IBC
         bool isAvailableCascadedGpm = puCascaded && pu.cu != puCascaded->cu && CU::isInter(*puCascaded->cu) && puCascaded->cu->geoFlag;
         if (isAvailableCascadedGpm)
@@ -27481,8 +27540,13 @@ void PU::spanMotionInfo( PredictionUnit &pu, const MergeCtx &mrgCtx )
     MotionInfo mi;
 
 #if JVET_AB0061_ITMP_BV_FOR_IBC
+#if BV_EIP
+    mi.isInter = !CU::isIntra(*pu.cu) || pu.cu->tmpFlag || hasEipBv(pu);
+    mi.isIBCmot = CU::isIBC(*pu.cu) || pu.cu->tmpFlag || hasEipBv(pu);
+#else
     mi.isInter  = !CU::isIntra(*pu.cu) || pu.cu->tmpFlag;
     mi.isIBCmot = CU::isIBC(*pu.cu) || pu.cu->tmpFlag;
+#endif
 #else
     mi.isInter = !CU::isIntra(*pu.cu);
     mi.isIBCmot = CU::isIBC(*pu.cu);
@@ -33585,14 +33649,19 @@ bool PU::isEIP(const PredictionUnit& pu, const ChannelType& chType)
 
 Position getRecoLinesEIP(const CodingUnit& cu, const ComponentID compId)
 {
+#if MM_EIP
+  const int numRecoLines = std::min<int>(std::min(cu.blocks[compId].width, cu.blocks[compId].height), MAX_EIP_DERIVED_SIZE);
+  const int left = std::min(cu.blocks[compId].y - EIP_FILTER_SIZE, numRecoLines);
+  const int above = std::min(cu.blocks[compId].x - EIP_FILTER_SIZE, numRecoLines);
+#else
   const int numRecoLines = std::min(cu.blocks[compId].width, cu.blocks[compId].height);
   const int above = std::min(cu.blocks[compId].y - EIP_FILTER_SIZE, numRecoLines);
   const int left = std::min(cu.blocks[compId].x - EIP_FILTER_SIZE, numRecoLines);
+#endif
   CHECK(above < 1 || left < 1, "no reconstruction lines above or left.");
 
   return Position(above, left);
 }
-
 bool getAllowedEip(const CodingUnit& cu, const ComponentID compId)
 {
   if (!cu.cs->sps->getUseEip())
@@ -33615,7 +33684,12 @@ bool getAllowedEip(const CodingUnit& cu, const ComponentID compId)
 
   const int numAboveAvail = cu.blocks[compId].y;
   const int numLeftAvail = cu.blocks[compId].x;
+#if MM_EIP
+  const int numRecoLines = std::min<int>(std::min(cu.blocks[compId].width, cu.blocks[compId].height), MAX_EIP_DERIVED_SIZE);
+  const int tplSize = numRecoLines + EIP_FILTER_SIZE;
+#else
   const int tplSize = std::min(cu.blocks[compId].height, cu.blocks[compId].width) + EIP_FILTER_SIZE;
+#endif
   if(numAboveAvail < tplSize || numLeftAvail < tplSize)
   {
     return false;
@@ -33649,9 +33723,58 @@ bool getAllowedEipMerge(const CodingUnit& cu, const ComponentID compId)
 
   return false;
 }
-
+#if MM_EIP_REFACTOR
+int getAllowedCurEip(const CodingUnit &cu, const ComponentID compId, static_vector<EIPInfo, NUM_DERIVED_EIP>& eipInfoList, bool bMmEip)
+{ 
+  const int log2Wm2 = floorLog2(cu.blocks[compId].width)  - 2;
+  const int log2Hm2 = floorLog2(cu.blocks[compId].height) - 2;
+  if(!bMmEip)
+  {
+    const int numOfCombEIP[4][4] = 
+    {
+      { 0, 0, 1, 1 },
+      { 0, 2, 2, 2 },
+      { 1, 2, 3, 3 },
+      { 1, 2, 3, 3 },
+    };
+    for (int i = 0; i < numOfCombEIP[log2Wm2][log2Hm2]; i++)
+    {
+      eipInfoList.push_back(g_eipInfoLut[log2Wm2][log2Hm2][i]);
+    }
+  }
+  else
+  {
+    const int numOfCombMmEIP[4][4] = 
+    {
+      { 0, 0, 2, 1 },
+      { 0, 3, 3, 3 },
+      { 2, 3, 5, 5 },
+      { 1, 3, 5, 5 },
+    };
+    for (int i = 0; i < numOfCombMmEIP[log2Wm2][log2Hm2]; i++)
+    {
+      EIPInfo mmInfo = g_mmEipInfoLut[log2Wm2][log2Hm2][i];
+      eipInfoList.push_back(mmInfo);
+    }
+  }
+  return int(eipInfoList.size());
+}
+#else
+#if MM_EIP
+int getAllowedCurEip(const CodingUnit &cu, const ComponentID compId, static_vector<EIPInfo, NUM_DERIVED_EIP>& eipInfoList, int& numSigEip, bool bMmEip)
+#else
 int getAllowedCurEip(const CodingUnit &cu, const ComponentID compId, static_vector<EIPInfo, NUM_DERIVED_EIP>& eipInfoList)
+#endif
 { 
+#if MM_EIP
+  const int numOfCombEIP[4][4] = 
+  {
+    { 0, 0, 1, 1 },
+    { 0, 2, 2, 2 },
+    { 1, 2, 3, 3 },
+    { 1, 2, 3, 3 },
+  };
+#else
   const int numOfCombEIP[4][4] = 
   {
     { 3, 3, 3, 2 },
@@ -33659,16 +33782,34 @@ int getAllowedCurEip(const CodingUnit &cu, const ComponentID compId, static_vect
     { 3, 5, 9, 9 },
     { 2, 5, 9, 9 },
   };
+#endif
   const int log2Wm2 = floorLog2(cu.blocks[compId].width)  - 2;
   const int log2Hm2 = floorLog2(cu.blocks[compId].height) - 2;
   for (int i = 0; i < numOfCombEIP[log2Wm2][log2Hm2]; i++)
   {
     eipInfoList.push_back(g_eipInfoLut[log2Wm2][log2Hm2][i]);
   }
-
+#if MM_EIP
+  numSigEip = int(eipInfoList.size());
+  const int numOfCombMmEIP[4][4] = 
+  {
+      { 0, 0, 2, 1 },
+      { 0, 3, 3, 3 },
+      { 2, 3, 5, 5 },
+      { 1, 3, 5, 5 },
+  };
+  for (int i = 0; i < numOfCombMmEIP[log2Wm2][log2Hm2]; i++)
+  {
+    EIPInfo mmInfo = g_mmEipInfoLut[log2Wm2][log2Hm2][i];
+    mmInfo.bMm = true;
+    eipInfoList.push_back(mmInfo);
+  }
+  return bMmEip ? ((int)eipInfoList.size() - numSigEip): numSigEip;
+#else
   return int(eipInfoList.size());
+#endif
 }
-
+#endif
 void CU::saveModelsInHEIP(const CodingUnit &cu)
 {
   if (!cu.Y().valid())
@@ -33951,5 +34092,63 @@ void getTemporalBv(const PredictionUnit &pu, std::vector<MotionInfo>& temporalMi
   }
   addOneTypeTempCandidates(pu, temporalMiCandList, temporalPos);
 }
+
+#if BV_EIP
+bool allowBvEip(const CodingUnit& cu, const ComponentID compId)
+{
+#if TEST_2_19_c_SIGNALLING
+  return getAllowedEip(cu, compId);
+#else
+  if (!cu.cs->sps->getUseEip())
+  {
+    return false;
+  }
+
+  const uint32_t width = cu.blocks[compId].width;
+  const uint32_t height = cu.blocks[compId].height;
+
+  if (width * height <= MIN_BV_EIP_BLK)
+  {
+    return false;
+  }
+
+  if (width > MAX_EIP_SIZE || height > MAX_EIP_SIZE)
+  {
+    return false;
+  }
+
+  const int numAboveAvail = cu.blocks[compId].y;
+  const int numLeftAvail = cu.blocks[compId].x;
+
+  if ((numAboveAvail >= (EIP_FILTER_SIZE + EIP_TPL_SIZE)) && (numLeftAvail >= (EIP_FILTER_SIZE + EIP_TPL_SIZE)))
+  {
+    return true;
+  }
+
+  return false;
+#endif
+}
+
+int getAllowedBvEip(const CodingUnit& cu, const ComponentID compId, static_vector<EIP_FILTER_TYPE, NUM_DERIVED_BVEIP>& eipFilterList)
+{
+  for (int i = 0; i < NUM_DERIVED_BVEIP; i++)
+  {
+    eipFilterList.push_back(EIP_FILTER_TYPE(i));
+  }
+  return int(eipFilterList.size());
+}
+#endif
+
+#if BV_EIP
+bool PU::hasEipBv(const PredictionUnit& pu, const ChannelType& chType)
+{
+  return allowBvEip(*pu.cu, ComponentID(chType)) && (pu.cu->eipFlag && pu.cu->bvEip && chType == CH_L);
+}
+
+bool PU::hasEipBv(const PredictionUnit* pu, const ChannelType& chType)
+{
+  return allowBvEip(*pu->cu, ComponentID(chType)) && (pu->cu->eipFlag && pu->cu->bvEip && chType == CH_L);
+}
+#endif
 #endif
 
diff --git a/source/Lib/CommonLib/UnitTools.h b/source/Lib/CommonLib/UnitTools.h
index c55bc2be..cf505a91 100644
--- a/source/Lib/CommonLib/UnitTools.h
+++ b/source/Lib/CommonLib/UnitTools.h
@@ -259,6 +259,10 @@ namespace PU
 #endif
 #if JVET_AG0058_EIP
   bool isEIP(const PredictionUnit& pu, const ChannelType& chType = CHANNEL_TYPE_LUMA);
+#if BV_EIP
+  bool hasEipBv(const PredictionUnit& pu, const ChannelType& chType = CHANNEL_TYPE_LUMA);
+  bool hasEipBv(const PredictionUnit* pu, const ChannelType& chType = CHANNEL_TYPE_LUMA);
+#endif
 #endif
   bool          isDMChromaMIP         (const PredictionUnit &pu);
 #if JVET_AB0155_SGPM
@@ -1457,8 +1461,18 @@ void fillNonMPMList(uint8_t* mpm, uint8_t* non_mpm);
 Position getRecoLinesEIP(const CodingUnit& cu, const ComponentID compId);
 bool     getAllowedEipMerge(const CodingUnit &cu, const ComponentID compId);
 bool     getAllowedEip(const CodingUnit &cu, const ComponentID compId);
+#if MM_EIP_REFACTOR
+int getAllowedCurEip(const CodingUnit& cu, const ComponentID compId, static_vector<EIPInfo, NUM_DERIVED_EIP>& eipInfoList, bool bMmEip = false);
+#elif MM_EIP
+int getAllowedCurEip(const CodingUnit& cu, const ComponentID compId, static_vector<EIPInfo, NUM_DERIVED_EIP>& eipInfoList, int& numSigEip, bool bMmEip = false);
+#else
 int getAllowedCurEip(const CodingUnit& cu, const ComponentID compId, static_vector<EIPInfo, NUM_DERIVED_EIP>& eipInfoList);
 #endif
+#if BV_EIP
+bool allowBvEip(const CodingUnit& cu, const ComponentID compId);
+int getAllowedBvEip(const CodingUnit& cu, const ComponentID compId, static_vector<EIP_FILTER_TYPE, NUM_DERIVED_BVEIP>& eipFilterList);
+#endif
+#endif  
 
 #if JVET_AI0082_TEMPORAL_BV
 bool getColocatedBVP(const PredictionUnit &pu, const Position &posIn, MotionInfo &rcMv, const int col);
diff --git a/source/Lib/CommonLib/x86/RdCostX86.h b/source/Lib/CommonLib/x86/RdCostX86.h
index 8cac6fc5..4b6244d6 100644
--- a/source/Lib/CommonLib/x86/RdCostX86.h
+++ b/source/Lib/CommonLib/x86/RdCostX86.h
@@ -2350,6 +2350,145 @@ static uint32_t xCalcHAD8x16_AVX2( const Pel* piOrg, const Pel* piCur, const int
   return (sad);
 }
 
+#if TEST_3_10_A
+static uint32_t xCalcHADs1x16_SSE(const Torg* piOrg, const Tcur* piCur, int iStrideOrg, int iStrideCur, int iRows, int iCols)
+{
+  __m128i diff[4], m1[4], m2[4];
+
+  if (iRows == 1)
+  {
+    __m128i org0 = _mm_loadu_si128((__m128i*)&piOrg[0]);
+    __m128i cur0 = _mm_loadu_si128((__m128i*)&piCur[0]);
+    diff[0] = _mm_sub_epi16(org0, cur0);
+    diff[1] = _mm_cvtepi16_epi32( _mm_srli_si128( diff[0], 8 ) );
+    diff[0] = _mm_cvtepi16_epi32( diff[0] );
+
+    org0 = _mm_loadu_si128((__m128i*)&piOrg[8]);
+    cur0 = _mm_loadu_si128((__m128i*)&piCur[8]);
+    diff[2] = _mm_sub_epi16(org0, cur0);
+    diff[3] = _mm_cvtepi16_epi32( _mm_srli_si128( diff[2], 8 ) );
+    diff[2] = _mm_cvtepi16_epi32( diff[2] );
+  }
+  else if (iCols == 1)
+  {
+    Pel diffI[16];
+    for (int i = 0; i < 16; i++)
+    {
+      diffI[i] = piOrg[0] - piCur[0];
+      piOrg += iStrideOrg;
+      piCur += iStrideCur;
+    }
+    diff[0] = _mm_loadu_si128((__m128i*)&diffI[0]);
+    diff[1] = _mm_cvtepi16_epi32( _mm_srli_si128( diff[0], 8 ) );
+    diff[0] = _mm_cvtepi16_epi32( diff[0] );
+    diff[2] = _mm_loadu_si128((__m128i*)&diffI[8]);
+    diff[3] = _mm_cvtepi16_epi32( _mm_srli_si128( diff[2], 8 ) );
+    diff[2] = _mm_cvtepi16_epi32( diff[2] );
+  }
+  else
+  {
+    std::cerr << "shall not be here" << std::endl;
+    return -1;
+  }
+
+  m2[0] = _mm_add_epi32(diff[0], diff[2]);
+  m2[1] = _mm_add_epi32(diff[1], diff[3]);
+  m2[2] = _mm_sub_epi32(diff[0], diff[2]);
+  m2[3] = _mm_sub_epi32(diff[1], diff[3]);
+
+  m1[0] = _mm_add_epi32(m2[0], m2[1]);
+  m1[1] = _mm_sub_epi32(m2[0], m2[1]);
+  m1[2] = _mm_add_epi32(m2[2], m2[3]);
+  m1[3] = _mm_sub_epi32(m2[2], m2[3]);
+
+  m2[0] = _mm_unpacklo_epi32(m1[0], m1[1]);
+  m2[1] = _mm_unpackhi_epi32(m1[0], m1[1]);
+  m2[2] = _mm_abs_epi32(_mm_add_epi32(m2[0], m2[1]));
+#if JVET_R0164_MEAN_SCALED_SATD
+  uint32_t absDc = _mm_cvtsi128_si32(m2[2]);
+#endif
+  m2[3] = _mm_abs_epi32(_mm_sub_epi32(m2[0], m2[1]));
+  __m128i iSum = _mm_add_epi32(m2[2], m2[3]);
+  m2[0] = _mm_unpacklo_epi32(m1[2], m1[3]);
+  m2[1] = _mm_unpackhi_epi32(m1[2], m1[3]);
+  m2[2] = _mm_abs_epi32(_mm_add_epi32(m2[0], m2[1]));
+  m2[3] = _mm_abs_epi32(_mm_sub_epi32(m2[0], m2[1]));
+  iSum = _mm_add_epi32(iSum, m2[2]);
+  iSum = _mm_add_epi32(iSum, m2[3]);
+  iSum = _mm_add_epi32(iSum, _mm_shuffle_epi32(iSum, 0x4e));   // 01001110
+  iSum = _mm_add_epi32(iSum, _mm_shuffle_epi32(iSum, 0xb1));   // 10110001
+  uint32_t sad = _mm_cvtsi128_si32( iSum );
+#if JVET_R0164_MEAN_SCALED_SATD
+  sad -= absDc;
+  sad += absDc >> 2;
+#endif
+  sad = sad >> 1;
+
+  return sad;
+}
+
+static uint32_t xCalcHADs1x8_SSE(const Torg* piOrg, const Tcur* piCur, int iStrideOrg, int iStrideCur, int iRows, int iCols)
+{
+  __m128i diff[2], m1[2], m2[2];
+
+  if (iRows == 1)
+  {
+    __m128i org0 = _mm_loadu_si128((__m128i*)&piOrg[0]);
+    __m128i cur0 = _mm_loadu_si128((__m128i*)&piCur[0]);
+    diff[0] = _mm_sub_epi16(org0, cur0);
+    diff[1] = _mm_cvtepi16_epi32( _mm_srli_si128( diff[0], 8 ) );
+    diff[0] = _mm_cvtepi16_epi32( diff[0] );
+  }
+  else if (iCols == 1)
+  {
+    Pel diffI[8];
+    for (int i = 0; i < 8; i++)
+    {
+      diffI[i] = piOrg[0] - piCur[0];
+      piOrg += iStrideOrg;
+      piCur += iStrideCur;
+    }
+    diff[0] = _mm_loadu_si128((__m128i*)&diffI[0]);
+    diff[1] = _mm_cvtepi16_epi32( _mm_srli_si128( diff[0], 8 ) );
+    diff[0] = _mm_cvtepi16_epi32( diff[0] );
+  }
+  else
+  {
+    std::cerr << "shall not be here" << std::endl;
+    return -1;
+  }
+
+  m2[0] = _mm_add_epi32(diff[0], diff[1]);
+  m2[1] = _mm_sub_epi32(diff[0], diff[1]);
+
+  m1[0] = _mm_unpacklo_epi32(m2[0], m2[1]);
+  m1[1] = _mm_unpackhi_epi32(m2[0], m2[1]);
+  m2[0] = _mm_add_epi32(m1[0], m1[1]);
+  m2[1] = _mm_sub_epi32(m1[0], m1[1]);
+
+  m1[0] = _mm_unpacklo_epi32(m2[0], m2[1]);
+  m1[1] = _mm_unpackhi_epi32(m2[0], m2[1]);
+  m2[0] = _mm_abs_epi32(_mm_add_epi32(m1[0], m1[1]));
+#if JVET_R0164_MEAN_SCALED_SATD
+  uint32_t absDc = _mm_cvtsi128_si32(m2[0]);
+#endif
+  m2[1] = _mm_abs_epi32(_mm_sub_epi32(m1[0], m1[1]));
+
+  __m128i iSum = _mm_add_epi32(m2[0], m2[1]);
+  iSum = _mm_add_epi32(iSum, _mm_shuffle_epi32(iSum, 0x4e));   // 01001110
+  iSum = _mm_add_epi32(iSum, _mm_shuffle_epi32(iSum, 0xb1));   // 10110001
+  uint32_t sad = _mm_cvtsi128_si32( iSum );
+
+#if JVET_R0164_MEAN_SCALED_SATD
+  sad -= absDc;
+  sad += absDc >> 2;
+#endif
+  sad  = ( int ) ( sad / sqrt( 8.0 * 1 ) * 2 );
+
+  return sad;
+}
+#endif
+
 template< X86_VEXT vext >
 Distortion RdCost::xGetSADwMask_SIMD( const DistParam &rcDtParam )
 {
@@ -2570,6 +2709,40 @@ Distortion RdCost::xGetHADs_SIMD( const DistParam &rcDtParam )
       piCur += iOffsetCur;
     }
   }
+#if TEST_3_10_A
+  else if (iRows == 1 && iCols % 16 == 0)
+  {
+    for( x = 0; x < iCols; x += 16 )
+    {
+      uiSum += xCalcHADs1x16_SSE(&piOrg[x], &piCur[x], iStrideOrg, iStrideCur, iRows, 16);
+    }
+  }
+  else if (iCols == 1 && iRows % 16 == 0)
+  {
+    for( y = 0; y < iRows; y += 16 )
+    {
+      uiSum += xCalcHADs1x16_SSE( &piOrg[0], &piCur[0], iStrideOrg, iStrideCur, 16, iCols );
+      piOrg += (iStrideOrg << 4);
+      piCur += (iStrideCur << 4);
+    }
+  }
+  else if (iRows == 1 && iCols % 8 == 0)
+  {
+    for( x = 0; x < iCols; x += 8 )
+    {
+      uiSum += xCalcHADs1x8_SSE(&piOrg[x], &piCur[x], iStrideOrg, iStrideCur, iRows, 8);
+    }
+  }
+  else if (iCols == 1 && iRows % 8 == 0)
+  {
+    for( y = 0; y < iRows; y += 8 )
+    {
+      uiSum += xCalcHADs1x8_SSE(&piOrg[0], &piCur[0], iStrideOrg, iStrideCur, 8, iCols);
+      piOrg += (iStrideOrg << 3);
+      piCur += (iStrideCur << 3);
+    }
+  }
+#endif
 #if JVET_AI0185_ADAPTIVE_COST_IN_MERGE_MODE
   else if (iRows == 1 || iCols == 1)
   {
diff --git a/source/Lib/DecoderLib/CABACReader.cpp b/source/Lib/DecoderLib/CABACReader.cpp
index cf13edaa..7b8f39f6 100644
--- a/source/Lib/DecoderLib/CABACReader.cpp
+++ b/source/Lib/DecoderLib/CABACReader.cpp
@@ -2725,7 +2725,7 @@ void CABACReader::cu_obic_flag(CodingUnit& cu )
 }
 #endif
 
-#if JVET_AG0058_EIP
+#if JVET_AG0058_EIP && !TEST_2_19_c_SIGNALLING
 void CABACReader::cu_eip_flag(CodingUnit& cu)
 {
   if (cu.timd || cu.dimd || !cu.Y().valid() || !isLuma(cu.chType))
@@ -2743,25 +2743,81 @@ void CABACReader::cu_eip_flag(CodingUnit& cu)
       if (getAllowedEip(cu, COMPONENT_Y) && getAllowedEipMerge(cu, COMPONENT_Y))
       {
         cu.eipMerge = m_BinDecoder.decodeBin(Ctx::EipFlag(1));
+#if BV_EIP
+        if (!cu.eipMerge)        {
+          cu.bvEip = m_BinDecoder.decodeBin(Ctx::EipFlag(2));
+        }
+        else
+        {
+          cu.bvEip = false;
+        }
+#endif
       }
       else if (getAllowedEipMerge(cu, COMPONENT_Y))
       {
+#if BV_EIP
+        if (allowBvEip(cu, COMPONENT_Y))
+        {
+          cu.bvEip = m_BinDecoder.decodeBin(Ctx::EipFlag(3));
+        }
+        else
+        {
+          cu.bvEip = false;
+        }
+        cu.eipMerge = !cu.bvEip;
+#else
         cu.eipMerge = true;
+#endif
       }
       else
       {
         cu.eipMerge = false;
+#if BV_EIP
+        cu.bvEip = false;
+#endif
       }
       if(cu.eipMerge)
       {
         cu.firstPU->intraDir[0] = unary_max_eqprob(NUM_EIP_MERGE_SIGNAL - 1);
       }
+#if BV_EIP
+      else if (cu.bvEip)
+      {
+        unsigned int symbol = 0;
+        static_vector<EIP_FILTER_TYPE, NUM_DERIVED_BVEIP> eipInfoList;
+        xReadTruncBinCode(symbol, getAllowedBvEip(cu, COMPONENT_Y, eipInfoList));
+        cu.firstPU->intraDir[0] = symbol;
+      }
+#endif
       else
       {
         unsigned int symbol = 0;
         static_vector<EIPInfo, NUM_DERIVED_EIP> eipInfoList;
+#if MM_EIP
+#if BV_EIP
+        cu.eipMmFlag = m_BinDecoder.decodeBin(Ctx::EipFlag(4));
+#else
+        cu.eipMmFlag = m_BinDecoder.decodeBin(Ctx::EipFlag(2));
+#endif
+#if MM_EIP_REFACTOR
+        int eipNum = getAllowedCurEip(cu, COMPONENT_Y, eipInfoList, cu.eipMmFlag);
+#else
+        int sigEipNum, eipNum;
+        eipNum = getAllowedCurEip(cu, COMPONENT_Y, eipInfoList, sigEipNum, cu.eipMmFlag);
+#endif
+        if (eipNum > 1)
+        {
+          xReadTruncBinCode(symbol, eipNum);
+        }
+#if MM_EIP_REFACTOR
+        cu.firstPU->intraDir[0] = symbol;
+#else
+        cu.firstPU->intraDir[0] = cu.eipMmFlag ? symbol + sigEipNum: symbol;
+#endif
+#else
         xReadTruncBinCode(symbol, getAllowedCurEip(cu, COMPONENT_Y, eipInfoList));
         cu.firstPU->intraDir[0] = symbol;
+#endif
       }
     }
   }
@@ -2773,6 +2829,86 @@ void CABACReader::cu_eip_flag(CodingUnit& cu)
 }
 #endif
 
+#if TEST_2_19_c_SIGNALLING
+void CABACReader::cu_eip_flag(CodingUnit& cu)
+{
+  if (cu.timd || cu.dimd || !cu.Y().valid() || !isLuma(cu.chType))
+  {
+    cu.eipFlag = false;
+    return;
+  }
+
+  const bool bCanUseEip = getAllowedEip(cu, COMPONENT_Y) || getAllowedEipMerge(cu, COMPONENT_Y);
+  if (bCanUseEip)
+  {
+    cu.eipFlag = m_BinDecoder.decodeBin(Ctx::EipFlag(0));
+    if (cu.eipFlag)
+    {
+      if (getAllowedEip(cu, COMPONENT_Y) && getAllowedEipMerge(cu, COMPONENT_Y))
+      {
+        cu.eipMerge = m_BinDecoder.decodeBin(Ctx::EipFlag(1));
+      }
+      else if (getAllowedEipMerge(cu, COMPONENT_Y))
+      {
+        cu.eipMerge = true;
+      }
+      else
+      {
+        cu.eipMerge = false;
+      }
+
+      if (cu.eipMerge)
+      {
+        cu.firstPU->intraDir[0] = unary_max_eqprob(NUM_EIP_MERGE_SIGNAL - 1);
+      }
+      else
+      {
+        unsigned int symbol = 0;
+        static_vector<EIPInfo, NUM_DERIVED_EIP> eipInfoList;
+        cu.eipMmFlag = m_BinDecoder.decodeBin(Ctx::EipFlag(4));
+        if (!cu.eipMmFlag && allowBvEip(cu, COMPONENT_Y))
+        {
+          cu.bvEip = m_BinDecoder.decodeBin(Ctx::EipFlag(3));
+        }
+        else
+        {
+          cu.bvEip = false;
+        }
+
+        if (cu.bvEip)
+        {
+          static_vector<EIP_FILTER_TYPE, NUM_DERIVED_BVEIP> bvEipInfoList;
+          xReadTruncBinCode(symbol, getAllowedBvEip(cu, COMPONENT_Y, bvEipInfoList));
+          cu.firstPU->intraDir[0] = symbol;
+        }
+        else
+        {
+#if MM_EIP_REFACTOR
+          int eipNum = getAllowedCurEip(cu, COMPONENT_Y, eipInfoList, cu.eipMmFlag);
+#else
+          int sigEipNum, eipNum;
+          eipNum = getAllowedCurEip(cu, COMPONENT_Y, eipInfoList, sigEipNum, cu.eipMmFlag);
+#endif
+          if (eipNum > 1)
+          {
+            xReadTruncBinCode(symbol, eipNum);
+          }
+#if MM_EIP_REFACTOR
+          cu.firstPU->intraDir[0] = symbol;
+#else
+          cu.firstPU->intraDir[0] = cu.eipMmFlag ? symbol + sigEipNum : symbol;
+#endif
+        }
+      }
+    }
+  }
+  else
+  {
+    cu.eipFlag = false;
+  }
+}
+#endif
+
 #if JVET_W0123_TIMD_FUSION
 void CABACReader::cu_timd_flag( CodingUnit& cu )
 {
diff --git a/source/Lib/DecoderLib/DecCu.cpp b/source/Lib/DecoderLib/DecCu.cpp
index 606cc785..0302a4e4 100644
--- a/source/Lib/DecoderLib/DecCu.cpp
+++ b/source/Lib/DecoderLib/DecCu.cpp
@@ -331,7 +331,14 @@ void DecCu::decompressCtu( CodingStructure& cs, const UnitArea& ctuArea )
       case MODE_PLT:
       case MODE_INTRA:
 #if (JVET_AG0146_DIMD_ITMP_IBC || JVET_AG0152_SGPM_ITMP_IBC || JVET_AG0151_INTRA_TMP_MERGE_MODE)
-        if (currCU.dimd || currCU.sgpm || (chType == CHANNEL_TYPE_LUMA && currCU.tmpFlag))
+        if (currCU.dimd || currCU.sgpm || (chType == CHANNEL_TYPE_LUMA && currCU.tmpFlag)
+#if BV_EIP
+          || (currCU.eipFlag && currCU.bvEip)
+#endif
+#if BV_EIP_MRG
+          || (currCU.eipFlag && currCU.eipMerge)
+#endif
+          )
         {
           m_pcIntraPred->m_bvBasedMergeCandidates.clear();
 #if JVET_AH0200_INTRA_TMP_BV_REORDER
@@ -1221,15 +1228,73 @@ void DecCu::xIntraRecBlk( TransformUnit& tu, const ComponentID compID )
     {
       const bool isEncoder = pu.cs->pcv->isEncoder;
       m_pcIntraPred->initEipParams(pu, COMPONENT_Y);
+
       if (!isEncoder)
       {
         if (pu.cu->eipMerge)
         {
           static_vector<EipModelCandidate, MAX_MERGE_EIP> eipMergeCandList;
           m_pcIntraPred->getNeiEipCands(pu, eipMergeCandList);
+#if BV_EIP_MRG
+          RefTemplateType tempType = m_pcIntraPred->getRefTemplateType(*(tu.cu), tu.cu->blocks[COMPONENT_Y]);
+          m_pcIntraPred->initBvEipParams(pu, tempType);
+          static_vector<EipModelCandidate, NUM_DERIVED_BVEIP> bvEipMergeCands;
+          m_pcIntraPred->getBvEipMergeCands(pu, bvEipMergeCands);
+          static_vector<EipModelCandidate, MAX_MERGE_EIP + NUM_DERIVED_BVEIP> tempMergeCands;
+          tempMergeCands.insert(tempMergeCands.begin(), eipMergeCandList.begin(), eipMergeCandList.end());
+          tempMergeCands.insert(tempMergeCands.end(), bvEipMergeCands.begin(), bvEipMergeCands.end());
+          m_pcIntraPred->reorderEipCands(pu, tempMergeCands);
+          eipMergeCandList.clear();
+          eipMergeCandList.insert(eipMergeCandList.begin(), tempMergeCands.begin(), tempMergeCands.end());
+#else
           m_pcIntraPred->reorderEipCands(pu, eipMergeCandList);
+#endif
           pu.cu->eipModel = eipMergeCandList[pu.intraDir[CHANNEL_TYPE_LUMA]];
         }
+#if BV_EIP
+        else if (pu.cu->bvEip)
+        {
+          RefTemplateType tempType = m_pcIntraPred->getRefTemplateType(*(tu.cu), tu.cu->blocks[COMPONENT_Y]);
+          tu.cu->tmpIdx = MAX_BV_LIST - 1;
+          m_pcIntraPred->getTargetTemplate(tu.cu, pu.lwidth(), pu.lheight(), tempType);
+          m_pcIntraPred->candidateSearchIntra(tu.cu, pu.lwidth(), pu.lheight(), tempType
+#if JVET_AG0136_INTRA_TMP_LIC || (JVET_AG0146_DIMD_ITMP_IBC || JVET_AG0152_SGPM_ITMP_IBC || JVET_AG0151_INTRA_TMP_MERGE_MODE)
+              , false
+#endif
+          );
+
+#if BV_EIP_QUICK_SEARCH
+          if (m_pcIntraPred->getTmpQuickCand())
+#else
+          if (m_pcIntraPred->getTmpNumCand())
+#endif
+          {
+            static_vector<EipModelCandidate, NUM_DERIVED_BVEIP> bvEipCandList;
+            m_pcIntraPred->initBvEipParams(pu, tempType);
+            m_pcIntraPred->getBvEipCands(pu, bvEipCandList);
+            pu.cu->eipModel = bvEipCandList[0];
+
+#if BV_EIP
+            pu.interDir = 1;
+            pu.refIdx[REF_PIC_LIST_0] = MAX_NUM_REF;
+            pu.mv[0] = Mv(pu.cu->tmpXdisp << MV_FRACTIONAL_BITS_INTERNAL, pu.cu->tmpYdisp << MV_FRACTIONAL_BITS_INTERNAL);
+            pu.bv = Mv(pu.cu->tmpXdisp, pu.cu->tmpYdisp);
+#endif
+          }
+          else if (getAllowedEip(*pu.cu, COMPONENT_Y))
+          {
+            static_vector<EipModelCandidate, NUM_DERIVED_EIP> eipModelCandList;
+            m_pcIntraPred->getCurEipCands(pu, eipModelCandList);
+            pu.cu->eipModel = eipModelCandList[0];
+#if BV_EIP
+            pu.interDir = 1;
+            pu.refIdx[REF_PIC_LIST_0] = MAX_NUM_REF;
+            pu.mv[0] = Mv(0, 0);
+            pu.bv = Mv(0, 0);
+#endif  
+          }
+        }
+#endif
         else 
         {
           static_vector<EipModelCandidate, NUM_DERIVED_EIP> eipModelCandList;
@@ -1237,7 +1302,13 @@ void DecCu::xIntraRecBlk( TransformUnit& tu, const ComponentID compID )
           pu.cu->eipModel = eipModelCandList[0];
         }
       }
+      //if (pu.cu->eipModel.bMm)
+      //  printf("mmEip\n");
+      //else
+      //  printf("eip\n");
+
       m_pcIntraPred->eipPred(pu, piPred);
+
       if (!isEncoder)
       {
 #if JVET_AI0050_INTER_MTSS
@@ -1743,7 +1814,11 @@ void DecCu::xReconIntraQT( CodingUnit &cu )
   }
 #endif
 #if JVET_AB0061_ITMP_BV_FOR_IBC
+#if BV_EIP
+  if (cu.blocks[CHANNEL_TYPE_LUMA].valid() && (cu.tmpFlag || PU::hasEipBv(*cu.firstPU)))
+#else
   if (cu.blocks[CHANNEL_TYPE_LUMA].valid() && cu.tmpFlag)
+#endif
   {
     PU::spanMotionInfo(*cu.firstPU);
   }
diff --git a/source/Lib/EncoderLib/CABACWriter.cpp b/source/Lib/EncoderLib/CABACWriter.cpp
index 9052734e..1b43ecdb 100644
--- a/source/Lib/EncoderLib/CABACWriter.cpp
+++ b/source/Lib/EncoderLib/CABACWriter.cpp
@@ -2434,7 +2434,7 @@ void CABACWriter::intra_luma_pred_mode( const PredictionUnit& pu )
 #endif
 }
 
-#if JVET_AG0058_EIP
+#if JVET_AG0058_EIP && !TEST_2_19_c_SIGNALLING
 void CABACWriter::cu_eip_flag(const CodingUnit& cu)
 {
   if (cu.timd || cu.dimd || !cu.Y().valid() || !isLuma(cu.chType))
@@ -2451,17 +2451,58 @@ void CABACWriter::cu_eip_flag(const CodingUnit& cu)
       if (getAllowedEip(cu, COMPONENT_Y) && getAllowedEipMerge(cu, COMPONENT_Y))
       {
         m_BinEncoder.encodeBin(cu.eipMerge, Ctx::EipFlag(1));
+#if BV_EIP
+        if (!cu.eipMerge && allowBvEip(cu, COMPONENT_Y))
+        {
+          m_BinEncoder.encodeBin(cu.bvEip, Ctx::EipFlag(2));
+        }
+#endif
       }
+#if BV_EIP
+      else if (getAllowedEipMerge(cu, COMPONENT_Y) && allowBvEip(cu, COMPONENT_Y))
+      {
+        m_BinEncoder.encodeBin(cu.bvEip, Ctx::EipFlag(3));
+      }
+#endif
+
+
       if(cu.eipMerge)
       {
         CHECK(cu.firstPU->intraDir[0] >= NUM_EIP_MERGE_SIGNAL, "cu.firstPU->intraDir[0] >= NUM_EIP_MERGE_SIGNAL");
         unary_max_eqprob(cu.firstPU->intraDir[0], NUM_EIP_MERGE_SIGNAL - 1);
       }
+#if BV_EIP
+      else if (cu.bvEip)
+      {
+        static_vector<EIP_FILTER_TYPE, NUM_DERIVED_BVEIP> eipInfoList;
+        xWriteTruncBinCode(cu.firstPU->intraDir[0], getAllowedBvEip(cu, COMPONENT_Y, eipInfoList));
+      }
+#endif
       else
       {
         CHECK(cu.firstPU->intraDir[0] >= NUM_DERIVED_EIP, "cu.firstPU->intraDir[0] >= NUM_DERIVED_EIP");
         static_vector<EIPInfo, NUM_DERIVED_EIP> eipInfoList;
+#if MM_EIP
+#if BV_EIP
+        m_BinEncoder.encodeBin(cu.eipMmFlag, Ctx::EipFlag(4));
+#else 
+        m_BinEncoder.encodeBin(cu.eipMmFlag, Ctx::EipFlag(2));
+#endif
+#if MM_EIP_REFACTOR
+        int eipNum = getAllowedCurEip(cu, COMPONENT_Y, eipInfoList, cu.eipMmFlag);
+        int eipIdx = cu.firstPU->intraDir[0];
+#else
+        int sigEipNum, eipNum, eipIdx;
+        eipNum = getAllowedCurEip(cu, COMPONENT_Y, eipInfoList, sigEipNum, cu.eipMmFlag);
+        eipIdx = cu.eipMmFlag ? cu.firstPU->intraDir[0] - sigEipNum: cu.firstPU->intraDir[0];
+#endif
+        if (eipNum > 1)
+        {
+          xWriteTruncBinCode(eipIdx, eipNum);
+        }
+#else
         xWriteTruncBinCode(cu.firstPU->intraDir[0], getAllowedCurEip(cu, COMPONENT_Y, eipInfoList));
+#endif
       }
     }
   }
@@ -2469,6 +2510,68 @@ void CABACWriter::cu_eip_flag(const CodingUnit& cu)
 }
 #endif
 
+#if TEST_2_19_c_SIGNALLING
+void CABACWriter::cu_eip_flag(const CodingUnit& cu)
+{
+  if (cu.timd || cu.dimd || !cu.Y().valid() || !isLuma(cu.chType))
+  {
+    return;
+  }
+
+  const bool bCanUseEip = getAllowedEip(cu, COMPONENT_Y) || getAllowedEipMerge(cu, COMPONENT_Y);
+  if (bCanUseEip)
+  {
+    m_BinEncoder.encodeBin(cu.eipFlag, Ctx::EipFlag(0));
+    if (cu.eipFlag)
+    {
+      if (getAllowedEip(cu, COMPONENT_Y) && getAllowedEipMerge(cu, COMPONENT_Y))
+      {
+        m_BinEncoder.encodeBin(cu.eipMerge, Ctx::EipFlag(1));
+      }
+
+      if (cu.eipMerge)
+      {
+        CHECK(cu.firstPU->intraDir[0] >= NUM_EIP_MERGE_SIGNAL, "cu.firstPU->intraDir[0] >= NUM_EIP_MERGE_SIGNAL");
+        unary_max_eqprob(cu.firstPU->intraDir[0], NUM_EIP_MERGE_SIGNAL - 1);
+      }
+      else
+      {
+        m_BinEncoder.encodeBin(cu.eipMmFlag, Ctx::EipFlag(4));
+
+        if (!cu.eipMmFlag && allowBvEip(cu, COMPONENT_Y))
+        {
+          m_BinEncoder.encodeBin(cu.bvEip, Ctx::EipFlag(3));
+        }
+
+        CHECK(cu.firstPU->intraDir[0] >= NUM_DERIVED_EIP, "cu.firstPU->intraDir[0] >= NUM_DERIVED_EIP");
+        static_vector<EIPInfo, NUM_DERIVED_EIP> eipInfoList;
+
+        if (cu.bvEip)
+        {
+          static_vector<EIP_FILTER_TYPE, NUM_DERIVED_BVEIP> bvEipInfoList;
+          xWriteTruncBinCode(cu.firstPU->intraDir[0], getAllowedBvEip(cu, COMPONENT_Y, bvEipInfoList));
+        }
+        else
+        {
+#if MM_EIP_REFACTOR
+          int eipNum = getAllowedCurEip(cu, COMPONENT_Y, eipInfoList, cu.eipMmFlag);
+          int eipIdx = cu.firstPU->intraDir[0];
+#else
+          int sigEipNum, eipNum, eipIdx;
+          eipNum = getAllowedCurEip(cu, COMPONENT_Y, eipInfoList, sigEipNum, cu.eipMmFlag);
+          eipIdx = cu.eipMmFlag ? cu.firstPU->intraDir[0] - sigEipNum : cu.firstPU->intraDir[0];
+#endif
+          if (eipNum > 1)
+          {
+            xWriteTruncBinCode(eipIdx, eipNum);
+          }
+        }
+      }
+    }
+  }
+}
+#endif
+
 #if JVET_W0123_TIMD_FUSION
 void CABACWriter::cu_timd_flag( const CodingUnit& cu )
 {
diff --git a/source/Lib/EncoderLib/EncCu.cpp b/source/Lib/EncoderLib/EncCu.cpp
index a34dd5a8..7eca903a 100644
--- a/source/Lib/EncoderLib/EncCu.cpp
+++ b/source/Lib/EncoderLib/EncCu.cpp
@@ -1142,6 +1142,9 @@ void EncCu::xCompressCU( CodingStructure*& tempCS, CodingStructure*& bestCS, Par
 #if JVET_AD0213_LIC_IMP
   m_pcInterSearch->resetFillLicTpl();
 #endif
+#if BV_EIP_QUICK_SEARCH
+  m_pcIntraSearch->m_tmpQuickCand = false;
+#endif
 
 #if JVET_Z0118_GDR
   if (m_pcEncCfg->getGdrEnabled())
@@ -3310,6 +3313,9 @@ bool EncCu::xCheckRDCostIntra(CodingStructure *&tempCS, CodingStructure *&bestCS
 #else
   int        endLfnstIdx         = sps.getUseLFNST() ? maxLfnstIdx : 0;
 #endif
+#if BV_EIP_FAST_ENC3
+  bool skipTmpFlm = false;
+#endif
 #if INTRA_TRANS_ENC_OPT
   if (m_pcEncCfg->getIntraPeriod() == 1)
   {
@@ -3318,6 +3324,12 @@ bool EncCu::xCheckRDCostIntra(CodingStructure *&tempCS, CodingStructure *&bestCS
     {
       endLfnstIdx = startLfnstIdx;
     }
+#if BV_EIP_FAST_ENC3
+    if (isLuma(partitioner.chType) && relatedCU.skipTmpFlm)
+    {
+      skipTmpFlm = true;
+    }
+#endif
   }
 #endif
 #if JVET_W0103_INTRA_MTS
@@ -3539,10 +3551,16 @@ bool EncCu::xCheckRDCostIntra(CodingStructure *&tempCS, CodingStructure *&bestCS
   m_pcIntraSearch->m_skipTimdLfnstMtsPass = false;
   m_modeCtrl->resetLfnstCost();
 #endif
+#if BV_EIP_FAST_SKIP_FLM_LFNST
+  m_pcIntraSearch->m_skipTmpFlmLfnstMtsPass = false;
+#endif
 #if JVET_AH0076_OBIC
   m_pcIntraSearch->m_skipObicLfnstMtsPass = false;
   m_pcIntraSearch->m_skipDimdLfnstMtsPass = false;
 #endif
+#if EIP_LFNST_FAST
+  m_pcIntraSearch->m_skipEipLfnstMtsPass = false;
+#endif
 #if JVET_AC0147_CCCM_NO_SUBSAMPLING
   m_pcIntraSearch->m_skipCCCMSATD = false;
 #endif
@@ -3889,6 +3907,12 @@ bool EncCu::xCheckRDCostIntra(CodingStructure *&tempCS, CodingStructure *&bestCS
 #if JVET_AD0086_ENHANCED_INTRA_TMP
                 for (int idx = 0; idx < m_pcIntraSearch->getTmpNumCand() && idx < MTMP_NUM; idx++)
                 {
+#if BV_EIP_FAST_ENC3
+                  if (skipTmpFlm)
+                  {
+                    break;
+                  }
+#endif
                   cu.tmpIdx = idx;
                   m_pcIntraSearch->xCalTmpFlmParam(&cu, cu.lwidth(), cu.lheight(), templateType);
                 }
@@ -3957,7 +3981,11 @@ bool EncCu::xCheckRDCostIntra(CodingStructure *&tempCS, CodingStructure *&bestCS
             PU::spanIpmInfoIntra(*cu.firstPU);
 #endif
 #if JVET_AB0061_ITMP_BV_FOR_IBC
+#if BV_EIP
+            if (cu.tmpFlag || PU::hasEipBv(*cu.firstPU))
+#else
             if (cu.tmpFlag)
+#endif
             {
               PU::spanMotionInfo(*cu.firstPU);
             }
diff --git a/source/Lib/EncoderLib/EncModeCtrl.h b/source/Lib/EncoderLib/EncModeCtrl.h
index f86fbb8b..3e25a93e 100644
--- a/source/Lib/EncoderLib/EncModeCtrl.h
+++ b/source/Lib/EncoderLib/EncModeCtrl.h
@@ -1364,6 +1364,9 @@ struct CodedCUInfo
 #if JVET_AH0200_INTRA_TMP_BV_REORDER
   bool     skipFracTmp;
 #endif
+#if EIP_CU_CHK_SAD
+  bool     skipEip;
+#endif
 
   bool     validMv[NUM_REF_PIC_LIST_01][MAX_STORED_CU_INFO_REFS];
   Mv       saveMv [NUM_REF_PIC_LIST_01][MAX_STORED_CU_INFO_REFS];
@@ -1390,6 +1393,9 @@ struct CodedCUInfo
 #if JVET_AB0092_GLM_WITH_LUMA
   bool     skipGLM;
 #endif
+#if BV_EIP_FAST_ENC3
+  bool     skipTmpFlm;
+#endif
 };
 
 class CacheBlkInfoCtrl
diff --git a/source/Lib/EncoderLib/IntraSearch.cpp b/source/Lib/EncoderLib/IntraSearch.cpp
index a1b68d55..7db15aff 100644
--- a/source/Lib/EncoderLib/IntraSearch.cpp
+++ b/source/Lib/EncoderLib/IntraSearch.cpp
@@ -103,6 +103,12 @@ IntraSearch::IntraSearch()
   {
     m_eipMergePredBuf[i] = nullptr;
   }
+#if BV_EIP
+  for (int i = 0; i < NUM_DERIVED_BVEIP; i++)
+  {
+    m_bvEipPredBuf[i] = nullptr;
+  }
+#endif
 #endif
 #if JVET_AH0076_OBIC
   m_dimdPredBuf = nullptr;
@@ -296,6 +302,13 @@ void IntraSearch::destroy()
     delete[] m_eipMergePredBuf[i];
     m_eipMergePredBuf[i] = nullptr;
   }
+#if BV_EIP
+  for (int i = 0; i < NUM_DERIVED_BVEIP; i++)
+  {
+    delete[] m_bvEipPredBuf[i];
+    m_bvEipPredBuf[i] = nullptr;
+  }
+#endif
 #endif
 #if JVET_AH0076_OBIC
   delete[] m_dimdPredBuf;
@@ -466,6 +479,12 @@ void IntraSearch::init( EncCfg*        pcEncCfg,
   {
     m_eipMergePredBuf[i] = new Pel[MAX_EIP_SIZE * MAX_EIP_SIZE];
   }
+#if BV_EIP
+  for (int i = 0; i < NUM_DERIVED_BVEIP; i++)
+  {
+    m_bvEipPredBuf[i] = new Pel[MAX_EIP_SIZE * MAX_EIP_SIZE];
+  }
+#endif
 #endif
 #if JVET_AH0076_OBIC
   m_dimdPredBuf = new Pel[(MAX_CU_SIZE>>1) * (MAX_CU_SIZE>>1)];
@@ -604,6 +623,12 @@ void IntraSearch::init( EncCfg*        pcEncCfg,
   m_skipObicLfnstMtsPass = false;
   m_skipDimdLfnstMtsPass = false;
 #endif
+#if EIP_LFNST_FAST
+  m_skipEipLfnstMtsPass = false;
+#endif
+#if BV_EIP_FAST_SKIP_FLM_LFNST
+  m_skipTmpFlmLfnstMtsPass = false;
+#endif
 }
 
 
@@ -846,6 +871,9 @@ bool IntraSearch::estIntraPredLumaQT(CodingUnit &cu, Partitioner &partitioner, c
 #endif
 #if JVET_AD0208_IBC_ADAPT_FOR_CAM_CAPTURED_CONTENTS
     m_bestIntraSADCost = MAX_DOUBLE;
+#endif
+#if BV_EIP_FAST_ENC
+    m_bestIntraHADCost = MAX_DOUBLE;
 #endif
   }
 #endif
@@ -869,6 +897,27 @@ bool IntraSearch::estIntraPredLumaQT(CodingUnit &cu, Partitioner &partitioner, c
 #if JVET_AG0058_EIP
   bool testEip = isLuma(partitioner.chType) && sps.getUseEip() && (getAllowedEip(cu, COMPONENT_Y) || getAllowedEipMerge(cu, COMPONENT_Y));
   bool eipSaveFlag = (cu.lfnstIdx == 0 && cu.mtsFlag == 0);
+#if EIP_LFNST_FAST
+  double eipCost = MAX_DOUBLE;
+  bool setSkipEipControl = (m_pcEncCfg->getIntraPeriod() == 1) && !cu.lfnstIdx && !cu.mtsFlag;
+#endif
+#if BV_EIP_FAST_SKIP_FLM_LFNST
+  double tmpFlmCost = MAX_DOUBLE;
+  bool setSkipFlmControl = (m_pcEncCfg->getIntraPeriod() == 1) && !cu.lfnstIdx && !cu.mtsFlag;
+#endif
+#if EIP_CU_CHK_SAD
+#if JVET_AI0136_ADAPTIVE_DUAL_TREE
+  CodedCUInfo *relatedCU = ((EncModeCtrlMTnoRQT *) m_modeCtrl)->getBlkInfoPtr(partitioner.currArea());
+  testEip &= !relatedCU->skipEip;
+#else
+  CodedCUInfo &relatedCU = ((EncModeCtrlMTnoRQT *) m_modeCtrl)->getBlkInfo(partitioner.currArea());
+  testEip &= !relatedCU.skipEip;
+#endif
+#endif
+#if EIP_CU_CHK_SAD || BV_EIP_FAST_ENC3
+  double eipBestSatdCost = MAX_DOUBLE;
+  bool isEipModeTestd = false;
+#endif
 #endif
 
 #if JVET_W0103_INTRA_MTS 
@@ -1281,7 +1330,12 @@ bool IntraSearch::estIntraPredLumaQT(CodingUnit &cu, Partitioner &partitioner, c
               // SAD is scaled by 2 to align with the scaling of HAD
 #if JVET_AD0208_IBC_ADAPT_FOR_CAM_CAPTURED_CONTENTS
               Distortion sadCost = distParamSad.distFunc(distParamSad);
+#if BV_EIP_FAST_ENC
+              Distortion hadCost = distParamHad.distFunc(distParamHad);
+              minSadHad += std::min(sadCost * 2, hadCost);
+#else
               minSadHad += std::min(sadCost * 2, distParamHad.distFunc(distParamHad));
+#endif
 #else
               minSadHad += std::min(distParamSad.distFunc(distParamSad) * 2, distParamHad.distFunc(distParamHad));
 #endif
@@ -1291,6 +1345,9 @@ bool IntraSearch::estIntraPredLumaQT(CodingUnit &cu, Partitioner &partitioner, c
               double cost = (double) minSadHad + (double) fracModeBits * sqrtLambdaForFirstPass;
 #if JVET_AD0208_IBC_ADAPT_FOR_CAM_CAPTURED_CONTENTS
               m_bestIntraSADCost = std::min(m_bestIntraSADCost, cost - (double)minSadHad + (double)sadCost);
+#endif
+#if BV_EIP_FAST_ENC
+              m_bestIntraHADCost = std::min(m_bestIntraHADCost, cost - (double)minSadHad + (double)hadCost);
 #endif
               DTRACE(g_trace_ctx, D_INTRA_COST, "IntraHAD: %u, %llu, %f (%d)\n", minSadHad, fracModeBits, cost, uiMode);
 
@@ -1325,7 +1382,12 @@ bool IntraSearch::estIntraPredLumaQT(CodingUnit &cu, Partitioner &partitioner, c
                 // SAD is scaled by 2 to align with the scaling of HAD
 #if JVET_AD0208_IBC_ADAPT_FOR_CAM_CAPTURED_CONTENTS
                 Distortion sadCost = distParamSad.distFunc(distParamSad);
+#if BV_EIP_FAST_ENC
+                Distortion hadCost = distParamHad.distFunc(distParamHad);
+                Distortion minSadHad = std::min(sadCost * 2, hadCost);
+#else
                 Distortion minSadHad = std::min(sadCost * 2, distParamHad.distFunc(distParamHad));
+#endif
 #else
                 Distortion minSadHad =
                   std::min(distParamSad.distFunc(distParamSad) * 2, distParamHad.distFunc(distParamHad));
@@ -1335,6 +1397,9 @@ bool IntraSearch::estIntraPredLumaQT(CodingUnit &cu, Partitioner &partitioner, c
                 double cost = (double) minSadHad + (double) fracModeBits * sqrtLambdaForFirstPass;
 #if JVET_AD0208_IBC_ADAPT_FOR_CAM_CAPTURED_CONTENTS
                 m_bestIntraSADCost = std::min(m_bestIntraSADCost, cost - (double)minSadHad + (double)sadCost);
+#endif
+#if BV_EIP_FAST_ENC
+                m_bestIntraHADCost = std::min(m_bestIntraHADCost, cost - (double)minSadHad + (double)hadCost);
 #endif
                 updateCandList(
                   ModeInfo(false, false, 0, NOT_INTRA_SUBPARTITIONS, dirPlanarModeIdx ? PL_VER_IDX : PL_HOR_IDX), cost,
@@ -1415,7 +1480,12 @@ bool IntraSearch::estIntraPredLumaQT(CodingUnit &cu, Partitioner &partitioner, c
                     // SAD is scaled by 2 to align with the scaling of HAD
 #if JVET_AD0208_IBC_ADAPT_FOR_CAM_CAPTURED_CONTENTS
                     Distortion sadCost = distParamSad.distFunc(distParamSad);
+#if BV_EIP_FAST_ENC
+                    Distortion hadCost = distParamHad.distFunc(distParamHad);
+                    Distortion minSadHad = std::min(sadCost * 2, hadCost);
+#else
                     Distortion minSadHad = std::min(sadCost * 2, distParamHad.distFunc(distParamHad));
+#endif
 #else
                     Distortion minSadHad =
                       std::min(distParamSad.distFunc(distParamSad) * 2, distParamHad.distFunc(distParamHad));
@@ -1426,6 +1496,9 @@ bool IntraSearch::estIntraPredLumaQT(CodingUnit &cu, Partitioner &partitioner, c
                     double cost = (double) minSadHad + (double) fracModeBits * sqrtLambdaForFirstPass;
 #if JVET_AD0208_IBC_ADAPT_FOR_CAM_CAPTURED_CONTENTS
                     m_bestIntraSADCost = std::min(m_bestIntraSADCost, cost - (double)minSadHad + (double)sadCost);
+#endif
+#if BV_EIP_FAST_ENC
+                    m_bestIntraHADCost = std::min(m_bestIntraHADCost, cost - (double)minSadHad + (double)hadCost);
 #endif
                     updateCandList(ModeInfo(false, false, 0, NOT_INTRA_SUBPARTITIONS, mode), cost, uiRdModeList,
                                    candCostList, numModesForFullRD);
@@ -1487,7 +1560,12 @@ bool IntraSearch::estIntraPredLumaQT(CodingUnit &cu, Partitioner &partitioner, c
                     // SAD is scaled by 2 to align with the scaling of HAD
 #if JVET_AD0208_IBC_ADAPT_FOR_CAM_CAPTURED_CONTENTS
                   Distortion sadCost = distParamSad.distFunc(distParamSad);
+#if BV_EIP_FAST_ENC
+                  Distortion hadCost = distParamHad.distFunc(distParamHad);
+                  Distortion minSadHad = std::min(sadCost * 2, hadCost);
+#else
                   Distortion minSadHad = std::min(sadCost * 2, distParamHad.distFunc(distParamHad));
+#endif
 #else
                   Distortion minSadHad =
                     std::min(distParamSad.distFunc(distParamSad) * 2, distParamHad.distFunc(distParamHad));
@@ -1498,6 +1576,9 @@ bool IntraSearch::estIntraPredLumaQT(CodingUnit &cu, Partitioner &partitioner, c
                   double cost = (double)minSadHad + (double)fracModeBits * sqrtLambdaForFirstPass;
 #if JVET_AD0208_IBC_ADAPT_FOR_CAM_CAPTURED_CONTENTS
                   m_bestIntraSADCost = std::min(m_bestIntraSADCost, cost - (double)minSadHad + (double)sadCost);
+#endif
+#if BV_EIP_FAST_ENC
+                  m_bestIntraHADCost = std::min(m_bestIntraHADCost, cost - (double)minSadHad + (double)hadCost);
 #endif
                   updateCandList(ModeInfo(false, false, uiMode, NOT_INTRA_SUBPARTITIONS, 0), cost, uiRdModeList,
                     candCostList, numModesForFullRD);
@@ -1549,7 +1630,12 @@ bool IntraSearch::estIntraPredLumaQT(CodingUnit &cu, Partitioner &partitioner, c
                   // SAD is scaled by 2 to align with the scaling of HAD
 #if JVET_AD0208_IBC_ADAPT_FOR_CAM_CAPTURED_CONTENTS
                   Distortion sadCost = distParamSad.distFunc(distParamSad);
+#if BV_EIP_FAST_ENC
+                  Distortion hadCost = distParamHad.distFunc(distParamHad);
+                  Distortion minSadHad = std::min(sadCost * 2, hadCost);
+#else
                   Distortion minSadHad = std::min(sadCost * 2, distParamHad.distFunc(distParamHad));
+#endif
 #else
                   Distortion minSadHad =
                     std::min(distParamSad.distFunc(distParamSad) * 2, distParamHad.distFunc(distParamHad));
@@ -1561,6 +1647,9 @@ bool IntraSearch::estIntraPredLumaQT(CodingUnit &cu, Partitioner &partitioner, c
                   double cost = (double) minSadHad + (double) fracModeBits * sqrtLambdaForFirstPass;
 #if JVET_AD0208_IBC_ADAPT_FOR_CAM_CAPTURED_CONTENTS
                   m_bestIntraSADCost = std::min(m_bestIntraSADCost, cost - (double)minSadHad + (double)sadCost);
+#endif
+#if BV_EIP_FAST_ENC
+                  m_bestIntraHADCost = std::min(m_bestIntraHADCost, cost - (double)minSadHad + (double)hadCost);
 #endif
                   updateCandList(ModeInfo(false, false, multiRefIdx, NOT_INTRA_SUBPARTITIONS, mode), cost, uiRdModeList,
                                  candCostList, numModesForFullRD);
@@ -1602,6 +1691,10 @@ bool IntraSearch::estIntraPredLumaQT(CodingUnit &cu, Partitioner &partitioner, c
               static_vector<double, FAST_UDI_MAX_RDMODE_NUM> candCostListFracTmp;
               Distortion backupMinSadHad[MTMP_NUM];
               Distortion backupSadCost[MTMP_NUM];
+#if BV_EIP_FAST_ENC
+              Distortion backupHadCost[MTMP_NUM];
+              Distortion backupLicHadCost[MTMP_NUM][4];
+#endif
               Distortion backupLicMinSadHad[MTMP_NUM][4];
               Distortion backupLicSadCost[MTMP_NUM][4];
               static_vector<ModeInfo, FAST_UDI_MAX_RDMODE_NUM> uiRdModeListLicFracTmp;
@@ -1680,6 +1773,12 @@ bool IntraSearch::estIntraPredLumaQT(CodingUnit &cu, Partitioner &partitioner, c
                     {
                       continue;
                     }
+#if BV_EIP_FAST_ENC3
+                    if (tmpFlmFlag && relatedCU->skipTmpFlm)
+                    {
+                      continue;
+                    }
+#endif
 
 #if JVET_AG0136_INTRA_TMP_LIC
                     for (int tmpLicFlag = 0; tmpLicFlag <= 1; tmpLicFlag++)
@@ -1776,7 +1875,12 @@ bool IntraSearch::estIntraPredLumaQT(CodingUnit &cu, Partitioner &partitioner, c
 #endif
 #if JVET_AD0208_IBC_ADAPT_FOR_CAM_CAPTURED_CONTENTS
                       Distortion sadCost = distParamSad.distFunc(distParamSad);
+#if BV_EIP_FAST_ENC
+                      Distortion hadCost = distParamHad.distFunc(distParamHad);
+                      Distortion minSadHad = std::min(sadCost * 2, hadCost);
+#else
                       Distortion minSadHad = std::min(sadCost * 2, distParamHad.distFunc(distParamHad));
+#endif
 #else
                       Distortion minSadHad =
                         std::min(distParamSad.distFunc(distParamSad) * 2, distParamHad.distFunc(distParamHad));
@@ -1807,6 +1911,9 @@ bool IntraSearch::estIntraPredLumaQT(CodingUnit &cu, Partitioner &partitioner, c
                       {
 #endif
                         m_bestIntraSADCost = std::min(m_bestIntraSADCost, cost - double(minSadHad) + (double)sadCost);
+#if BV_EIP_FAST_ENC
+                        m_bestIntraHADCost = std::min(m_bestIntraHADCost, cost - (double)minSadHad + (double)hadCost);
+#endif
 #if JVET_AH0200_INTRA_TMP_BV_REORDER
                       }
 #endif
@@ -1824,12 +1931,18 @@ bool IntraSearch::estIntraPredLumaQT(CodingUnit &cu, Partitioner &partitioner, c
                       {
                         backupLicMinSadHad[cu.tmpIdx][cu.ibcLicIdx] = minSadHad;
                         backupLicSadCost[cu.tmpIdx][cu.ibcLicIdx] = sadCost;
+#if BV_EIP_FAST_ENC
+                        backupLicHadCost[cu.tmpIdx][cu.ibcLicIdx] = hadCost;
+#endif
                         updateCandList(ModeInfo(0, 0, 0, NOT_INTRA_SUBPARTITIONS, 0, 1, cu.tmpIdx, cu.tmpFusionFlag, cu.tmpFlmFlag, cu.tmpLicFlag, cu.ibcLicIdx, cu.tmpIsSubPel, cu.tmpSubPelIdx, cu.tmpFracIdx), cost, uiRdModeListLicFracTmp, candCostListLicFracTmp, numModesForLicFracIntraTmp);
                       }
                       else
                       {
                         backupMinSadHad[cu.tmpIdx] = minSadHad;
                         backupSadCost[cu.tmpIdx] = sadCost;
+#if BV_EIP_FAST_ENC
+                        backupHadCost[cu.tmpIdx] = hadCost;
+#endif
                         updateCandList(ModeInfo(0, 0, 0, NOT_INTRA_SUBPARTITIONS, 0, 1, cu.tmpIdx, cu.tmpFusionFlag, cu.tmpFlmFlag, cu.tmpLicFlag, cu.ibcLicIdx, cu.tmpIsSubPel, cu.tmpSubPelIdx, cu.tmpFracIdx), cost, uiRdModeListFracTmp, candCostListFracTmp, numModesForFracIntraTmp);
                       }
 #else
@@ -1998,6 +2111,9 @@ bool IntraSearch::estIntraPredLumaQT(CodingUnit &cu, Partitioner &partitioner, c
 
                     Distortion sadCost;
                     Distortion minSadHad;
+#if BV_EIP_FAST_ENC
+                    Distortion hadCost;
+#endif
                     uint64_t fracModeBits = 0;
                     double cost;
                     if(cu.tmpIsSubPel)
@@ -2006,7 +2122,12 @@ bool IntraSearch::estIntraPredLumaQT(CodingUnit &cu, Partitioner &partitioner, c
                       generateTMPrediction(piPred.buf, piPred.stride, placeHolder, pu, cu.tmpLicFlag, false);
   #if JVET_AD0208_IBC_ADAPT_FOR_CAM_CAPTURED_CONTENTS&&JVET_AE0169_BIPREDICTIVE_IBC
                       sadCost = distParamSad.distFunc(distParamSad);
+#if BV_EIP_FAST_ENC
+                      hadCost = distParamHad.distFunc(distParamHad);
+                      minSadHad = std::min(sadCost * 2, hadCost);
+#else
                       minSadHad = std::min(sadCost * 2, distParamHad.distFunc(distParamHad));
+#endif
   #else
                       minSadHad =
                           std::min(distParamSad.distFunc(distParamSad) * 2, distParamHad.distFunc(distParamHad));
@@ -2015,6 +2136,9 @@ bool IntraSearch::estIntraPredLumaQT(CodingUnit &cu, Partitioner &partitioner, c
                     else
                     {
                       sadCost = backupSadCost[cu.tmpIdx];
+#if BV_EIP_FAST_ENC
+                      hadCost = backupHadCost[cu.tmpIdx];
+#endif
                       minSadHad = backupMinSadHad[cu.tmpIdx];
                     }
                     if(!cu.tmpIsSubPel && !cu.tmpFracIdx)
@@ -2037,6 +2161,9 @@ bool IntraSearch::estIntraPredLumaQT(CodingUnit &cu, Partitioner &partitioner, c
   #if JVET_AD0208_IBC_ADAPT_FOR_CAM_CAPTURED_CONTENTS&&JVET_AE0169_BIPREDICTIVE_IBC
                     m_bestIntraSADCost = std::min(m_bestIntraSADCost, cost - double(minSadHad) + (double)sadCost);
   #endif
+#if BV_EIP_FAST_ENC
+                    m_bestIntraHADCost = std::min(m_bestIntraHADCost, cost - (double)minSadHad + (double)hadCost);
+#endif
                     DTRACE(g_trace_ctx, D_INTRA_COST, "IntraTPM: %u, %llu, %f (%d)\n", minSadHad, fracModeBits, cost, 0);
   #if JVET_AG0136_INTRA_TMP_LIC
                     updateCandList(ModeInfo(0, 0, 0, NOT_INTRA_SUBPARTITIONS, 0, 1, cu.tmpIdx, cu.tmpFusionFlag, cu.tmpFlmFlag, cu.tmpLicFlag, cu.ibcLicIdx, cu.tmpIsSubPel, cu.tmpSubPelIdx, cu.tmpFracIdx), cost, uiRdModeList, candCostList, numModesForFullRD);
@@ -2068,6 +2195,9 @@ bool IntraSearch::estIntraPredLumaQT(CodingUnit &cu, Partitioner &partitioner, c
                     cu.tmpFracIdx = spIdx;
 
                     Distortion sadCost;
+#if BV_EIP_FAST_ENC
+                    Distortion hadCost;
+#endif
                     Distortion minSadHad;
                     uint64_t fracModeBits = 0;
                     double cost;
@@ -2093,7 +2223,12 @@ bool IntraSearch::estIntraPredLumaQT(CodingUnit &cu, Partitioner &partitioner, c
 
 #if JVET_AD0208_IBC_ADAPT_FOR_CAM_CAPTURED_CONTENTS&&JVET_AE0169_BIPREDICTIVE_IBC
                       sadCost = distParamSad.distFunc(distParamSad);
+#if BV_EIP_FAST_ENC
+                      hadCost = distParamHad.distFunc(distParamHad);
+                      minSadHad = std::min(sadCost * 2, hadCost);
+#else
                       minSadHad = std::min(sadCost * 2, distParamHad.distFunc(distParamHad));
+#endif
 #else
                       minSadHad =
                           std::min(distParamSad.distFunc(distParamSad) * 2, distParamHad.distFunc(distParamHad));
@@ -2102,6 +2237,9 @@ bool IntraSearch::estIntraPredLumaQT(CodingUnit &cu, Partitioner &partitioner, c
                     else
                     {
                       sadCost = backupLicSadCost[cu.tmpIdx][cu.ibcLicIdx];
+#if BV_EIP_FAST_ENC
+                      hadCost = backupLicHadCost[cu.tmpIdx][cu.ibcLicIdx];
+#endif
                       minSadHad = backupLicMinSadHad[cu.tmpIdx][cu.ibcLicIdx];                  
                     }
                     if(!cu.tmpIsSubPel && !cu.tmpFracIdx)
@@ -2121,6 +2259,9 @@ bool IntraSearch::estIntraPredLumaQT(CodingUnit &cu, Partitioner &partitioner, c
                     }
 #if JVET_AD0208_IBC_ADAPT_FOR_CAM_CAPTURED_CONTENTS&&JVET_AE0169_BIPREDICTIVE_IBC
                     m_bestIntraSADCost = std::min(m_bestIntraSADCost, cost - double(minSadHad) + (double)sadCost);
+#endif
+#if BV_EIP_FAST_ENC
+                    m_bestIntraHADCost = std::min(m_bestIntraHADCost, cost - (double)minSadHad + (double)hadCost);
 #endif
                     DTRACE(g_trace_ctx, D_INTRA_COST, "IntraTPM: %u, %llu, %f (%d)\n", minSadHad, fracModeBits, cost, 0);
                     updateCandList(ModeInfo(0, 0, 0, NOT_INTRA_SUBPARTITIONS, 0, 1, cu.tmpIdx, cu.tmpFusionFlag, cu.tmpFlmFlag, cu.tmpLicFlag, cu.ibcLicIdx, cu.tmpIsSubPel, cu.tmpSubPelIdx, cu.tmpFracIdx), cost, uiRdModeList, candCostList, numModesForFullRD);
@@ -2280,7 +2421,12 @@ bool IntraSearch::estIntraPredLumaQT(CodingUnit &cu, Partitioner &partitioner, c
                 // SAD is scaled by 2 to align with the scaling of HAD
 #if JVET_AD0208_IBC_ADAPT_FOR_CAM_CAPTURED_CONTENTS
                 Distortion sadCost = distParamSad.distFunc(distParamSad);
+#if BV_EIP_FAST_ENC
+                Distortion hadCost = distParamHad.distFunc(distParamHad);
+                Distortion minSadHad = std::min(sadCost * 2, hadCost);
+#else
                 Distortion minSadHad = std::min(sadCost * 2, distParamHad.distFunc(distParamHad));
+#endif
 #else
                 Distortion minSadHad =
                   std::min(distParamSad.distFunc(distParamSad) * 2, distParamHad.distFunc(distParamHad));
@@ -2292,6 +2438,9 @@ bool IntraSearch::estIntraPredLumaQT(CodingUnit &cu, Partitioner &partitioner, c
                 double cost            = double(minSadHad) + double(fracModeBits) * sqrtLambdaForFirstPass;
 #if JVET_AD0208_IBC_ADAPT_FOR_CAM_CAPTURED_CONTENTS
                 m_bestIntraSADCost = std::min(m_bestIntraSADCost, cost - double(minSadHad) + (double)sadCost);
+#endif
+#if BV_EIP_FAST_ENC
+                m_bestIntraHADCost = std::min(m_bestIntraHADCost, cost - (double)minSadHad + (double)hadCost);
 #endif
                 mipHadCost[uiModeFull] = cost;
                 DTRACE(g_trace_ctx, D_INTRA_COST, "IntraMIP: %u, %llu, %f (%d)\n", minSadHad, fracModeBits, cost,
@@ -2650,13 +2799,21 @@ bool IntraSearch::estIntraPredLumaQT(CodingUnit &cu, Partitioner &partitioner, c
                 Distortion minSadHad = 0;
 #if JVET_AD0208_IBC_ADAPT_FOR_CAM_CAPTURED_CONTENTS
                 Distortion sadCost = distParamSad.distFunc(distParamSad);
+#if BV_EIP_FAST_ENC
+                Distortion hadCost = distParamHad.distFunc(distParamHad);
+                minSadHad += std::min(sadCost * 2, hadCost);
+#else
                 minSadHad += std::min(sadCost * 2, distParamHad.distFunc(distParamHad));
+#endif
 #else
                 minSadHad += std::min(distParamSad.distFunc(distParamSad) * 2, distParamHad.distFunc(distParamHad));
 #endif
                 double cost = (double) minSadHad + (double) fracModeBits * sqrtLambdaForFirstPass;
 #if JVET_AD0208_IBC_ADAPT_FOR_CAM_CAPTURED_CONTENTS
                 m_bestIntraSADCost = std::min(m_bestIntraSADCost, cost - (double)minSadHad + (double)sadCost);
+#endif
+#if BV_EIP_FAST_ENC
+                m_bestIntraHADCost = std::min(m_bestIntraHADCost, cost - (double)minSadHad + (double)hadCost);
 #endif
                 updateCandList(ModeInfo(false, false, 0, NOT_INTRA_SUBPARTITIONS, SGPM_IDX,
 #if JVET_V0130_INTRA_TMP
@@ -2721,10 +2878,18 @@ bool IntraSearch::estIntraPredLumaQT(CodingUnit &cu, Partitioner &partitioner, c
           {
             if (eipSaveFlag)
             {
+#if BV_EIP_FAST_ENC
+              double bestEipCostHAD = MAX_DOUBLE;
+#endif
               m_uiSavedRdModeListEip.clear();
               m_uiSavedHadModeListEip.clear();
               m_dSavedModeCostEip.clear();
               m_dSavedHadListEip.clear();
+#if MM_EIP
+              m_encBestEipCost = MAX_DOUBLE;
+              cu.eipMmFlag = false;
+              m_numSigEip = 0;
+#endif
               cu.tmpFlag = false;
               cu.mipFlag = false;
               cu.sgpm = false;
@@ -2735,9 +2900,175 @@ bool IntraSearch::estIntraPredLumaQT(CodingUnit &cu, Partitioner &partitioner, c
               initEipParams(pu, COMPONENT_Y);
               static_vector<EipModelCandidate,NUM_DERIVED_EIP> eipModelCandList;
               static_vector<EipModelCandidate, MAX_MERGE_EIP> eipMergeCandList;
+#if BV_EIP
+              static_vector<EipModelCandidate, NUM_DERIVED_BVEIP> bvEipModelCandList;
+#endif
               getCurEipCands(pu, eipModelCandList);          
+#if !BV_EIP_FAST_ENC
               getNeiEipCands(pu, eipMergeCandList);          
+#if BV_EIP_MRG
+              RefTemplateType templateType = getRefTemplateType(cu, cu.blocks[COMPONENT_Y]);
+              initBvEipParams(pu, templateType);
+              static_vector<EipModelCandidate, NUM_DERIVED_BVEIP> bvEipMergeCands;
+              getBvEipMergeCands(pu, bvEipMergeCands);
+              static_vector<EipModelCandidate, MAX_MERGE_EIP + NUM_DERIVED_BVEIP> tempMergeCands;
+              tempMergeCands.insert(tempMergeCands.begin(), eipMergeCandList.begin(), eipMergeCandList.end());
+              tempMergeCands.insert(tempMergeCands.end(), bvEipMergeCands.begin(), bvEipMergeCands.end());
+              reorderEipCands(pu, tempMergeCands);
+              eipMergeCandList.clear();
+              eipMergeCandList.insert(eipMergeCandList.begin(), tempMergeCands.begin(), tempMergeCands.end());
+              CHECK(int(eipMergeCandList.size()) > NUM_EIP_MERGE_SIGNAL, "");
+#else
               reorderEipCands(pu, eipMergeCandList);
+#endif
+#else
+              bool skipBVEIPcheck = false;
+              static_vector<EipModelCandidate, NUM_MERGE_BV_EIP> bvEipMergeCands;
+#endif
+#if BV_EIP
+#if !BV_EIP_FAST_ENC
+              if (m_tmpNumCand)
+              {
+                RefTemplateType templateType = getRefTemplateType(cu, cu.blocks[COMPONENT_Y]);
+                initBvEipParams(pu, templateType);
+                getBvEipCands(pu, bvEipModelCandList);
+              }
+              else if(getAllowedEip(*pu.cu, COMPONENT_Y) && allowBvEip(*pu.cu, COMPONENT_Y))
+              {
+                bvEipModelCandList.push_back(eipModelCandList[0]);
+              }
+#endif
+              const int numRdEIP = std::max(NUM_EIP_MERGE_SIGNAL + NUM_DERIVED_EIP + NUM_DERIVED_BVEIP, (numModesForFullRD + 1) / 2);
+              for (int whichEip = 0; whichEip < 3; whichEip++)
+              {
+#if BV_EIP_FAST_ENC
+                if (whichEip == 1)
+                {
+#if BV_EIP_RA_ENC
+                  if (bestEipCostHAD > m_bestIntraHADCost * ENC_SKIP_SATD_MRGEIP_FACTOR && bestEipCostHAD != MAX_DOUBLE && m_pcEncCfg->getIntraPeriod() == 1)
+#else
+                  if (bestEipCostHAD > m_bestIntraHADCost * ENC_SKIP_SATD_MRGEIP_FACTOR && bestEipCostHAD != MAX_DOUBLE)
+#endif
+                  {
+                    break;
+                  }
+                  else
+                  {
+#if BV_EIP_MRG
+                    getNeiEipCands(pu, eipMergeCandList);   
+                    RefTemplateType templateType = getRefTemplateType(cu, cu.blocks[COMPONENT_Y]);
+                    initBvEipParams(pu, templateType);
+                    static_vector<EipModelCandidate, NUM_DERIVED_BVEIP> bvEipMergeCands;
+                    getBvEipMergeCands(pu, bvEipMergeCands);
+                    static_vector<EipModelCandidate, MAX_MERGE_EIP + NUM_DERIVED_BVEIP> tempMergeCands;
+                    tempMergeCands.insert(tempMergeCands.begin(), eipMergeCandList.begin(), eipMergeCandList.end());
+                    tempMergeCands.insert(tempMergeCands.end(), bvEipMergeCands.begin(), bvEipMergeCands.end());
+                    reorderEipCands(pu, tempMergeCands);
+                    eipMergeCandList.clear();
+                    eipMergeCandList.insert(eipMergeCandList.begin(), tempMergeCands.begin(), tempMergeCands.end());
+                    CHECK(int(eipMergeCandList.size()) > NUM_EIP_MERGE_SIGNAL, "");
+#else
+                    getNeiEipCands(pu, eipMergeCandList);
+                    reorderEipCands(pu, eipMergeCandList);
+#endif
+#if BV_EIP_MRG
+                    skipBVEIPcheck = (!bvEipMergeCands.empty() && !m_bvBasedMergeCandidates.empty() && (m_tmpXdisp[0] == m_bvBasedMergeCandidates[0].getHor()) && (m_tmpYdisp[0] == m_bvBasedMergeCandidates[0].getVer())) ? true : false;
+#endif
+                  }
+                }
+#if BV_EIP_FAST_ENC2
+                else if (whichEip == 2 && (tmpBestSatdCost < m_bestIntraHADCost * ENC_SKIP_EIP_ACCOR_TMP
+#if BV_EIP_RA_ENC
+                  && m_pcEncCfg->getIntraPeriod() == 1
+#endif
+                  ))
+#else
+                else if (whichEip == 2)
+#endif
+                {
+                  for (auto cIdx = 0; cIdx < int(eipMergeCandList.size()); cIdx++)
+                  {
+                    eipMergeCandList[cIdx].isBvEip = false;
+                  }
+                  for (auto cIdx = 0; cIdx < int(bvEipMergeCands.size()); cIdx++)
+                  {
+                    bvEipMergeCands[cIdx].isBvEip = false;
+                  }
+
+#if BV_EIP_RA_ENC
+                  if (bestEipCostHAD > m_bestIntraHADCost * ENC_SKIP_SATD_BVEIP_FACTOR && bestEipCostHAD != MAX_DOUBLE && m_pcEncCfg->getIntraPeriod() == 1)
+#else
+                  if (bestEipCostHAD > m_bestIntraHADCost * ENC_SKIP_SATD_BVEIP_FACTOR && bestEipCostHAD != MAX_DOUBLE)  
+#endif
+                  {
+                    break;
+                  }
+                  else
+                  {
+#if BV_EIP_QUICK_SEARCH
+                    if (m_tmpQuickCand)
+#else
+                    if (m_tmpNumCand)
+#endif
+                    {
+                      if (!skipBVEIPcheck)
+                      {
+#if !BV_EIP_MRG
+                        RefTemplateType templateType = getRefTemplateType(cu, cu.blocks[COMPONENT_Y]);
+                        initBvEipParams(pu, templateType);
+#endif
+                        getBvEipCands(pu, bvEipModelCandList);
+                      }
+                    }
+                    else if (getAllowedEip(*pu.cu, COMPONENT_Y) && allowBvEip(*pu.cu, COMPONENT_Y))
+                    {
+                      bvEipModelCandList.push_back(eipModelCandList[0]);
+                    }
+                  }
+                }
+#endif
+                cu.eipMerge = bool(whichEip == 1);
+                cu.bvEip = bool(whichEip == 2);
+                int numEipLoop;
+                switch (whichEip)
+                {
+                case 0: numEipLoop = int(eipModelCandList.size()); break;
+                case 1: numEipLoop = int(eipMergeCandList.size()); break;
+                case 2: numEipLoop = int(bvEipModelCandList.size()); break;
+                default: THROW("error Eip type!"); break;
+                }
+                for (int i = 0; i < numEipLoop; i++)
+                {
+                  pu.intraDir[0] = i;
+#if MM_EIP
+                  cu.eipMmFlag = (!cu.eipMerge && !cu.bvEip) && (i >= m_numSigEip);
+#if MM_EIP_REFACTOR
+                  if(cu.eipMmFlag)
+                  {
+                    pu.intraDir[0] -= m_numSigEip;
+                  }
+#endif
+#endif
+                  if (cu.bvEip)
+                  {
+                    cu.eipModel = bvEipModelCandList[i];
+                    m_bvEipModel[i] = cu.eipModel;
+                  }
+                  else if (cu.eipMerge)
+                  {
+                    cu.eipModel = eipMergeCandList[i];
+                    m_eipMergeModel[i] = cu.eipModel;
+#if EIP_DIMD_OPT
+                    m_eipMergeModel[i].eipDimdMode = -1;
+#endif
+                  }
+                  else
+                  {
+                    cu.eipModel = eipModelCandList[i];
+                    m_eipModel[i] = cu.eipModel;
+                  }
+                  
+#else
               const int numRdEIP = std::max(NUM_EIP_MERGE_SIGNAL + NUM_DERIVED_EIP, (numModesForFullRD + 1) / 2);
               for(int mergeFlag = 0; mergeFlag < 2; mergeFlag++)
               {
@@ -2745,36 +3076,83 @@ bool IntraSearch::estIntraPredLumaQT(CodingUnit &cu, Partitioner &partitioner, c
                 for(int i = 0; i < (cu.eipMerge ? eipMergeCandList.size() : eipModelCandList.size()); i++)
                 {
                   pu.intraDir[0] = i;
+#if MM_EIP
+                  cu.eipMmFlag = (!cu.eipMerge) && (i >= m_numSigEip);
+#if MM_EIP_REFACTOR
+                  if(cu.eipMmFlag)
+                  {
+                    pu.intraDir[0] -= m_numSigEip;
+                  }
+#endif
+#endif
                   cu.eipModel = cu.eipMerge ? eipMergeCandList[i] : eipModelCandList[i];
                   if(cu.eipMerge)
                   {
                     m_eipMergeModel[i] = cu.eipModel;
+#if EIP_DIMD_OPT
+                    m_eipMergeModel[i].eipDimdMode = -1;
+#endif
                   }
                   else
                   {
                     m_eipModel[i] = cu.eipModel;
                   }
+#endif
                   eipPred(pu, piPred);
 #if JVET_AD0208_IBC_ADAPT_FOR_CAM_CAPTURED_CONTENTS
                   Distortion sadCost = distParamSad.distFunc(distParamSad);
+#if BV_EIP_FAST_ENC
+                  Distortion hadCost = distParamHad.distFunc(distParamHad);
+                  Distortion minSadHad = std::min(sadCost * 2, hadCost);
+#else
                   Distortion minSadHad = std::min(sadCost * 2, distParamHad.distFunc(distParamHad));
+#endif
 #else
                   Distortion minSadHad = std::min(distParamSad.distFunc(distParamSad) * 2, distParamHad.distFunc(distParamHad));
 #endif
                   loadStartStates();
+#if MM_EIP_REFACTOR
+                  uint64_t fracModeBits = xFracModeBitsIntra(pu, pu.intraDir[0], CHANNEL_TYPE_LUMA);
+#else
                   uint64_t fracModeBits = xFracModeBitsIntra(pu, i, CHANNEL_TYPE_LUMA);
+#endif  
                   double cost = double(minSadHad) + double(fracModeBits) * sqrtLambdaForFirstPass;
+#if BV_EIP_FAST_ENC
+#if MM_EIP
+                  if (whichEip != 2 && !cu.eipModel.bMm)
+#else
+                  if (whichEip != 2)
+#endif
+                  {
+                    bestEipCostHAD = std::min(bestEipCostHAD, cost - (double)minSadHad + (double)hadCost);
+                  }
+#endif
 #if JVET_AD0208_IBC_ADAPT_FOR_CAM_CAPTURED_CONTENTS
                   m_bestIntraSADCost = std::min(m_bestIntraSADCost, cost - (double)minSadHad + (double)sadCost);
 #endif
-
+#if BV_EIP
+                  ModeInfo modeInfo(false, cu.eipMerge, 0, NOT_INTRA_SUBPARTITIONS, EIP_IDX + i, false, 0, cu.bvEip);
+                  PelBuf eipSaveBuf(cu.eipMerge ? m_eipMergePredBuf[i] : (cu.bvEip ? m_bvEipPredBuf[i] : m_eipPredBuf[i]), pu.Y());
+#else
                   ModeInfo modeInfo(false, cu.eipMerge, 0, NOT_INTRA_SUBPARTITIONS, EIP_IDX + i);
                   PelBuf eipSaveBuf(cu.eipMerge ? m_eipMergePredBuf[i]: m_eipPredBuf[i], pu.Y());
+#endif
                   eipSaveBuf.copyFrom(piPred);
+#if MM_EIP
+#if BV_EIP_ENC
+                  if(!cu.eipModel.bMm && !cu.eipModel.isBvEip && cost < m_encBestEipCost)
+#else
+                  if(!cu.eipModel.bMm && cost < m_encBestEipCost)
+#endif
+                  {
+                    m_encBestEipCost = cost;
+                  }
+#endif
                   updateCandList(modeInfo, cost, m_uiSavedRdModeListEip, m_dSavedModeCostEip, numRdEIP);
                   updateCandList(modeInfo, double(minSadHad * 0.8), m_uiSavedHadModeListEip, m_dSavedHadListEip, numRdEIP);
                 }
               }
+#if !EIP_DIMD_OPT
               for(const auto& modeInfo: m_uiSavedRdModeListEip)
               {
                 CHECK(modeInfo.modeId < EIP_IDX || modeInfo.modeId >= EIP_IDX + std::max(NUM_DERIVED_EIP, MAX_MERGE_EIP), "A non-EIP mode is in EIP mode list") 
@@ -2812,8 +3190,22 @@ bool IntraSearch::estIntraPredLumaQT(CodingUnit &cu, Partitioner &partitioner, c
                   CHECK(modeIdx >= NUM_DERIVED_EIP, "modeIdx >= NUM_DERIVED_EIP");
                 }
               }
+#endif
+#if EIP_CU_CHK_SAD || BV_EIP_FAST_ENC3
+              if (m_dSavedModeCostEip.size() > 0)
+              {
+                isEipModeTestd = true;
+                eipBestSatdCost = m_dSavedModeCostEip[0];
+              }
+#endif
               cu.eipFlag = false;
               cu.eipMerge = false;
+#if BV_EIP
+              cu.bvEip = false;
+#endif			  
+#if MM_EIP
+              cu.eipMmFlag = false;
+#endif
             }
             for (auto i = 0; i < m_uiSavedRdModeListEip.size(); i++)
             {
@@ -2828,9 +3220,17 @@ bool IntraSearch::estIntraPredLumaQT(CodingUnit &cu, Partitioner &partitioner, c
             }
             int numNonEip = numModesForFullRD - numEip;
             int lastModeId = uiRdModeList[numModesForFullRD - 1].modeId;
-            bool lastModeIsEip = (lastModeId >= EIP_IDX && lastModeId < EIP_IDX + std::max(NUM_DERIVED_EIP, MAX_MERGE_EIP)) ;
+            bool lastModeIsEip = (lastModeId >= EIP_IDX && lastModeId < EIP_IDX + std::max(NUM_DERIVED_EIP, MAX_MERGE_EIP));
+#if MM_EIP
+            bool reduceRD =  m_dSavedModeCostEip.size() ? (pu.Y().area() < 256) && (m_encBestEipCost < candCostList[numModesForFullRD - 1]) && (lastModeIsEip || (numNonEip > 1)) : false;
+#else
             bool reduceRD =  m_dSavedModeCostEip.size() ? (pu.Y().area() < 256) && (m_dSavedModeCostEip[0] < candCostList[numModesForFullRD - 1]) && (lastModeIsEip || (numNonEip > 1)) : false;
+#endif 
+#if EIP_RA_ENC
+            if( reduceRD && m_pcEncCfg->getIntraPeriod() == 1)
+#else
             if( reduceRD && pu.cs->slice->isIntra())
+#endif
             {
               uiRdModeList.pop_back();
               candCostList.pop_back();
@@ -2856,6 +3256,31 @@ bool IntraSearch::estIntraPredLumaQT(CodingUnit &cu, Partitioner &partitioner, c
 #endif
           }
 #endif
+#if BV_EIP_FAST_ENC3
+          if (isTmpModeTestd && isEipModeTestd)
+          {
+            if (relatedCU && !relatedCU->skipTmpFlm && (tmpBestSatdCost > m_bestIntraSADHADCost * ENC_FLM_SAD_CHK_RATE) && (eipBestSatdCost > m_bestIntraSADHADCost * ENC_FLM_SAD_CHK_RATE))
+            {
+              relatedCU->skipTmpFlm = true;
+            }
+          }
+#endif
+#if EIP_CU_CHK_SAD
+          if(isEipModeTestd && m_pcEncCfg->getIntraPeriod() == 1)
+          {
+#if JVET_AI0136_ADAPTIVE_DUAL_TREE
+            if(relatedCU && !relatedCU->skipEip && (eipBestSatdCost > m_bestIntraSADHADCost * ENC_EIP_SAD_CHK_RATE))
+            {
+              relatedCU->skipEip = true;
+            }
+#else
+            if(!relatedCU.skipEip && (eipBestSatdCost > m_bestIntraSADHADCost * ENC_EIP_SAD_CHK_RATE))
+            {
+              relatedCU.skipEip = true;
+            }
+#endif
+          }
+#endif
 #endif
           if (m_pcEncCfg->getFastUDIUseMPMEnabled())
           {
@@ -3354,7 +3779,11 @@ bool IntraSearch::estIntraPredLumaQT(CodingUnit &cu, Partitioner &partitioner, c
       cu.tmpFlag = uiOrgMode.tmpFlag;
 #if JVET_AD0086_ENHANCED_INTRA_TMP
       cu.tmpIdx = uiOrgMode.tmpIdx;
+#if BV_EIP
+      cu.tmpFusionFlag = false;
+#else
       cu.tmpFusionFlag = uiOrgMode.tmpFusionFlag;
+#endif
       cu.tmpFlmFlag = uiOrgMode.tmpFlmFlag;
 #if JVET_AG0136_INTRA_TMP_LIC
       cu.tmpLicFlag = uiOrgMode.tmpLicFlag;
@@ -3367,6 +3796,19 @@ bool IntraSearch::estIntraPredLumaQT(CodingUnit &cu, Partitioner &partitioner, c
         cu.tmpSubPelIdx = uiOrgMode.tmpSubPelIdx;
 #if JVET_AH0200_INTRA_TMP_BV_REORDER
         cu.tmpFracIdx    = uiOrgMode.tmpFracIdx;
+#endif
+#if BV_EIP
+        cu.tmpFusionFlag = uiOrgMode.tmpFusionFlag;
+#endif
+#if BV_EIP_FAST_SKIP_FLM_LFNST
+        if (cu.tmpFlmFlag)
+        {
+          if (m_skipTmpFlmLfnstMtsPass)
+          {
+            CHECK(!cu.lfnstIdx && !cu.mtsFlag, "invalid logic");
+            continue;
+          }
+        }
 #endif
       }
 #if JVET_AH0200_INTRA_TMP_BV_REORDER
@@ -3391,9 +3833,76 @@ bool IntraSearch::estIntraPredLumaQT(CodingUnit &cu, Partitioner &partitioner, c
       cu.eipFlag = (mode >= 0) && (uiOrgMode.modeId >= EIP_IDX) && (uiOrgMode.modeId < EIP_IDX + std::max(NUM_DERIVED_EIP, MAX_MERGE_EIP));
       if (cu.eipFlag)
       {
+#if EIP_LFNST_FAST
+        if (m_skipEipLfnstMtsPass)
+        {
+          cu.eipFlag = false;
+          CHECK(!cu.lfnstIdx && !cu.mtsFlag, "invalid logic");
+          continue;
+        }
+#endif
         cu.eipMerge = uiOrgMode.mipTrFlg;
         pu.intraDir[0] = uiOrgMode.modeId - EIP_IDX;
         cu.eipModel = cu.eipMerge ? m_eipMergeModel[pu.intraDir[0]] : m_eipModel[pu.intraDir[0]];
+#if BV_EIP
+        cu.bvEip = uiOrgMode.tmpFusionFlag;
+        if (cu.bvEip)
+        {
+          cu.eipModel = m_bvEipModel[pu.intraDir[0]];
+        }
+#endif
+#if BV_EIP && MM_EIP
+        cu.eipMmFlag = (!cu.eipMerge && !cu.bvEip) && (pu.intraDir[0] >= m_numSigEip);
+#elif MM_EIP
+        cu.eipMmFlag = (!cu.eipMerge) && (pu.intraDir[0] >= m_numSigEip);
+#endif  
+#if MM_EIP_REFACTOR
+        if(cu.eipMmFlag)
+        {
+          pu.intraDir[0] -= m_numSigEip;
+        }
+#endif
+#if EIP_DIMD_OPT
+        if ((cu.lfnstIdx || cu.mtsFlag) && cu.eipModel.eipDimdMode == -1)
+        {
+#if MM_EIP_REFACTOR
+          const auto modeIdx = cu.eipMmFlag ? (pu.intraDir[0] + m_numSigEip): pu.intraDir[0];
+#else
+          const auto modeIdx = pu.intraDir[0];
+#endif
+          if(cu.eipMerge)
+          {
+            PelBuf eipSaveBuf(m_eipMergePredBuf[modeIdx], pu.Y());
+            cu.eipModel.eipDimdMode = m_eipMergeModel[modeIdx].eipDimdMode = deriveIpmForTransform(eipSaveBuf, cu
+#if JVET_AI0050_INTER_MTSS
+              , cu.dimdDerivedIntraDir2nd
+#endif
+            );
+            CHECK(modeIdx >= NUM_EIP_MERGE_SIGNAL, "modeIdx >= NUM_EIP_MERGE_SIGNAL");
+          }
+#if BV_EIP
+          else if(cu.bvEip)
+          {
+            PelBuf eipSaveBuf(m_bvEipPredBuf[modeIdx], pu.Y());
+            cu.eipModel.eipDimdMode = m_bvEipModel[modeIdx].eipDimdMode = deriveIpmForTransform(eipSaveBuf, cu
+#if JVET_AI0050_INTER_MTSS
+              , cu.dimdDerivedIntraDir2nd
+#endif
+            );
+          }
+#endif
+          else
+          {
+            PelBuf eipSaveBuf(m_eipPredBuf[modeIdx], pu.Y());
+            cu.eipModel.eipDimdMode = m_eipModel[modeIdx].eipDimdMode = deriveIpmForTransform(eipSaveBuf, cu
+#if JVET_AI0050_INTER_MTSS
+              , cu.dimdDerivedIntraDir2nd
+#endif
+            );
+            CHECK(modeIdx >= NUM_DERIVED_EIP, "modeIdx >= NUM_DERIVED_EIP");
+          }
+        }
+#endif
       }
 #endif
 #if JVET_W0123_TIMD_FUSION
@@ -3698,6 +4207,24 @@ bool IntraSearch::estIntraPredLumaQT(CodingUnit &cu, Partitioner &partitioner, c
             }
           }
         }
+#endif
+#if EIP_LFNST_FAST
+        if (setSkipEipControl)
+        {
+          if (cu.eipFlag && (csTemp->cost < eipCost))
+          {
+            eipCost = csTemp->cost;
+          }
+        }
+#endif
+#if BV_EIP_FAST_SKIP_FLM_LFNST
+        if (setSkipFlmControl)
+        {
+          if (cu.tmpFlmFlag && csTemp->cost < tmpFlmCost)
+          {
+            tmpFlmCost = csTemp->cost;
+          }
+        }
 #endif
         // check r-d cost
         if( csTemp->cost < csBest->cost )
@@ -3817,6 +4344,15 @@ bool IntraSearch::estIntraPredLumaQT(CodingUnit &cu, Partitioner &partitioner, c
       }
     }
 #endif
+#if BV_EIP_FAST_SKIP_FLM_LFNST
+    if (setSkipFlmControl && regAngCost != MAX_DOUBLE && tmpFlmCost != MAX_DOUBLE)
+    {
+      if (regAngCost * 1.3 < tmpFlmCost)
+      {
+        m_skipTmpFlmLfnstMtsPass = true;
+      }
+    }
+#endif  
 #if JVET_AH0076_OBIC
     if (setSkipDimdControl)
     {
@@ -3829,6 +4365,15 @@ bool IntraSearch::estIntraPredLumaQT(CodingUnit &cu, Partitioner &partitioner, c
         m_skipObicLfnstMtsPass = true;
       }
     }
+#endif
+#if EIP_LFNST_FAST
+    if (setSkipEipControl)
+    {
+      if(regAngCost != MAX_DOUBLE && eipCost != MAX_DOUBLE && regAngCost * EIP_LNFST_MTS_RATE < eipCost)
+      {
+        m_skipEipLfnstMtsPass = true;
+      }
+    }
 #endif
     cu.ispMode = uiBestPUMode.ispMod;
     cu.lfnstIdx = bestLfnstIdx;
@@ -3863,7 +4408,18 @@ bool IntraSearch::estIntraPredLumaQT(CodingUnit &cu, Partitioner &partitioner, c
 #endif
 #if JVET_AD0086_ENHANCED_INTRA_TMP
       cu.tmpIdx        = uiBestPUMode.tmpIdx;
+#if BV_EIP
+      if (cu.tmpFlag)
+      {
+        cu.tmpFusionFlag = uiBestPUMode.tmpFusionFlag;
+      }
+      else
+      {
+        cu.tmpFusionFlag = false;
+      }
+#else
       cu.tmpFusionFlag = uiBestPUMode.tmpFusionFlag;
+#endif
       cu.tmpFlmFlag    = uiBestPUMode.tmpFlmFlag;
 #if JVET_AG0136_INTRA_TMP_LIC
       cu.tmpLicFlag = uiBestPUMode.tmpLicFlag;
@@ -3944,6 +4500,74 @@ bool IntraSearch::estIntraPredLumaQT(CodingUnit &cu, Partitioner &partitioner, c
         cu.eipMerge = uiBestPUMode.mipTrFlg;
         pu.intraDir[0] = uiBestPUMode.modeId - EIP_IDX;
         cu.eipModel = cu.eipMerge ? m_eipMergeModel[pu.intraDir[0]] : m_eipModel[pu.intraDir[0]];
+#if BV_EIP
+        cu.bvEip = uiBestPUMode.tmpFusionFlag;
+        if (cu.bvEip)
+        {
+          cu.eipModel = m_bvEipModel[pu.intraDir[0]];
+          pu.interDir = 1;
+          pu.refIdx[REF_PIC_LIST_0] = MAX_NUM_REF;
+#if BV_EIP_QUICK_SEARCH
+          pu.mv[0] = m_tmpQuickCand ? Mv(m_tmpXdispQuick << MV_FRACTIONAL_BITS_INTERNAL, m_tmpYdispQuick << MV_FRACTIONAL_BITS_INTERNAL) : Mv(0, 0);
+          pu.bv = m_tmpQuickCand ? Mv(m_tmpXdispQuick, m_tmpYdispQuick) : Mv(0, 0);
+#else
+          pu.mv[0] = m_tmpNumCand ? Mv(m_tmpXdisp[0] << MV_FRACTIONAL_BITS_INTERNAL, m_tmpYdisp[0] << MV_FRACTIONAL_BITS_INTERNAL) : Mv(0, 0);
+          pu.bv = m_tmpNumCand ? Mv(m_tmpXdisp[0], m_tmpYdisp[0]) : Mv(0, 0);
+#endif
+        }
+#endif
+#if BV_EIP && MM_EIP
+        cu.eipMmFlag = (!cu.eipMerge && !cu.bvEip) && (pu.intraDir[0] >= m_numSigEip);
+#elif MM_EIP
+        cu.eipMmFlag = (!cu.eipMerge) && (pu.intraDir[0] >= m_numSigEip);
+#endif  
+#if MM_EIP_REFACTOR
+        if(cu.eipMmFlag)
+        {
+          pu.intraDir[0] -= m_numSigEip;
+        }
+#endif
+#if EIP_DIMD_OPT
+        if (cu.eipModel.eipDimdMode == -1)
+        {
+#if MM_EIP_REFACTOR
+          const auto modeIdx = cu.eipMmFlag ? (pu.intraDir[0] + m_numSigEip): pu.intraDir[0];
+#else
+          const auto modeIdx = pu.intraDir[0];
+#endif
+          if(cu.eipMerge)
+          {
+            PelBuf eipSaveBuf(m_eipMergePredBuf[modeIdx], pu.Y());
+            cu.eipModel.eipDimdMode = m_eipMergeModel[modeIdx].eipDimdMode = deriveIpmForTransform(eipSaveBuf, cu
+#if JVET_AI0050_INTER_MTSS
+              , cu.dimdDerivedIntraDir2nd
+#endif
+            );
+            CHECK(modeIdx >= NUM_EIP_MERGE_SIGNAL, "modeIdx >= NUM_EIP_MERGE_SIGNAL");
+          }
+#if BV_EIP
+          else if(cu.bvEip)
+          {
+            PelBuf eipSaveBuf(m_bvEipPredBuf[modeIdx], pu.Y());
+            cu.eipModel.eipDimdMode = m_bvEipModel[modeIdx].eipDimdMode = deriveIpmForTransform(eipSaveBuf, cu
+#if JVET_AI0050_INTER_MTSS
+              , cu.dimdDerivedIntraDir2nd
+#endif
+            );
+          }
+#endif
+          else
+          {
+            PelBuf eipSaveBuf(m_eipPredBuf[modeIdx], pu.Y());
+            cu.eipModel.eipDimdMode = m_eipModel[modeIdx].eipDimdMode = deriveIpmForTransform(eipSaveBuf, cu
+#if JVET_AI0050_INTER_MTSS
+              , cu.dimdDerivedIntraDir2nd
+#endif
+            );
+            CHECK(modeIdx >= NUM_DERIVED_EIP, "modeIdx >= NUM_DERIVED_EIP");
+          }
+        }
+#endif
       }
 #endif
 #if JVET_AC0115_INTRA_TMP_DIMD_MTS_LFNST 
@@ -9415,7 +10039,19 @@ void IntraSearch::xSelectAMTForFullRD(TransformUnit &tu
 #if JVET_AG0058_EIP
   else if (PU::isEIP(pu, chType))
   {
+#if BV_EIP
+#if MM_EIP_REFACTOR
+    const CPelBuf eipSaveBuf(pu.cu->eipMerge ? m_eipMergePredBuf[pu.intraDir[0]] : (pu.cu->bvEip ? m_bvEipPredBuf[pu.intraDir[0]] : m_eipPredBuf[pu.intraDir[0] + (pu.cu->eipMmFlag ? m_numSigEip: 0)]), pu.Y());
+#else
+    const CPelBuf eipSaveBuf(pu.cu->eipMerge ? m_eipMergePredBuf[pu.intraDir[0]] : (pu.cu->bvEip ? m_bvEipPredBuf[pu.intraDir[0]] : m_eipPredBuf[pu.intraDir[0]]), pu.Y());
+#endif
+#else 
+#if MM_EIP_REFACTOR
+    const CPelBuf eipSaveBuf(pu.cu->eipMerge ? m_eipMergePredBuf[pu.intraDir[0]] : m_eipPredBuf[pu.intraDir[0] + (pu.cu->eipMmFlag ? m_numSigEip: 0)], pu.Y());
+#else
     const CPelBuf eipSaveBuf(pu.cu->eipMerge ? m_eipMergePredBuf[pu.intraDir[0]] : m_eipPredBuf[pu.intraDir[0]], pu.Y());
+#endif
+#endif
     piPred.copyFrom(eipSaveBuf);
   }
 #endif
@@ -9789,7 +10425,19 @@ void IntraSearch::xIntraCodingTUBlock(TransformUnit &tu, const ComponentID &comp
 #if JVET_AG0058_EIP
         else if (PU::isEIP(pu, chType))
         {
+#if BV_EIP
+#if MM_EIP_REFACTOR
+          const CPelBuf eipSaveBuf(pu.cu->eipMerge ? m_eipMergePredBuf[pu.intraDir[0]] : (pu.cu->bvEip ? m_bvEipPredBuf[pu.intraDir[0]] : m_eipPredBuf[pu.intraDir[0] + (pu.cu->eipMmFlag ? m_numSigEip: 0)]), pu.Y());
+#else
+          const CPelBuf eipSaveBuf(pu.cu->eipMerge ? m_eipMergePredBuf[pu.intraDir[0]] : (pu.cu->bvEip ? m_bvEipPredBuf[pu.intraDir[0]] : m_eipPredBuf[pu.intraDir[0]]), pu.Y());
+#endif
+#else 
+#if MM_EIP_REFACTOR
+          const CPelBuf eipSaveBuf(pu.cu->eipMerge ? m_eipMergePredBuf[pu.intraDir[0]] : m_eipPredBuf[pu.intraDir[0] + (pu.cu->eipMmFlag ? m_numSigEip: 0)], pu.Y());
+#else
           const CPelBuf eipSaveBuf(pu.cu->eipMerge ? m_eipMergePredBuf[pu.intraDir[0]] : m_eipPredBuf[pu.intraDir[0]], pu.Y());
+#endif
+#endif
           piPred.copyFrom(eipSaveBuf);
         }
 #endif
diff --git a/source/Lib/EncoderLib/IntraSearch.h b/source/Lib/EncoderLib/IntraSearch.h
index bee4cf92..c3872b8a 100644
--- a/source/Lib/EncoderLib/IntraSearch.h
+++ b/source/Lib/EncoderLib/IntraSearch.h
@@ -595,6 +595,9 @@ private:
 #endif
 #if JVET_AD0208_IBC_ADAPT_FOR_CAM_CAPTURED_CONTENTS
   double     m_bestIntraSADCost;
+#endif
+#if BV_EIP_FAST_ENC
+  double     m_bestIntraHADCost;
 #endif
   ModeInfo                                           m_savedRdModeFirstColorSpace[4 * NUM_LFNST_NUM_PER_SET * 2][FAST_UDI_MAX_RDMODE_NUM];
   char                                               m_savedBDPCMModeFirstColorSpace[4 * NUM_LFNST_NUM_PER_SET * 2][FAST_UDI_MAX_RDMODE_NUM];
@@ -634,10 +637,16 @@ private:
 #if JVET_AG0058_EIP
   Pel* m_eipPredBuf[NUM_DERIVED_EIP];
   Pel* m_eipMergePredBuf[MAX_MERGE_EIP];
+#if BV_EIP
+  Pel* m_bvEipPredBuf[NUM_DERIVED_BVEIP];
+#endif
   static_vector<ModeInfo, NUM_DERIVED_EIP + MAX_MERGE_EIP> m_uiSavedRdModeListEip;
   static_vector<ModeInfo, NUM_DERIVED_EIP + MAX_MERGE_EIP> m_uiSavedHadModeListEip;
   static_vector<double, NUM_DERIVED_EIP + MAX_MERGE_EIP>   m_dSavedModeCostEip;
   static_vector<double, NUM_DERIVED_EIP + MAX_MERGE_EIP>   m_dSavedHadListEip;
+#if MM_EIP
+  double m_encBestEipCost;
+#endif
 #endif
 #if JVET_AH0076_OBIC
   Pel* m_dimdPredBuf;
@@ -724,6 +733,9 @@ public:
 #if INTRA_TRANS_ENC_OPT
   bool            m_skipTimdLfnstMtsPass;
 #endif
+#if BV_EIP_FAST_SKIP_FLM_LFNST
+  bool            m_skipTmpFlmLfnstMtsPass;
+#endif
 #if JVET_AH0076_OBIC
   bool            m_skipObicLfnstMtsPass;
   bool            m_skipDimdLfnstMtsPass;
@@ -732,6 +744,9 @@ public:
   bool            m_skipCCCMSATD;
   int             m_isCccmNoSubModeEnabledInRdo[MMLM_T_IDX + 1];
 #endif 
+#if EIP_LFNST_FAST
+  bool            m_skipEipLfnstMtsPass;
+#endif
 #if JVET_AD0202_CCCM_MDF
   bool            m_skipCCCMwithMdfSATD;
   int             m_isCccmWithMdfEnabledInRdo[5][MMLM_T_IDX + 1];
@@ -817,6 +832,9 @@ public:
 #if JVET_AG0058_EIP
   EipModelCandidate m_eipModel[NUM_DERIVED_EIP];
   EipModelCandidate m_eipMergeModel[MAX_MERGE_EIP];
+#if BV_EIP
+  EipModelCandidate m_bvEipModel[NUM_DERIVED_BVEIP];
+#endif
 #endif
 protected:
 
