diff --git a/source/Lib/CommonLib/CommonDef.h b/source/Lib/CommonLib/CommonDef.h
index bee99bcb21e37dd80613583421f626a787c25a36..e134f2a4dbebcde5c07aaa145ced313786f706e1 100644
--- a/source/Lib/CommonLib/CommonDef.h
+++ b/source/Lib/CommonLib/CommonDef.h
@@ -884,7 +884,13 @@ static const int LM_SYMBOL_NUM = (1 + NUM_LMC_MODE);
 
 static const int MAX_NUM_MIP_MODE =                                32; ///< maximum number of MIP pred. modes
 #if JVET_AB0155_SGPM
+#if JVET_AJ0112_REGRESSION_SGPM
+static const int RSGPM_CAND_NUM = 4;
+static const int SGPM_CAND_NUM = 16;
+static const int SGPM_NUM = RSGPM_CAND_NUM + SGPM_CAND_NUM;
+#else
 static const int SGPM_NUM = 16;
+#endif
 static const int FAST_UDI_MAX_RDMODE_NUM = (NUM_LUMA_MODE + MAX_NUM_MIP_MODE + SGPM_NUM);   ///< maximum number of RD comparison in fast-UDI estimation loop
 #else
 
diff --git a/source/Lib/CommonLib/IntraPrediction.cpp b/source/Lib/CommonLib/IntraPrediction.cpp
index cb57db0e926565c2c3104227b1f2f337a0829661..c18de13d3915495666cbab13239c6f23b7a7da89 100644
--- a/source/Lib/CommonLib/IntraPrediction.cpp
+++ b/source/Lib/CommonLib/IntraPrediction.cpp
@@ -445,7 +445,11 @@ void IntraPrediction::init(ChromaFormat chromaFormatIDC, const unsigned bitDepth
   }
 
   // the number of total temporal buffers can be adjusted by changing the number here
+#if JVET_AJ0112_REGRESSION_SGPM
+  m_sgpmBuffer.resize(NUM_PRIMARY_MOST_PROBABLE_MODES + SGPM_NUM_BVS + 1);
+#else
   m_sgpmBuffer.resize(1);
+#endif
 
   for (auto &buffer: m_sgpmBuffer)
   {
@@ -2669,18 +2673,21 @@ void IntraPrediction::predIntraAng( const ComponentID compId, PelBuf &piPred, co
 
 #if JVET_AB0155_SGPM
   
-  if(PU::isSgpm(pu, channelType))
+  if (PU::isSgpm(pu, channelType))
   {
-    int            width  = piPred.width;
+    int            width = piPred.width;
     int            height = piPred.height;
     const UnitArea localUnitArea(pu.chromaFormat, Area(0, 0, width, height));
     PelBuf predFusion = m_tempBuffer[1].getBuf(localUnitArea.Y());
     IntraPredParam m_ipaParam2 = m_ipaParam;
     CompArea compArea = (compID == COMPONENT_Y) ? pu.Y()
-                                               : (compID == COMPONENT_Cb) ? pu.Cb() : pu.Cr();
+      : (compID == COMPONENT_Cb) ? pu.Cb() : pu.Cr();
+#if JVET_AJ0112_REGRESSION_SGPM
+    initIntraPatternChType(*pu.cu, pu.Y(), true, 0, false);
+#endif
 #if JVET_AG0152_SGPM_ITMP_IBC
     bool isBvPredicted = 0;
-    if (pu.cu->sgpmMode1 >= SGPM_BV_START_IDX && pu.cu->ispMode == 0 )
+    if (pu.cu->sgpmMode1 >= SGPM_BV_START_IDX && pu.cu->ispMode == 0)
     {
       isBvPredicted = 1;
       Mv bv = pu.cu->sgpmBv1;
@@ -2693,61 +2700,79 @@ void IntraPrediction::predIntraAng( const ComponentID compId, PelBuf &piPred, co
     if (!isBvPredicted)
     {
 #endif
-    initIntraPatternChType(*pu.cu, compArea, false, 1); 
-    const uint32_t uiDirMode2 = PU::getFinalIntraMode(pu, channelType, 1);
-    const CPelBuf &srcBuf2 = CPelBuf(getPredictorPtr(compID), srcStride, srcHStride);
-    switch (uiDirMode2)
-    {
+      initIntraPatternChType(*pu.cu, compArea, false, 1);
+      const uint32_t uiDirMode2 = PU::getFinalIntraMode(pu, channelType, 1);
+      const CPelBuf &srcBuf2 = CPelBuf(getPredictorPtr(compID), srcStride, srcHStride);
+      switch (uiDirMode2)
+      {
 #if JVET_AC0105_DIRECTIONAL_PLANAR
-    case (PLANAR_IDX): xPredIntraPlanar(srcBuf2, predFusion, 0); break;
+      case (PLANAR_IDX): xPredIntraPlanar(srcBuf2, predFusion, 0); break;
 #else
-    case (PLANAR_IDX): xPredIntraPlanar(srcBuf2, predFusion); break;
+      case (PLANAR_IDX): xPredIntraPlanar(srcBuf2, predFusion); break;
 #endif
-    case (DC_IDX): xPredIntraDc(srcBuf2, predFusion, channelType, false); break;
+      case (DC_IDX): xPredIntraDc(srcBuf2, predFusion, channelType, false); break;
 #if JVET_AB0157_INTRA_FUSION
-    default:
-       int weightMode = 4;
-      xPredIntraAng(
+      default:
+        int weightMode = 4;
+        xPredIntraAng(
 #if JVET_AJ0057_HL_INTRA_METHOD_CONTROL
-        pu,
+          pu,
 #endif
-        srcBuf2, predFusion, channelType, clpRng, bExtIntraDir, srcBuf2nd, pu.cu->ispMode!=NOT_INTRA_SUBPARTITIONS, weightMode); break;
+          srcBuf2, predFusion, channelType, clpRng, bExtIntraDir, srcBuf2nd, pu.cu->ispMode != NOT_INTRA_SUBPARTITIONS, weightMode); break;
 #else
-    default: xPredIntraAng(
+      default: xPredIntraAng(
 #if JVET_AJ0057_HL_INTRA_METHOD_CONTROL
-      pu,
+        pu,
 #endif
-      srcBuf2, predFusion, channelType, clpRng, bExtIntraDir); break;
+        srcBuf2, predFusion, channelType, clpRng, bExtIntraDir); break;
 #endif
-    }
+      }
 
-    #if JVET_X0148_TIMD_PDPC
+#if JVET_X0148_TIMD_PDPC
 #if CIIP_PDPC
-    if ((m_ipaParam.applyPDPC || pu.ciipPDPC) && (uiDirMode2 == PLANAR_IDX || uiDirMode2 == DC_IDX))
+      if ((m_ipaParam.applyPDPC || pu.ciipPDPC) && (uiDirMode2 == PLANAR_IDX || uiDirMode2 == DC_IDX))
 #else
-    if (m_ipaParam.applyPDPC && (uiDirMode2 == PLANAR_IDX || uiDirMode2 == DC_IDX))
+      if (m_ipaParam.applyPDPC && (uiDirMode2 == PLANAR_IDX || uiDirMode2 == DC_IDX))
 #endif
-    {
-      xIntraPredPlanarDcPdpc(srcBuf2, m_tempBuffer[1].getBuf(localUnitArea.Y()).buf,
-                             m_tempBuffer[1].getBuf(localUnitArea.Y()).stride, iWidth, iHeight
+      {
+        xIntraPredPlanarDcPdpc(srcBuf2, m_tempBuffer[1].getBuf(localUnitArea.Y()).buf,
+          m_tempBuffer[1].getBuf(localUnitArea.Y()).stride, iWidth, iHeight
 #if CIIP_PDPC       
-           ,pu.ciipPDPC
+          , pu.ciipPDPC
 #endif   
-      );
-    }
+        );
+      }
 #endif
 #if JVET_AG0152_SGPM_ITMP_IBC
     }
 #endif
-    
-    m_ipaParam           = m_ipaParam2;
-    
+
+    m_ipaParam = m_ipaParam2;
+
 #if JVET_AJ0107_GPM_SHAPE_ADAPT
-    int     splitDir   = g_sgpmSplitDir[pu.cu->sgpmSplitDir];
+    int     splitDir = g_sgpmSplitDir[pu.cu->sgpmSplitDir];
 #else
-    int     splitDir   = pu.cu->sgpmSplitDir;
+    int     splitDir = pu.cu->sgpmSplitDir;
 #endif
+#if JVET_AJ0112_REGRESSION_SGPM
+    if (PU::isRegressionSgpm(pu))
+    {
+      PelUnitBuf pred = PelUnitBuf(pu.chromaFormat, piPred);
+      PelUnitBuf predTemp = PelUnitBuf(pu.chromaFormat, predFusion);
+      m_blendBuf.resize(pu.lwidth() * pu.lheight());
+      int16_t* blendBuf = m_blendBuf.data();
+      WeightBuf bufWeight = WeightBuf(blendBuf, pu.lumaSize());
+      const int geoBlendingLog2WeightBase = 5;
+      m_if.m_weightAffineBlk(pu, bufWeight, geoBlendingLog2WeightBase, pu.cu->blendModel);
+      m_if.weightedBlendBlk(pu, pu.lumaSize().width, pu.lumaSize().height, COMPONENT_Y, pred, pred, predTemp, bufWeight, geoBlendingLog2WeightBase, false);
+    }
+    else
+    {
+      m_if.m_weightedSgpm(pu, width, height, compID, splitDir, piPred, piPred, predFusion);
+    }
+#else
     m_if.m_weightedSgpm(pu, width, height, compID, splitDir, piPred, piPred, predFusion);
+#endif
   }
 #endif
 
@@ -8698,6 +8723,11 @@ void IntraPrediction::deriveSgpmModeOrdered(const CPelBuf &recoBuf, const CompAr
   bool       sadPartsNeeded[NUM_LUMA_MODE + SGPM_NUM_BVS][GEO_NUM_PARTITION_MODE] = {};
 #endif
   bool       ipmNeeded[NUM_LUMA_MODE + SGPM_NUM_BVS] = {};
+#if JVET_AJ0112_REGRESSION_SGPM
+  bool       ipmNeededforRsgpm[NUM_LUMA_MODE + SGPM_NUM_BVS] = {};
+  int        ipmIdxInList[NUM_LUMA_MODE];
+  memset(ipmIdxInList, 0, NUM_LUMA_MODE * sizeof(int));
+#endif
 
 #if JVET_AH0200_INTRA_TMP_BV_REORDER
   int numBVs = (int)m_sgpmMvBasedMergeCandidates.size();
@@ -8760,6 +8790,12 @@ void IntraPrediction::deriveSgpmModeOrdered(const CPelBuf &recoBuf, const CompAr
   for (int splitDir = 0; splitDir < GEO_NUM_PARTITION_MODE; splitDir++)
   {
 #endif
+#if JVET_AJ0112_REGRESSION_SGPM
+    if (!pu.cs->pcv->isEncoder && PU::isRegressionSgpm(pu))
+    {
+      break;
+    }
+#endif
 #if JVET_AJ0107_GPM_SHAPE_ADAPT
 #else
     if (!g_sgpmSplitDir[splitDir])
@@ -8813,17 +8849,60 @@ void IntraPrediction::deriveSgpmModeOrdered(const CPelBuf &recoBuf, const CompAr
     }
   }
 
+#if JVET_AJ0112_REGRESSION_SGPM
+  const int numRegularMode = NUM_PRIMARY_MOST_PROBABLE_MODES;
+  uint8_t mpmList[numRegularMode];
+  if (PU::isRegressionSgpmAllow(pu) && (pu.cs->pcv->isEncoder || PU::isRegressionSgpm(pu)))
+  {
+    int numCand = 0;
+    mpmList[numCand++] = PLANAR_IDX;
+    numCand += getSpatialIpm(pu, mpmList + 1, numRegularMode - 1
+#if JVET_AC0094_REF_SAMPLES_OPT
+      , true
+#endif
+    );
+    fillMPMList(pu, mpmList, numRegularMode, numCand, false);
+    for (int modeIdx = 0; modeIdx < numRegularMode; modeIdx++)
+    {
+      ipmNeededforRsgpm[mpmList[modeIdx]] = true;
+      ipmIdxInList[mpmList[modeIdx]] = modeIdx + 1;
+    }
+#if JVET_AG0152_SGPM_ITMP_IBC
+    for (int modeIdx = 0; modeIdx < numItmpIbc; modeIdx++)
+    {
+      ipmNeededforRsgpm[SGPM_BV_START_IDX + modeIdx] = true;
+    }
+#endif
+  }
+#endif
+
   for (int ipmIdx = 0; ipmIdx < NUM_LUMA_MODE; ipmIdx++)
   {
+#if JVET_AJ0112_REGRESSION_SGPM
+    if (ipmNeeded[ipmIdx] || ipmNeededforRsgpm[ipmIdx])
+#else
     if (ipmNeeded[ipmIdx])
+#endif
     {
       int iMode = MAP67TO131(ipmIdx);
       initPredTimdIntraParams(pu, area, iMode, true);
+#if JVET_AJ0112_REGRESSION_SGPM
+      Pel *tempPred = m_sgpmBuffer[ipmIdxInList[ipmIdx]].getBuf(localUnitArea.Y()).buf;
+#else
       Pel *tempPred = m_sgpmBuffer[0].getBuf(localUnitArea.Y()).buf;
+#endif
       predTimdIntraAng(COMPONENT_Y, pu, iMode, tempPred, uiPredStride, uiRealW, uiRealH, eTempType,
                        (eTempType == ABOVE_NEIGHBOR) ? 0 : iTempWidth, (eTempType == LEFT_NEIGHBOR) ? 0 : iTempHeight);
 
+#if JVET_AJ0112_REGRESSION_SGPM
+      if (!ipmNeeded[ipmIdx])
+      {
+        continue;
+      }
+      PelBuf predBuf = m_sgpmBuffer[ipmIdxInList[ipmIdx]].getBuf(localUnitArea.Y());
+#else
       PelBuf predBuf = m_sgpmBuffer[0].getBuf(localUnitArea.Y());
+#endif
       PelBuf recBuf  = cs.picture->getRecoBuf(area);
       PelBuf adBuf   = m_sgpmBuffer[0].getBuf(localUnitArea.Y());
 
@@ -8856,27 +8935,55 @@ void IntraPrediction::deriveSgpmModeOrdered(const CPelBuf &recoBuf, const CompAr
   for (int i = 0; i < numItmpIbc; i++)
   { 
     int ipmIdx = SGPM_BV_START_IDX + i;
+#if JVET_AJ0112_REGRESSION_SGPM
+    if (ipmNeeded[ipmIdx] || ipmNeededforRsgpm[ipmIdx])
+#else
     if (ipmNeeded[ipmIdx])
+#endif
     {
 #if JVET_AH0200_INTRA_TMP_BV_REORDER
+#if JVET_AJ0112_REGRESSION_SGPM
+      PelUnitBuf tempPUTopBufWithIdx(pu.chromaFormat, PelBuf(m_sgpmBuffer[NUM_PRIMARY_MOST_PROBABLE_MODES + 1 + i].getBuf(localUnitArea.Y()).buf + iTempWidth, uiPredStride, uiRealW - iTempWidth, iTempHeight));
+      PelUnitBuf tempPULeftBufWithIdx(pu.chromaFormat, PelBuf(m_sgpmBuffer[NUM_PRIMARY_MOST_PROBABLE_MODES + 1 + i].getBuf(localUnitArea.Y()).buf + iTempHeight * uiPredStride, uiPredStride, iTempWidth, uiRealH - iTempHeight));
+#endif
       Mv tempMv(BVCostVec[i].first);
       Mv mvTop(0, -iTempHeight);
       mvTop.changePrecision(MV_PRECISION_INT, MV_PRECISION_INTERNAL);
       mvTop += tempMv;
+#if JVET_AJ0112_REGRESSION_SGPM
+      m_pcInterPred->getPredIBCBlk(pu, COMPONENT_Y, pu.cs->picture, mvTop, tempPUTopBufWithIdx, true, true);
+#else
       m_pcInterPred->getPredIBCBlk(pu, COMPONENT_Y, pu.cs->picture, mvTop, tempPUTopBuf, true, true);
+#endif
 
       Mv mvLeft(-iTempWidth, 0);
       mvLeft.changePrecision(MV_PRECISION_INT, MV_PRECISION_INTERNAL);
       mvLeft += tempMv;
+#if JVET_AJ0112_REGRESSION_SGPM
+      m_pcInterPred->getPredIBCBlk(pu, COMPONENT_Y, pu.cs->picture, mvLeft, tempPULeftBufWithIdx, true, true);
+#else
       m_pcInterPred->getPredIBCBlk(pu, COMPONENT_Y, pu.cs->picture, mvLeft, tempPULeftBuf, true,true);
+#endif
+#else
+#if JVET_AJ0112_REGRESSION_SGPM
+      Pel* tempPred = m_sgpmBuffer[NUM_PRIMARY_MOST_PROBABLE_MODES + 1 + i].getBuf(localUnitArea.Y()).buf;
 #else
       Pel* tempPred = m_sgpmBuffer[0].getBuf(localUnitArea.Y()).buf;
+#endif
       Pel* piOrg = cs.picture->getRecoBuf(area).buf;
       int  iOrgStride = cs.picture->getRecoBuf(area).stride;
       piOrg += (iRefY - iCurY) * iOrgStride + (iRefX - iCurX);
       predTimdIbcItmp(COMPONENT_Y, pu, BVCostVec[i].first, tempPred, uiPredStride, uiRealW, uiRealH, eTempType, iTempWidth, iTempHeight, piOrg, iOrgStride);
 #endif
+#if JVET_AJ0112_REGRESSION_SGPM
+      if (!ipmNeeded[ipmIdx])
+      {
+        continue;
+      }
+      PelBuf predBuf = m_sgpmBuffer[NUM_PRIMARY_MOST_PROBABLE_MODES + 1 + i].getBuf(localUnitArea.Y());
+#else
       PelBuf predBuf = m_sgpmBuffer[0].getBuf(localUnitArea.Y());
+#endif
       PelBuf recBuf = cs.picture->getRecoBuf(area);
       PelBuf adBuf = m_sgpmBuffer[0].getBuf(localUnitArea.Y());
 
@@ -8908,12 +9015,85 @@ void IntraPrediction::deriveSgpmModeOrdered(const CPelBuf &recoBuf, const CompAr
 #endif
   // check every possible combination
   uint32_t cntComb = 0;
+#if JVET_AJ0112_REGRESSION_SGPM
+  static_vector<SgpmInfo, SGPM_CAND_NUM> tmpModeList;
+  static_vector<double, SGPM_CAND_NUM> tmpCostList;
+  const int bcwBlendingLog2WeightBase = 5;  // BCW is 3, GEO is 5
+  int min = 1;
+  int max = (1 << bcwBlendingLog2WeightBase) - 1;
+  AffineBlendingModel blendModel;
+  blendModel = AffineBlendingModel(bcwBlendingLog2WeightBase, min, max); // only positive weights
+  bool skipRsgpm = (!PU::isRegressionSgpmAllow(pu) || (!pu.cs->pcv->isEncoder && !PU::isRegressionSgpm(pu)));
+  if (!skipRsgpm)
+  {
+    for (int mode0Idx = 0; mode0Idx < numRegularMode + numItmpIbc - 1; mode0Idx++)
+    {
+      for (int mode1Idx = mode0Idx + 1; mode1Idx < numRegularMode + numItmpIbc; mode1Idx++)
+      {
+        if (mode0Idx == mode1Idx)
+        {
+          continue;
+        }
+
+        int ipm0Idx = 0;
+        int ipm1Idx = 0;
+        Mv sgpmBv0 = Mv(0, 0);
+        Mv sgpmBv1 = Mv(0, 0);
+        PelBuf tempPred0;
+        PelBuf tempPred1;
+
+        if (mode0Idx < numRegularMode)
+        {
+          ipm0Idx = mpmList[mode0Idx];
+          tempPred0 = m_sgpmBuffer[mode0Idx + 1].getBuf(localUnitArea.Y());
+        }
+        else
+        {
+          ipm0Idx = SGPM_BV_START_IDX + mode0Idx - numRegularMode;
+          sgpmBv0 = BVCostVec[ipm0Idx - SGPM_BV_START_IDX].first;
+          tempPred0 = m_sgpmBuffer[mode0Idx + 1].getBuf(localUnitArea.Y());
+        }
+        if (mode1Idx < numRegularMode)
+        {
+          ipm1Idx = mpmList[mode1Idx];
+          tempPred1 = m_sgpmBuffer[mode1Idx + 1].getBuf(localUnitArea.Y());
+        }
+        else
+        {
+          ipm1Idx = SGPM_BV_START_IDX + mode1Idx - numRegularMode;
+          sgpmBv1 = BVCostVec[ipm1Idx - SGPM_BV_START_IDX].first;
+          tempPred1 = m_sgpmBuffer[mode1Idx + 1].getBuf(localUnitArea.Y());
+        }
+
+        PelBuf recBuf = cs.picture->getRecoBuf(area);
+        PelBuf adBuf = m_sgpmBuffer[0].getBuf(localUnitArea.Y());
+        // derive weights
+        blendModel = AffineBlendingModel(bcwBlendingLog2WeightBase, min, max); // only positive weights
+        double cost = (double)deriveSgpmBlending(pu, tempPred0, tempPred1, recBuf, adBuf, blendModel);
+
+        cntComb++;
+
+        if ((cntComb > RSGPM_CAND_NUM && cost < candCostList[RSGPM_CAND_NUM - 1]) || cntComb <= RSGPM_CAND_NUM)
+        {
+          updateCandList(SgpmInfo(0, ipm0Idx, ipm1Idx, sgpmBv0, sgpmBv1, true, blendModel), cost, candModeList, candCostList, RSGPM_CAND_NUM);
+        }
+      }
+    }
+    cntComb = 0;
+  }
+#endif
 #if JVET_AJ0107_GPM_SHAPE_ADAPT
   for (int splitDir = 0; splitDir < SGPM_TOTAL_NUM_PARTITIONS; splitDir++)
 #else
   for (int splitDir = 0; splitDir < GEO_NUM_PARTITION_MODE; splitDir++)
 #endif
   {
+#if JVET_AJ0112_REGRESSION_SGPM
+    if (!pu.cs->pcv->isEncoder && PU::isRegressionSgpm(pu))
+    {
+      break;
+    }
+#endif
 #if JVET_AJ0107_GPM_SHAPE_ADAPT
 #else
     if (!g_sgpmSplitDir[splitDir])
@@ -8956,6 +9136,22 @@ void IntraPrediction::deriveSgpmModeOrdered(const CPelBuf &recoBuf, const CompAr
 
         cntComb++;
 
+#if JVET_AJ0112_REGRESSION_SGPM
+        if (!PU::isRegressionSgpmAllow(pu))
+        {
+          if ((cntComb > SGPM_NUM && cost < candCostList[SGPM_NUM - 1]) || cntComb <= SGPM_NUM)
+          {
+            updateCandList(SgpmInfo(splitDir, ipm0Idx, ipm1Idx, sgpmBv0, sgpmBv1, false, blendModel), cost, candModeList, candCostList, SGPM_NUM);
+          }
+        }
+        else
+        {
+          if ((cntComb > SGPM_CAND_NUM && cost < tmpCostList[SGPM_CAND_NUM - 1]) || cntComb <= SGPM_CAND_NUM)
+          {
+            updateCandList(SgpmInfo(splitDir, ipm0Idx, ipm1Idx, sgpmBv0, sgpmBv1, false, blendModel), cost, tmpModeList, tmpCostList, SGPM_CAND_NUM);
+          }
+        }
+#else
         if ((cntComb > SGPM_NUM && cost < candCostList[SGPM_NUM - 1]) || cntComb <= SGPM_NUM)
         {
 #if JVET_AG0152_SGPM_ITMP_IBC 
@@ -8964,11 +9160,190 @@ void IntraPrediction::deriveSgpmModeOrdered(const CPelBuf &recoBuf, const CompAr
           updateCandList(SgpmInfo(splitDir, ipm0Idx, ipm1Idx), cost, candModeList, candCostList, SGPM_NUM);
 #endif
         }
+#endif
       }
     }
   }
+
+#if JVET_AJ0112_REGRESSION_SGPM
+  if (PU::isRegressionSgpmAllow(pu))
+  {
+    SgpmInfo sgpmInfo = SgpmInfo();
+    if (!pu.cs->pcv->isEncoder && !PU::isRegressionSgpm(pu))
+    {
+      for (int i = 0; i < RSGPM_CAND_NUM; i++)
+      {
+        candModeList.push_back(sgpmInfo);
+        candCostList.push_back(0);
+      }
+    }
+    if (pu.cs->pcv->isEncoder || !PU::isRegressionSgpm(pu))
+    {
+      for (int i = 0; i < SGPM_CAND_NUM; i++)
+      {
+        candModeList.push_back(tmpModeList[i]);
+        candCostList.push_back(tmpCostList[i]);
+      }
+    }
+  }
+#endif
+}
+#if JVET_AJ0112_REGRESSION_SGPM
+int IntraPrediction::deriveSgpmBlending(PredictionUnit& pu, PelBuf &predBuf0, PelBuf &predBuf1, PelBuf &recBuf, PelBuf &adBuf, AffineBlendingModel &blendModel)
+{
+  int width = pu.lumaSize().width;
+  int height = pu.lumaSize().height;
+  const int channelBitDepth = pu.cs->sps->getBitDepth(CHANNEL_TYPE_LUMA);
+
+  Pel(*A)[CCCM_REF_SAMPLES_MAX] = m_a;
+  static Pel Y[BCW_MAX_REF_SAMPLES];
+
+  int sampleNum = 0;
+  const int iTempWidth = SGPM_TEMPLATE_SIZE, iTempHeight = SGPM_TEMPLATE_SIZE;
+
+  // top template
+  Pel *piPred0 = predBuf0.buf + iTempWidth;
+  Pel *piPred1 = predBuf1.buf + iTempWidth;
+  Pel *piRec = recBuf.buf - iTempHeight * recBuf.stride;
+
+  for (int y = 0; y < iTempHeight; y++)
+  {
+    for (int x = 0; x < width; x++)
+    {
+      int posX = x;
+      int posY = y - iTempHeight;
+      A[0][sampleNum] = ((piPred1[x + y * predBuf1.stride] - piPred0[x + y * predBuf0.stride]) * posX);
+      A[1][sampleNum] = ((piPred1[x + y * predBuf1.stride] - piPred0[x + y * predBuf0.stride]) * posY);
+      A[2][sampleNum] = (piPred1[x + y * predBuf1.stride] - piPred0[x + y * predBuf0.stride]);
+      Y[sampleNum++] = (piRec[x + y * recBuf.stride] - piPred0[x + y * predBuf0.stride]);
+    }
+  }
+
+  // left template
+  piPred0 = predBuf0.buf + iTempHeight * predBuf0.stride;
+  piPred1 = predBuf1.buf + iTempHeight * predBuf1.stride;
+  piRec = recBuf.buf - iTempWidth;
+
+  for (int y = 0; y < height; y++)
+  {
+    for (int x = 0; x < iTempWidth; x++)
+    {
+      int posX = x - iTempWidth;
+      int posY = y;
+      A[0][sampleNum] = ((piPred1[x + y * predBuf1.stride] - piPred0[x + y * predBuf0.stride]) * posX);
+      A[1][sampleNum] = ((piPred1[x + y * predBuf1.stride] - piPred0[x + y * predBuf0.stride]) * posY);
+      A[2][sampleNum] = (piPred1[x + y * predBuf1.stride] - piPred0[x + y * predBuf0.stride]);
+      Y[sampleNum++] = (piRec[x + y * recBuf.stride] - piPred0[x + y * predBuf0.stride]);
+    }
+  }
+  CccmModel       bcwModel(3, channelBitDepth);
+  CccmCovariance  bcwSolver;
+
+  if (!sampleNum) // should never happen
+  {
+    bcwModel.clearModel();
+  }
+  else
+  {
+#if JVET_AB0174_CCCM_DIV_FREE
+    bcwSolver.solve1(A, Y, sampleNum, 0, bcwModel);
+#else
+    bcwSolver.solve2(A, Y, Y, sampleNum, bcwModel, bcwModel);
+#endif
+  }
+
+  const int bcwBlendingLog2WeightBase = 5;
+
+  uint64_t  maxParam = blendModel.params[0] > blendModel.params[1] ? blendModel.params[0] : blendModel.params[1];
+  maxParam = blendModel.params[2] > maxParam ? blendModel.params[2] : maxParam;
+  int shiftA = floorLog2Uint64(maxParam) - 31;
+  shiftA = shiftA < 0 ? 0 : shiftA;
+
+  int offsetA = shiftA ? 1 << (shiftA - 1) : 0;
+  blendModel.params[0] = (int)((bcwModel.params[0] + offsetA) >> shiftA);
+  blendModel.params[1] = (int)((bcwModel.params[1] + offsetA) >> shiftA);
+  blendModel.params[2] = (int)((bcwModel.params[2] + offsetA) >> shiftA);
+
+  blendModel.shift = CCCM_DECIM_BITS - shiftA - bcwBlendingLog2WeightBase;
+  blendModel.offset = blendModel.shift ? (1 << (blendModel.shift - 1)) : 0;
+  if (blendModel.shift < 0)
+  {
+    printf("deriveRegressionSgpmBlending() failed.\n");
+    exit(0);
+  }
+
+  blendModel.valid = true;
+
+  const int shiftBlend = bcwBlendingLog2WeightBase;
+  const int iOne = 1 << shiftBlend;
+  const int offBlend = 1 << (shiftBlend - 1);
+
+  // check validity :
+  int cornerWeight[4];
+  cornerWeight[0] = blendModel.compute(0, 0);
+  cornerWeight[1] = blendModel.compute(width - 1, 0);
+  cornerWeight[2] = blendModel.compute(0, height - 1);
+  cornerWeight[3] = blendModel.compute(width - 1, height - 1);
+  int minWeight = cornerWeight[0];
+  int maxWeight = cornerWeight[0];
+  for (int i = 0; i < 4; i++)
+  {
+    minWeight = std::min(minWeight, cornerWeight[i]);
+    maxWeight = std::max(maxWeight, cornerWeight[i]);
+  }
+  bool unvalid = abs(minWeight - maxWeight) <= 4;
+  if (unvalid)
+  {
+    blendModel.params[0] = (int)(0);
+    blendModel.params[1] = (int)(0);
+    blendModel.params[2] = (int)(16);
+    blendModel.shift = 0;
+    blendModel.offset = 0;
+  }
+
+  // calculate SAD
+  // top template
+  piPred0 = predBuf0.buf + iTempWidth;
+  piPred1 = predBuf1.buf + iTempWidth;
+  piRec = recBuf.buf - iTempHeight * recBuf.stride;
+  Pel *piFinal = adBuf.buf + iTempWidth;
+
+  int sad = 0;
+
+  for (int y = 0; y < iTempHeight; y++)
+  {
+    for (int x = 0; x < width; x++)
+    {
+      int posX = x;
+      int posY = y - iTempHeight;
+      int iWeight = blendModel.compute(posX, posY);
+      piFinal[x + y * adBuf.stride] = ((iOne - iWeight) * piPred0[x + y * predBuf0.stride] + iWeight * piPred1[x + y * predBuf1.stride] + offBlend) >> shiftBlend;
+      sad += abs(piFinal[x + y * adBuf.stride] - piRec[x + y * recBuf.stride]);
+    }
+  }
+
+  // left template
+  piPred0 = predBuf0.buf + iTempHeight * predBuf0.stride;
+  piPred1 = predBuf1.buf + iTempHeight * predBuf1.stride;
+  piRec = recBuf.buf - iTempWidth;
+  piFinal = adBuf.buf + iTempHeight * adBuf.stride;
+
+  for (int y = 0; y < height; y++)
+  {
+    for (int x = 0; x < iTempWidth; x++)
+    {
+      int posX = x - iTempWidth;
+      int posY = y;
+      int iWeight = blendModel.compute(posX, posY);
+      piFinal[x + y * adBuf.stride] = ((iOne - iWeight) * piPred0[x + y * predBuf0.stride] + iWeight * piPred1[x + y * predBuf1.stride] + offBlend) >> shiftBlend;
+      sad += abs(piFinal[x + y * adBuf.stride] - piRec[x + y * recBuf.stride]);
+    }
+  }
+
+  return sad;
 }
 #endif
+#endif
 
 #if JVET_AD0085_MPM_SORTING
 void IntraPrediction::deriveMPMSorted(const PredictionUnit& pu, uint8_t* mpm, int& sortedSize, int iStartIdx)
diff --git a/source/Lib/CommonLib/IntraPrediction.h b/source/Lib/CommonLib/IntraPrediction.h
index 8616bc643c30ebb160d5f0e9a48660649f07e1d8..9601371d850774aeb9dce09cf084b4f2e15f8909 100644
--- a/source/Lib/CommonLib/IntraPrediction.h
+++ b/source/Lib/CommonLib/IntraPrediction.h
@@ -314,6 +314,9 @@ public:
 #endif
 
 protected:
+#if JVET_AJ0112_REGRESSION_SGPM
+  std::vector <int16_t> m_blendBuf;
+#endif
 #if JVET_AC0094_REF_SAMPLES_OPT
   Pel m_refBuffer[MAX_NUM_COMPONENT][NUM_PRED_BUF][((MAX_CU_SIZE << 3) + 1 + MAX_REF_LINE_IDX) * 2];
 #else
@@ -967,6 +970,9 @@ public:
   void deriveSgpmModeOrdered(const CPelBuf &recoBuf, const CompArea &area, CodingUnit &cu,
                              static_vector<SgpmInfo, SGPM_NUM> &candModeList,
                              static_vector<double, SGPM_NUM> &  candCostList);
+#if JVET_AJ0112_REGRESSION_SGPM
+  int deriveSgpmBlending(PredictionUnit& pu, PelBuf &predBuf0, PelBuf &predBuf1, PelBuf &recBuf, PelBuf &adBuf, AffineBlendingModel &blendModel);
+#endif
 #endif
 #if JVET_AD0085_MPM_SORTING
   void deriveMPMSorted(const PredictionUnit& pu, uint8_t* mpm, int& sortedSize, int iStartIdx);
diff --git a/source/Lib/CommonLib/TrQuant.cpp b/source/Lib/CommonLib/TrQuant.cpp
index d10f3a8ba1f08d05a1b3ad3c127c9d7f6501f1b3..effbff74de9efcf3d32cdec8da33333cdf1bd20e 100644
--- a/source/Lib/CommonLib/TrQuant.cpp
+++ b/source/Lib/CommonLib/TrQuant.cpp
@@ -550,9 +550,17 @@ void TrQuant::xInvLfnst( const TransformUnit &tu, const ComponentID compID )
     {
 #if JVET_AJ0107_GPM_SHAPE_ADAPT
       CHECK(tu.cu->sgpmSplitDir >= SGPM_TOTAL_NUM_PARTITIONS, "Invalid splitDir for SGPM");
+#if JVET_AJ0112_REGRESSION_SGPM
+      intraMode = PU::isRegressionSgpm(*tu.cs->getPU(area.pos(), toChannelType(compID))) ? tu.cu->sgpmDimdMode : g_geoAngle2IntraAng[g_geoParams[g_sgpmSplitDir[tu.cu->sgpmSplitDir]][0]];
+#else
       intraMode = g_geoAngle2IntraAng[g_geoParams[g_sgpmSplitDir[tu.cu->sgpmSplitDir]][0]];
+#endif
+#else
+#if JVET_AJ0112_REGRESSION_SGPM
+      intraMode = PU::isRegressionSgpm(*tu.cs->getPU(area.pos(), toChannelType(compID))) ? tu.cu->sgpmDimdMode : g_geoAngle2IntraAng[g_geoParams[tu.cu->sgpmSplitDir][0]];
 #else
       intraMode = g_geoAngle2IntraAng[g_geoParams[tu.cu->sgpmSplitDir][0]];
+#endif
 #endif
     }
 #endif
@@ -885,9 +893,17 @@ void TrQuant::xFwdLfnst( const TransformUnit &tu, const ComponentID compID, cons
     {
 #if JVET_AJ0107_GPM_SHAPE_ADAPT
       CHECK(tu.cu->sgpmSplitDir >= SGPM_TOTAL_NUM_PARTITIONS, "Invalid splitDir for SGPM");
+#if JVET_AJ0112_REGRESSION_SGPM
+      intraMode = PU::isRegressionSgpm(*tu.cs->getPU(area.pos(), toChannelType(compID))) ? tu.cu->sgpmDimdMode : g_geoAngle2IntraAng[g_geoParams[g_sgpmSplitDir[tu.cu->sgpmSplitDir]][0]];
+#else
       intraMode = g_geoAngle2IntraAng[g_geoParams[g_sgpmSplitDir[tu.cu->sgpmSplitDir]][0]];
+#endif
+#else
+#if JVET_AJ0112_REGRESSION_SGPM
+      intraMode = PU::isRegressionSgpm(*tu.cs->getPU(area.pos(), toChannelType(compID))) ? tu.cu->sgpmDimdMode : g_geoAngle2IntraAng[g_geoParams[tu.cu->sgpmSplitDir][0]];
 #else
       intraMode = g_geoAngle2IntraAng[g_geoParams[tu.cu->sgpmSplitDir][0]];
+#endif
 #endif
     }
 #endif
@@ -1362,9 +1378,17 @@ void TrQuant::getTrTypes(const TransformUnit tu, const ComponentID compID, int &
       {
 #if JVET_AJ0107_GPM_SHAPE_ADAPT
         CHECK(tu.cu->sgpmSplitDir >= SGPM_TOTAL_NUM_PARTITIONS, "Invalid splitDir for SGPM");
+#if JVET_AJ0112_REGRESSION_SGPM
+        predMode = PU::isRegressionSgpm(*tu.cs->getPU(area.pos(), toChannelType(compID))) ? tu.cu->sgpmDimdMode : g_geoAngle2IntraAng[g_geoParams[g_sgpmSplitDir[tu.cu->sgpmSplitDir]][0]];
+#else
         predMode = g_geoAngle2IntraAng[g_geoParams[g_sgpmSplitDir[tu.cu->sgpmSplitDir]][0]];
+#endif
+#else
+#if JVET_AJ0112_REGRESSION_SGPM
+        predMode = PU::isRegressionSgpm(*tu.cs->getPU(area.pos(), toChannelType(compID))) ? tu.cu->sgpmDimdMode : g_geoAngle2IntraAng[g_geoParams[tu.cu->sgpmSplitDir][0]];
 #else
         predMode = g_geoAngle2IntraAng[g_geoParams[tu.cu->sgpmSplitDir][0]];
+#endif
 #endif
       }
 #endif
@@ -3105,9 +3129,17 @@ int TrQuant::getLfnstIdx(const TransformUnit &tu, ComponentID compID)
   {
 #if JVET_AJ0107_GPM_SHAPE_ADAPT
     CHECK(tu.cu->sgpmSplitDir >= SGPM_TOTAL_NUM_PARTITIONS, "Invalid splitDir for SGPM");
+#if JVET_AJ0112_REGRESSION_SGPM
+    intraMode = PU::isRegressionSgpm(*tu.cs->getPU(area.pos(), toChannelType(compID))) ? tu.cu->sgpmDimdMode : g_geoAngle2IntraAng[g_geoParams[g_sgpmSplitDir[tu.cu->sgpmSplitDir]][0]];
+#else
     intraMode = g_geoAngle2IntraAng[g_geoParams[g_sgpmSplitDir[tu.cu->sgpmSplitDir]][0]];
+#endif
+#else
+#if JVET_AJ0112_REGRESSION_SGPM
+    intraMode = PU::isRegressionSgpm(*tu.cs->getPU(area.pos(), toChannelType(compID))) ? tu.cu->sgpmDimdMode : g_geoAngle2IntraAng[g_geoParams[tu.cu->sgpmSplitDir][0]];
 #else
     intraMode = g_geoAngle2IntraAng[g_geoParams[tu.cu->sgpmSplitDir][0]];
+#endif
 #endif
   }
 #endif
diff --git a/source/Lib/CommonLib/TypeDef.h b/source/Lib/CommonLib/TypeDef.h
index 3d4fabef4e11ceafea4ed3a66644e1428af45ae4..9cd2b8b16d619d0f641bf6f8efe2d36c5ddc8ecb 100644
--- a/source/Lib/CommonLib/TypeDef.h
+++ b/source/Lib/CommonLib/TypeDef.h
@@ -191,6 +191,7 @@
 #define JVET_AB0155_SGPM                                  1 // JVET-AB0155: Spatial geometric partitioning mode
 #if JVET_AB0155_SGPM
 #define JVET_AC0189_SGPM_NO_BLENDING                      1 // JVET-AC0189: Allow no blending for SGPM
+#define JVET_AJ0112_REGRESSION_SGPM                       1 // JVET-AJ0112: Regression-based SGPM blending
 #endif
 #define JVET_AB0157_TMRL                                  1 // JVET-AB0157: Template-based multiple reference line intra prediction
 #if JVET_AB0157_TMRL
diff --git a/source/Lib/CommonLib/Unit.cpp b/source/Lib/CommonLib/Unit.cpp
index 8140f4f0d2c22c6e0acb40ceabf23d30a56c1d7f..154c49bbe7d5cac024142392bbe92ab27a9beb6e 100644
--- a/source/Lib/CommonLib/Unit.cpp
+++ b/source/Lib/CommonLib/Unit.cpp
@@ -476,6 +476,9 @@ CodingUnit& CodingUnit::operator=( const CodingUnit& other )
 #if JVET_AB0067_MIP_DIMD_LFNST
   mipDimdMode       = other.mipDimdMode;
 #endif
+#if JVET_AJ0112_REGRESSION_SGPM
+  sgpmDimdMode = other.sgpmDimdMode;
+#endif
 #if JVET_V0130_INTRA_TMP
   tmpFlag           = other.tmpFlag;
 #if JVET_AC0115_INTRA_TMP_DIMD_MTS_LFNST 
@@ -786,6 +789,9 @@ void CodingUnit::initData()
 #if JVET_AB0067_MIP_DIMD_LFNST
   mipDimdMode       = 0;
 #endif
+#if JVET_AJ0112_REGRESSION_SGPM
+  sgpmDimdMode = 0;
+#endif
 #if JVET_V0130_INTRA_TMP
   tmpFlag = false;
 #if JVET_AC0115_INTRA_TMP_DIMD_MTS_LFNST 
diff --git a/source/Lib/CommonLib/Unit.h b/source/Lib/CommonLib/Unit.h
index 2f54ebf4431289fa3ef4df3f6f84e0e6ed73ed3d..9667e70b4ab7732fbab381322a03b92089277f5e 100644
--- a/source/Lib/CommonLib/Unit.h
+++ b/source/Lib/CommonLib/Unit.h
@@ -425,6 +425,9 @@ struct CodingUnit : public UnitArea
 #if JVET_AG0152_SGPM_ITMP_IBC
   Mv             sgpmBv0;
   Mv             sgpmBv1;
+#endif
+#if JVET_AJ0112_REGRESSION_SGPM
+  AffineBlendingModel sgpmBlendModel;
 #endif
   bool           sgpm;
   int            sgpmIdx;
@@ -467,6 +470,9 @@ struct CodingUnit : public UnitArea
 #if JVET_AB0067_MIP_DIMD_LFNST
   int            mipDimdMode;
 #endif
+#if JVET_AJ0112_REGRESSION_SGPM
+  int            sgpmDimdMode;
+#endif
 #if JVET_V0130_INTRA_TMP
   bool		    	 tmpFlag;
 #if JVET_AC0115_INTRA_TMP_DIMD_MTS_LFNST 
@@ -1155,8 +1161,15 @@ struct SgpmInfo
   int sgpmMode1;
   Mv   sgpmBv0;
   Mv   sgpmBv1;
+#if JVET_AJ0112_REGRESSION_SGPM
+  bool isRegression;
+  AffineBlendingModel blendModel;
+  SgpmInfo() : sgpmSplitDir(0), sgpmMode0(0), sgpmMode1(0), sgpmBv0(0, 0), sgpmBv1(0, 0), isRegression(false), blendModel(AffineBlendingModel(5, 1, 31)) {}
+  SgpmInfo(const int sd, const int sm0, const int sm1, const Mv sbv0, const Mv sbv1, bool isR, AffineBlendingModel bM) : sgpmSplitDir(sd), sgpmMode0(sm0), sgpmMode1(sm1), sgpmBv0(sbv0), sgpmBv1(sbv1), isRegression(isR), blendModel(bM){}
+#else
   SgpmInfo() : sgpmSplitDir(0), sgpmMode0(0), sgpmMode1(0), sgpmBv0(0, 0), sgpmBv1(0, 0) {}
   SgpmInfo(const int sd, const int sm0, const int sm1, const Mv sbv0, const Mv sbv1) : sgpmSplitDir(sd), sgpmMode0(sm0), sgpmMode1(sm1), sgpmBv0(sbv0), sgpmBv1(sbv1) {}
+#endif
 
   SgpmInfo& operator=(const SgpmInfo& other)
   {
@@ -1165,6 +1178,10 @@ struct SgpmInfo
     sgpmMode1 = other.sgpmMode1;
     sgpmBv0 = other.sgpmBv0;
     sgpmBv1 = other.sgpmBv1;
+#if JVET_AJ0112_REGRESSION_SGPM
+    isRegression = other.isRegression;
+    blendModel = other.blendModel;
+#endif
     return *this;
   }
 };
diff --git a/source/Lib/CommonLib/UnitTools.cpp b/source/Lib/CommonLib/UnitTools.cpp
index 64f18539c302bcb4d5201a5cebf66123562e2113..8ddae2ea281aca9d55c3df92cf9de26f8fa76065 100644
--- a/source/Lib/CommonLib/UnitTools.cpp
+++ b/source/Lib/CommonLib/UnitTools.cpp
@@ -5104,6 +5104,16 @@ bool PU::isDMChromaSgpm(const PredictionUnit &pu)
 {
   return false;
 }
+#if JVET_AJ0112_REGRESSION_SGPM
+bool PU::isRegressionSgpm(const PredictionUnit &pu)
+{
+  return (pu.cu->sgpm && isRegressionSgpmAllow(pu) && pu.cu->sgpmIdx < RSGPM_CAND_NUM);
+}
+bool PU::isRegressionSgpmAllow(const PredictionUnit &pu)
+{
+  return (pu.lwidth() > 4 && pu.lheight() > 4);
+}
+#endif
 #endif
 
 #if JVET_AH0076_OBIC
@@ -33602,9 +33612,17 @@ uint32_t PU::getFinalIntraModeForTransform( const TransformUnit &tu, const Compo
   if( PU::isSgpm( *tu.cs->getPU( area.pos(), toChannelType( compID ) ), toChannelType( compID ) ) )
   {
 #if JVET_AJ0107_GPM_SHAPE_ADAPT
+#if JVET_AJ0112_REGRESSION_SGPM
+    intraMode = PU::isRegressionSgpm(*tu.cs->getPU(area.pos(), toChannelType(compID))) ? tu.cu->sgpmDimdMode : g_geoAngle2IntraAng[g_geoParams[g_sgpmSplitDir[tu.cu->sgpmSplitDir]][0]];
+#else
     intraMode = g_geoAngle2IntraAng[g_geoParams[g_sgpmSplitDir[tu.cu->sgpmSplitDir]][0]];
+#endif
+#else
+#if JVET_AJ0112_REGRESSION_SGPM
+    intraMode = PU::isRegressionSgpm(*tu.cs->getPU(area.pos(), toChannelType(compID))) ? tu.cu->sgpmDimdMode : g_geoAngle2IntraAng[g_geoParams[tu.cu->sgpmSplitDir][0]];
 #else
     intraMode = g_geoAngle2IntraAng[g_geoParams[tu.cu->sgpmSplitDir][0]];
+#endif
 #endif
   }
 #endif
@@ -33760,9 +33778,17 @@ int getSpatialIpm(const PredictionUnit& pu, uint8_t* spatialIpm, const int maxCa
         if (neighborPu->cu->sgpm)
         {
 #if JVET_AJ0107_GPM_SHAPE_ADAPT
+#if JVET_AJ0112_REGRESSION_SGPM
+          neighborMode = PU::isRegressionSgpm(*neighborPu) ? neighborPu->cu->sgpmDimdMode : g_geoAngle2IntraAng[g_geoParams[g_sgpmSplitDir[neighborPu->cu->sgpmSplitDir]][0]];
+#else
           neighborMode = g_geoAngle2IntraAng[g_geoParams[g_sgpmSplitDir[neighborPu->cu->sgpmSplitDir]][0]];
+#endif
+#else
+#if JVET_AJ0112_REGRESSION_SGPM
+          neighborMode = PU::isRegressionSgpm(*neighborPu) ? neighborPu->cu->sgpmDimdMode : g_geoAngle2IntraAng[g_geoParams[neighborPu->cu->sgpmSplitDir][0]];
 #else
-          neighborMode = g_geoAngle2IntraAng[g_geoParams[neighborPu->cu->sgpmSplitDir][0]];;
+          neighborMode = g_geoAngle2IntraAng[g_geoParams[neighborPu->cu->sgpmSplitDir][0]];
+#endif
 #endif
         }
 #if JVET_AD0085_TMRL_EXTENSION
diff --git a/source/Lib/CommonLib/UnitTools.h b/source/Lib/CommonLib/UnitTools.h
index afbbdd79112e693e3e175cd138f23893dd59b8d1..20ffe3c56e57e314e3b79b6d1e99859dc6bfb414 100644
--- a/source/Lib/CommonLib/UnitTools.h
+++ b/source/Lib/CommonLib/UnitTools.h
@@ -279,6 +279,10 @@ namespace PU
 #if JVET_AB0155_SGPM
   bool isSgpm(const PredictionUnit &pu, const ChannelType &chType = CHANNEL_TYPE_LUMA);
   bool isDMChromaSgpm(const PredictionUnit &pu);
+#if JVET_AJ0112_REGRESSION_SGPM
+  bool isRegressionSgpm(const PredictionUnit &pu);
+  bool isRegressionSgpmAllow(const PredictionUnit &pu);
+#endif
 #endif
 #if JVET_AB0155_SGPM
   uint32_t getIntraDirLuma(const PredictionUnit &pu, const int partIdx = 0);
diff --git a/source/Lib/DecoderLib/DecCu.cpp b/source/Lib/DecoderLib/DecCu.cpp
index 7bd40bc70c7429539b5e76ea91ac35107a2ca050..1cddbaa3a2216f42eb2da3d4271f895c812b9ae1 100644
--- a/source/Lib/DecoderLib/DecCu.cpp
+++ b/source/Lib/DecoderLib/DecCu.cpp
@@ -417,8 +417,11 @@ void DecCu::decompressCtu( CodingStructure& cs, const UnitArea& ctuArea )
           static_vector<SgpmInfo, SGPM_NUM> sgpmInfoList;
           static_vector<double, SGPM_NUM>   sgpmCostList;
           int                         sgpmIdx = currCU.sgpmIdx;
-
+#if JVET_AJ0112_REGRESSION_SGPM
+          if (currCU.lwidth() * currCU.lheight() <= 1024 && currCU.cs->sps->getUseTimd() && !PU::isRegressionSgpm(*pu))
+#else
           if (currCU.lwidth() * currCU.lheight() <= 1024 && currCU.cs->sps->getUseTimd() )
+#endif
           {
             m_pcIntraPred->deriveTimdMode(currCU.cs->picture->getRecoBuf(area), area, currCU, false, true);
           }
@@ -432,6 +435,9 @@ void DecCu::decompressCtu( CodingStructure& cs, const UnitArea& ctuArea )
 #if JVET_AG0152_SGPM_ITMP_IBC
           currCU.sgpmBv0 = sgpmInfoList[sgpmIdx].sgpmBv0;
           currCU.sgpmBv1 = sgpmInfoList[sgpmIdx].sgpmBv1;
+#if JVET_AJ0112_REGRESSION_SGPM
+          currCU.blendModel = sgpmInfoList[sgpmIdx].blendModel;
+#endif
           pu->intraDir[0] = currCU.sgpmMode0 >= SGPM_BV_START_IDX ? 0 : currCU.sgpmMode0;
           pu->intraDir1[0] = currCU.sgpmMode1 >= SGPM_BV_START_IDX ? 0 : currCU.sgpmMode1;
 #else
@@ -1349,6 +1355,19 @@ void DecCu::xIntraRecBlk( TransformUnit& tu, const ComponentID compID )
 #endif
         );
       }
+#endif
+#if JVET_AJ0112_REGRESSION_SGPM
+      if (compID == COMPONENT_Y && pu.cu->sgpm && PU::isRegressionSgpm(pu))
+      {
+#if JVET_AI0050_INTER_MTSS
+        int secondDimdIntraDir = 0;
+#endif
+        m_pcIntraPred->IntraPrediction::deriveIpmForTransform(piPred, *pu.cu
+#if JVET_AI0050_INTER_MTSS
+          , secondDimdIntraDir
+#endif
+        );
+      }
 #endif
     }
   }
diff --git a/source/Lib/EncoderLib/EncCu.cpp b/source/Lib/EncoderLib/EncCu.cpp
index 6e855806252e62bc396e503bc6ff6dc35b70725e..fe626ccb17c3cb6f4ebad8df625801874e5f728d 100644
--- a/source/Lib/EncoderLib/EncCu.cpp
+++ b/source/Lib/EncoderLib/EncCu.cpp
@@ -3862,6 +3862,9 @@ bool EncCu::xCheckRDCostIntra(CodingStructure *&tempCS, CodingStructure *&bestCS
   m_pcIntraSearch->m_skipTimdLfnstMtsPass = false;
   m_modeCtrl->resetLfnstCost();
 #endif
+#if JVET_AJ0112_REGRESSION_SGPM
+  m_pcIntraSearch->m_skipSgpmLfnstMtsPass = false;
+#endif
 #if JVET_AH0076_OBIC
   m_pcIntraSearch->m_skipObicLfnstMtsPass = false;
   m_pcIntraSearch->m_skipDimdLfnstMtsPass = false;
diff --git a/source/Lib/EncoderLib/IntraSearch.cpp b/source/Lib/EncoderLib/IntraSearch.cpp
index 6259405359ad5c627a5162b05631d33165144768..f8f9b5bd89983a90413a6f337362f2422fe2c0a6 100644
--- a/source/Lib/EncoderLib/IntraSearch.cpp
+++ b/source/Lib/EncoderLib/IntraSearch.cpp
@@ -633,6 +633,9 @@ void IntraSearch::init( EncCfg*        pcEncCfg,
 #if INTRA_TRANS_ENC_OPT
   m_skipTimdLfnstMtsPass = false;
 #endif
+#if JVET_AJ0112_REGRESSION_SGPM
+  m_skipSgpmLfnstMtsPass = false;
+#endif
 #if JVET_AJ0061_TIMD_MERGE
   m_skipTimdMrgLfnstMtsPass = false;
   m_skipObicMode            = false;
@@ -988,6 +991,10 @@ bool IntraSearch::estIntraPredLumaQT(CodingUnit &cu, Partitioner &partitioner, c
   bool setSkipTimdControl = (m_pcEncCfg->getIntraPeriod() == 1) && !cu.lfnstIdx && !cu.mtsFlag;
   double timdAngCost = MAX_DOUBLE;
 #endif
+#if JVET_AJ0112_REGRESSION_SGPM
+  double sgpmCost = MAX_DOUBLE;
+  bool setSkipSgpmControl = (m_pcEncCfg->getIntraPeriod() == 1) && !cu.lfnstIdx && !cu.mtsFlag;
+#endif
 #if JVET_AJ0061_TIMD_MERGE
   bool setSkipTimdMrgControl = (m_pcEncCfg->getIntraPeriod() == 1) && !cu.lfnstIdx && !cu.mtsFlag;
   double timdMrgAngCost[NUM_TIMD_MERGE_MODES];
@@ -1065,6 +1072,9 @@ bool IntraSearch::estIntraPredLumaQT(CodingUnit &cu, Partitioner &partitioner, c
     bool bestObicMode = false;
     int bestMipDimd = 0;
 #endif
+#if JVET_AJ0112_REGRESSION_SGPM
+    int bestSgpmDimd = 0;
+#endif
 #if JVET_W0123_TIMD_FUSION
     bool bestTimdMode = false;
 #if JVET_AJ0061_TIMD_MERGE
@@ -2964,439 +2974,516 @@ bool IntraSearch::estIntraPredLumaQT(CodingUnit &cu, Partitioner &partitioner, c
           cu.dimd = false;
           cu.obicFlag = false;
 #endif
-#if JVET_AB0155_SGPM
-          if (testSgpm)
+#if JVET_AJ0112_REGRESSION_SGPM
+          if ((m_pcEncCfg->getUseFastLFNST() && !LFNSTLoadFlag) || !m_pcEncCfg->getUseFastLFNST())
           {
-            if (SGPMSaveFlag)
+#endif
+#if JVET_AB0155_SGPM
+            if (testSgpm)
             {
-              m_uiSavedRdModeListSGPM.clear();
-              m_dSavedModeCostSGPM.clear();
-              m_uiSavedHadModeListSGPM.clear();
-              m_dSavedHadListSGPM.clear();
+              if (SGPMSaveFlag)
+              {
+                m_uiSavedRdModeListSGPM.clear();
+                m_dSavedModeCostSGPM.clear();
+                m_uiSavedHadModeListSGPM.clear();
+                m_dSavedHadListSGPM.clear();
 
 #if JVET_V0130_INTRA_TMP
-              cu.tmpFlag      = false;
+                cu.tmpFlag = false;
 #endif
-              pu.multiRefIdx  = 0;
-              cu.mipFlag      = false;
-              
+                pu.multiRefIdx = 0;
+                cu.mipFlag = false;
+
 #if JVET_AB0157_INTRA_FUSION
-              initIntraPatternChType(cu, pu.Y(), true, 0, false);
+                initIntraPatternChType(cu, pu.Y(), true, 0, false);
 #else
-              initIntraPatternChType(cu, pu.Y(), true);
+                initIntraPatternChType(cu, pu.Y(), true);
 #endif
 
-              // get single mode predictions
-              for (int sgpmIdx = 0; sgpmIdx < SGPM_NUM; sgpmIdx++)
-              {
-                int      sgpmMode[2];
-                sgpmMode[0] = sgpmInfoList[sgpmIdx].sgpmMode0;
-                sgpmMode[1] = sgpmInfoList[sgpmIdx].sgpmMode1;
+                // get single mode predictions
+                for (int sgpmIdx = 0; sgpmIdx < SGPM_NUM; sgpmIdx++)
+                {
+                  int      sgpmMode[2];
+                  sgpmMode[0] = sgpmInfoList[sgpmIdx].sgpmMode0;
+                  sgpmMode[1] = sgpmInfoList[sgpmIdx].sgpmMode1;
 #if JVET_AG0152_SGPM_ITMP_IBC
-                Mv      sgpmBV[2];
-                sgpmBV[0] = sgpmInfoList[sgpmIdx].sgpmBv0;
-                sgpmBV[1] = sgpmInfoList[sgpmIdx].sgpmBv1;
+                  Mv      sgpmBV[2];
+                  sgpmBV[0] = sgpmInfoList[sgpmIdx].sgpmBv0;
+                  sgpmBV[1] = sgpmInfoList[sgpmIdx].sgpmBv1;
 #endif
-                for (int idxIn2 = 0; idxIn2 < 2; idxIn2++)
-                {
-                  if (!m_intraModeReady[sgpmMode[idxIn2]])
+                  for (int idxIn2 = 0; idxIn2 < 2; idxIn2++)
                   {
-#if JVET_AG0152_SGPM_ITMP_IBC 
-                    if (sgpmMode[idxIn2] >= SGPM_BV_START_IDX)
+                    if (!m_intraModeReady[sgpmMode[idxIn2]])
                     {
-                      // BV based mode
-                      Mv timdBv = sgpmBV[idxIn2];
+#if JVET_AG0152_SGPM_ITMP_IBC 
+                      if (sgpmMode[idxIn2] >= SGPM_BV_START_IDX)
+                      {
+                        // BV based mode
+                        Mv timdBv = sgpmBV[idxIn2];
 #if JVET_AH0200_INTRA_TMP_BV_REORDER
-                      predUsingBv(piPred.buf, piPred.stride, timdBv, cu, false);
+                        predUsingBv(piPred.buf, piPred.stride, timdBv, cu, false);
 #else
-                      predUsingBv(piPred.buf, piPred.stride, timdBv, cu);
+                        predUsingBv(piPred.buf, piPred.stride, timdBv, cu);
 #endif
-                    }
-                    else
-                    {
+                      }
+                      else
+                      {
 #endif
-                    pu.intraDir[0] = sgpmMode[idxIn2];
+                        pu.intraDir[0] = sgpmMode[idxIn2];
 
-                    initPredIntraParams(pu, pu.Y(), sps);
+                        initPredIntraParams(pu, pu.Y(), sps);
 #if JVET_AH0209_PDP
-                    predIntraAng(COMPONENT_Y, piPred, pu, false, false);
+                        predIntraAng(COMPONENT_Y, piPred, pu, false, false);
 #elif JVET_AB0157_INTRA_FUSION
-                    predIntraAng(COMPONENT_Y, piPred, pu, false);
+                        predIntraAng(COMPONENT_Y, piPred, pu, false);
 #else
-                    predIntraAng(COMPONENT_Y, piPred, pu);
+                        predIntraAng(COMPONENT_Y, piPred, pu);
 #endif
 #if JVET_AG0152_SGPM_ITMP_IBC
-                    }
+                      }
 #endif
 
 
-                    PelBuf predBuf(m_intraPredBuf[sgpmMode[idxIn2]], tmpArea);
-                    predBuf.copyFrom(piPred);
-                    m_intraModeReady[sgpmMode[idxIn2]] = 1;
+                      PelBuf predBuf(m_intraPredBuf[sgpmMode[idxIn2]], tmpArea);
+                      predBuf.copyFrom(piPred);
+                      m_intraModeReady[sgpmMode[idxIn2]] = 1;
+                    }
                   }
                 }
-              }
 
-              cu.sgpm = true;
-              // frac bits calculate once because all are the same
-              cu.sgpmIdx      = 0;
-              cu.sgpmSplitDir = sgpmInfoList[0].sgpmSplitDir;
-              cu.sgpmMode0    = sgpmInfoList[0].sgpmMode0;
-              cu.sgpmMode1    = sgpmInfoList[0].sgpmMode1;
+                cu.sgpm = true;
+                // frac bits calculate once because all are the same
+                cu.sgpmIdx = 0;
+                cu.sgpmSplitDir = sgpmInfoList[0].sgpmSplitDir;
+                cu.sgpmMode0 = sgpmInfoList[0].sgpmMode0;
+                cu.sgpmMode1 = sgpmInfoList[0].sgpmMode1;
 #if JVET_AG0152_SGPM_ITMP_IBC
-              cu.sgpmBv0 = sgpmInfoList[0].sgpmBv0;
-              cu.sgpmBv1 = sgpmInfoList[0].sgpmBv1;
-              pu.intraDir[0] = cu.sgpmMode0 >= SGPM_BV_START_IDX ? 0 : cu.sgpmMode0;
-              pu.intraDir1[0] = cu.sgpmMode1 >= SGPM_BV_START_IDX ? 0 : cu.sgpmMode1;
+                cu.sgpmBv0 = sgpmInfoList[0].sgpmBv0;
+                cu.sgpmBv1 = sgpmInfoList[0].sgpmBv1;
+                pu.intraDir[0] = cu.sgpmMode0 >= SGPM_BV_START_IDX ? 0 : cu.sgpmMode0;
+                pu.intraDir1[0] = cu.sgpmMode1 >= SGPM_BV_START_IDX ? 0 : cu.sgpmMode1;
 #else
-              pu.intraDir[0]  = cu.sgpmMode0;
-              pu.intraDir1[0] = cu.sgpmMode1;
+                pu.intraDir[0] = cu.sgpmMode0;
+                pu.intraDir1[0] = cu.sgpmMode1;
 #endif
-              
-              loadStartStates();
-
-              uint64_t fracModeBits = xFracModeBitsIntra(pu, 0, CHANNEL_TYPE_LUMA);
+#if JVET_AJ0112_REGRESSION_SGPM
+                cu.blendModel = sgpmInfoList[0].blendModel;
+#endif
+#if !JVET_AJ0112_REGRESSION_SGPM
+                loadStartStates();
 
-              for (int sgpmIdx = 0; sgpmIdx < SGPM_NUM; sgpmIdx++)
-              {
-                int sgpmMode0 = sgpmInfoList[sgpmIdx].sgpmMode0;
-                int sgpmMode1 = sgpmInfoList[sgpmIdx].sgpmMode1;
-                PelBuf src0(m_intraPredBuf[sgpmMode0], tmpArea);
-                PelBuf src1(m_intraPredBuf[sgpmMode1], tmpArea);
+                uint64_t fracModeBits = xFracModeBitsIntra(pu, 0, CHANNEL_TYPE_LUMA);
+#endif
 
+                for (int sgpmIdx = 0; sgpmIdx < SGPM_NUM; sgpmIdx++)
+                {
+                  int sgpmMode0 = sgpmInfoList[sgpmIdx].sgpmMode0;
+                  int sgpmMode1 = sgpmInfoList[sgpmIdx].sgpmMode1;
+                  PelBuf src0(m_intraPredBuf[sgpmMode0], tmpArea);
+                  PelBuf src1(m_intraPredBuf[sgpmMode1], tmpArea);
+#if JVET_AJ0112_REGRESSION_SGPM
+                  cu.sgpmIdx = sgpmIdx;
+                  loadStartStates();
+                  uint64_t fracModeBits = xFracModeBitsIntra(pu, 0, CHANNEL_TYPE_LUMA);
+                  if (sgpmInfoList[sgpmIdx].isRegression)
+                  {
+                    PelUnitBuf pred = PelUnitBuf(pu.chromaFormat, piPred);
+                    PelUnitBuf pred0 = PelUnitBuf(pu.chromaFormat, src0);
+                    PelUnitBuf pred1 = PelUnitBuf(pu.chromaFormat, src1);
+                    m_blendBuf.resize(pu.lwidth() * pu.lheight());
+                    int16_t* blendBuf = m_blendBuf.data();
+                    WeightBuf bufWeight = WeightBuf(blendBuf, pu.lumaSize());
+                    const int geoBlendingLog2WeightBase = 5;
+                    pcInterPred->weightedAffineBlk(pu, bufWeight, geoBlendingLog2WeightBase, sgpmInfoList[sgpmIdx].blendModel);
+                    pcInterPred->weightedBlendBlk(pu, 0, pred, pred0, pred1, bufWeight, geoBlendingLog2WeightBase, false);
+                  }
+                  else
+                  {
+#if JVET_AJ0107_GPM_SHAPE_ADAPT
+                    m_if.m_weightedSgpm(pu, width, height, COMPONENT_Y, g_sgpmSplitDir[sgpmInfoList[sgpmIdx].sgpmSplitDir], piPred, src0, src1);
+#else
+                    m_if.m_weightedSgpm(pu, width, height, COMPONENT_Y, sgpmInfoList[sgpmIdx].sgpmSplitDir, piPred, src0, src1);
+#endif
+                  }
+#else
 #if JVET_AJ0107_GPM_SHAPE_ADAPT
-                m_if.m_weightedSgpm(pu, width, height, COMPONENT_Y, g_sgpmSplitDir[sgpmInfoList[sgpmIdx].sgpmSplitDir], piPred, src0, src1);
+                  m_if.m_weightedSgpm(pu, width, height, COMPONENT_Y, g_sgpmSplitDir[sgpmInfoList[sgpmIdx].sgpmSplitDir], piPred, src0, src1);
 #else
-                m_if.m_weightedSgpm(pu, width, height, COMPONENT_Y, sgpmInfoList[sgpmIdx].sgpmSplitDir, piPred, src0, src1);
+                  m_if.m_weightedSgpm(pu, width, height, COMPONENT_Y, sgpmInfoList[sgpmIdx].sgpmSplitDir, piPred, src0, src1);
+#endif
 #endif
 
-                PelBuf predBuf(m_sgpmPredBuf[sgpmIdx], tmpArea);
-                predBuf.copyFrom(piPred);
+                  PelBuf predBuf(m_sgpmPredBuf[sgpmIdx], tmpArea);
+                  predBuf.copyFrom(piPred);
 
-                Distortion minSadHad = 0;
+                  Distortion minSadHad = 0;
 #if JVET_AD0208_IBC_ADAPT_FOR_CAM_CAPTURED_CONTENTS
-                Distortion sadCost = distParamSad.distFunc(distParamSad);
-                minSadHad += std::min(sadCost * 2, distParamHad.distFunc(distParamHad));
+                  Distortion sadCost = distParamSad.distFunc(distParamSad);
+                  minSadHad += std::min(sadCost * 2, distParamHad.distFunc(distParamHad));
 #else
-                minSadHad += std::min(distParamSad.distFunc(distParamSad) * 2, distParamHad.distFunc(distParamHad));
+                  minSadHad += std::min(distParamSad.distFunc(distParamSad) * 2, distParamHad.distFunc(distParamHad));
 #endif
-                double cost = (double) minSadHad + (double) fracModeBits * sqrtLambdaForFirstPass;
+                  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);
+                  m_bestIntraSADCost = std::min(m_bestIntraSADCost, cost - (double)minSadHad + (double)sadCost);
 #endif
-                updateCandList(ModeInfo(false, false, 0, NOT_INTRA_SUBPARTITIONS, SGPM_IDX,
+                  updateCandList(ModeInfo(false, false, 0, NOT_INTRA_SUBPARTITIONS, SGPM_IDX,
 #if JVET_V0130_INTRA_TMP
-                                        false, //tmpFlag
+                    false, //tmpFlag
 #endif
 #if JVET_AD0086_ENHANCED_INTRA_TMP
-                                        0, false, false,  
+                    0, false, false,
 #if JVET_AG0136_INTRA_TMP_LIC
-                                        false, 0,
+                    false, 0,
 #endif
-                                        0, 0,
+                    0, 0,
 #if JVET_AH0200_INTRA_TMP_BV_REORDER
-                                        -1, 
+                    - 1,
 #endif
 #endif
-                                        true, sgpmInfoList[sgpmIdx].sgpmSplitDir, sgpmInfoList[sgpmIdx].sgpmMode0,
-                                        sgpmInfoList[sgpmIdx].sgpmMode1, sgpmIdx
+                    true, sgpmInfoList[sgpmIdx].sgpmSplitDir, sgpmInfoList[sgpmIdx].sgpmMode0,
+                    sgpmInfoList[sgpmIdx].sgpmMode1, sgpmIdx
 #if JVET_AG0152_SGPM_ITMP_IBC
-                                        , sgpmInfoList[sgpmIdx].sgpmBv0, sgpmInfoList[sgpmIdx].sgpmBv1
+                    , sgpmInfoList[sgpmIdx].sgpmBv0, sgpmInfoList[sgpmIdx].sgpmBv1
 #endif
-),
-                               cost, m_uiSavedRdModeListSGPM, m_dSavedModeCostSGPM, SGPM_NUM);
-                updateCandList(ModeInfo(false, false, 0, NOT_INTRA_SUBPARTITIONS, SGPM_IDX,
+#if JVET_AJ0112_REGRESSION_SGPM
+                    , sgpmInfoList[sgpmIdx].isRegression, sgpmInfoList[sgpmIdx].blendModel
+#endif
+                  ),
+                    cost, m_uiSavedRdModeListSGPM, m_dSavedModeCostSGPM, SGPM_NUM);
+                  updateCandList(ModeInfo(false, false, 0, NOT_INTRA_SUBPARTITIONS, SGPM_IDX,
 #if JVET_V0130_INTRA_TMP
-                                        false, //tmpFlag
+                    false, //tmpFlag
 #endif
 #if JVET_AD0086_ENHANCED_INTRA_TMP
-                                        0, false, false,  
+                    0, false, false,
 #if JVET_AG0136_INTRA_TMP_LIC
-                                        false, 0,
+                    false, 0,
 #endif     
-                                        0, 0,
+                    0, 0,
 #if JVET_AH0200_INTRA_TMP_BV_REORDER
-                                        -1, 
+                    - 1,
 #endif
 #endif
-                                        true, sgpmInfoList[sgpmIdx].sgpmSplitDir, sgpmInfoList[sgpmIdx].sgpmMode0,
-                                        sgpmInfoList[sgpmIdx].sgpmMode1, sgpmIdx
+                    true, sgpmInfoList[sgpmIdx].sgpmSplitDir, sgpmInfoList[sgpmIdx].sgpmMode0,
+                    sgpmInfoList[sgpmIdx].sgpmMode1, sgpmIdx
 #if JVET_AG0152_SGPM_ITMP_IBC
-                                        , sgpmInfoList[sgpmIdx].sgpmBv0, sgpmInfoList[sgpmIdx].sgpmBv1
+                    , sgpmInfoList[sgpmIdx].sgpmBv0, sgpmInfoList[sgpmIdx].sgpmBv1
 #endif
-),
-                               double(minSadHad), m_uiSavedHadModeListSGPM, m_dSavedHadListSGPM, SGPM_NUM);
-              }
-
-              cu.sgpm = false;
-            }
-
-            int updateNum = std::min<int>( (numModesForFullRD + 1) / 2, (int)m_uiSavedRdModeListSGPM.size() );
+#if JVET_AJ0112_REGRESSION_SGPM
+                    , sgpmInfoList[sgpmIdx].isRegression, sgpmInfoList[sgpmIdx].blendModel
+#endif
+                  ),
+                    double(minSadHad), m_uiSavedHadModeListSGPM, m_dSavedHadListSGPM, SGPM_NUM);
+                }
 
-            for (auto listIdx = 0; listIdx < updateNum; listIdx++)
-            {
-              updateCandList(m_uiSavedRdModeListSGPM[listIdx], m_dSavedModeCostSGPM[listIdx], uiRdModeList,
-                             candCostList, numModesForFullRD);
-              updateCandList(m_uiSavedHadModeListSGPM[listIdx], m_dSavedHadListSGPM[listIdx], uiHadModeList,
-                             candHadList, numHadCand);
+                cu.sgpm = false;
+              }
+#if JVET_AJ0112_REGRESSION_SGPM
+              numModesForFullRD++;
+              int updateNum = (int)m_uiSavedRdModeListSGPM.size();
+#else
+              int updateNum = std::min<int>((numModesForFullRD + 1) / 2, (int)m_uiSavedRdModeListSGPM.size());
+#endif
+              for (auto listIdx = 0; listIdx < updateNum; listIdx++)
+              {
+                updateCandList(m_uiSavedRdModeListSGPM[listIdx], m_dSavedModeCostSGPM[listIdx], uiRdModeList,
+                  candCostList, numModesForFullRD);
+                updateCandList(m_uiSavedHadModeListSGPM[listIdx], m_dSavedHadListSGPM[listIdx], uiHadModeList,
+                  candHadList, numHadCand);
+              }
             }
-          }
 #endif
 #if JVET_AG0058_EIP
-          if (testEip)
-          {
-            if (eipSaveFlag)
+            if (testEip)
             {
-              m_uiSavedRdModeListEip.clear();
-              m_uiSavedHadModeListEip.clear();
-              m_dSavedModeCostEip.clear();
-              m_dSavedHadListEip.clear();
+              if (eipSaveFlag)
+              {
+                m_uiSavedRdModeListEip.clear();
+                m_uiSavedHadModeListEip.clear();
+                m_dSavedModeCostEip.clear();
+                m_dSavedHadListEip.clear();
 #if JVET_AJ0082_MM_EIP
-              m_encBestEipCost = MAX_DOUBLE;
-              cu.eipMmFlag = false;
-              m_numSigEip = 0;
+                m_encBestEipCost = MAX_DOUBLE;
+                cu.eipMmFlag = false;
+                m_numSigEip = 0;
 #endif
-              cu.tmpFlag = false;
-              cu.mipFlag = false;
-              cu.sgpm = false;
-              pu.multiRefIdx = 0;
+                cu.tmpFlag = false;
+                cu.mipFlag = false;
+                cu.sgpm = false;
+                pu.multiRefIdx = 0;
 
-              cu.eipFlag = true;
-              cu.eipMerge = false;
-              initEipParams(pu, COMPONENT_Y);
-              static_vector<EipModelCandidate,NUM_DERIVED_EIP> eipModelCandList;
-              static_vector<EipModelCandidate, MAX_MERGE_EIP> eipMergeCandList;
-              getCurEipCands(pu, eipModelCandList);          
-              getNeiEipCands(pu, eipMergeCandList);          
-              reorderEipCands(pu, eipMergeCandList);
-              const int numRdEIP = std::max(NUM_EIP_MERGE_SIGNAL + NUM_DERIVED_EIP, (numModesForFullRD + 1) / 2);
-              for(int mergeFlag = 0; mergeFlag < 2; mergeFlag++)
-              {
-                cu.eipMerge = bool(mergeFlag);
-                for(int i = 0; i < (cu.eipMerge ? eipMergeCandList.size() : eipModelCandList.size()); i++)
+                cu.eipFlag = true;
+                cu.eipMerge = false;
+                initEipParams(pu, COMPONENT_Y);
+                static_vector<EipModelCandidate, NUM_DERIVED_EIP> eipModelCandList;
+                static_vector<EipModelCandidate, MAX_MERGE_EIP> eipMergeCandList;
+                getCurEipCands(pu, eipModelCandList);
+                getNeiEipCands(pu, eipMergeCandList);
+                reorderEipCands(pu, eipMergeCandList);
+                const int numRdEIP = std::max(NUM_EIP_MERGE_SIGNAL + NUM_DERIVED_EIP, (numModesForFullRD + 1) / 2);
+                for (int mergeFlag = 0; mergeFlag < 2; mergeFlag++)
                 {
-                  pu.intraDir[0] = i;
-#if JVET_AJ0082_MM_EIP
-                  cu.eipMmFlag = (!cu.eipMerge) && (i >= m_numSigEip);
-                  if(cu.eipMmFlag)
+                  cu.eipMerge = bool(mergeFlag);
+                  for (int i = 0; i < (cu.eipMerge ? eipMergeCandList.size() : eipModelCandList.size()); i++)
                   {
-                    pu.intraDir[0] -= m_numSigEip;
-                  }
+                    pu.intraDir[0] = i;
+#if JVET_AJ0082_MM_EIP
+                    cu.eipMmFlag = (!cu.eipMerge) && (i >= m_numSigEip);
+                    if (cu.eipMmFlag)
+                    {
+                      pu.intraDir[0] -= m_numSigEip;
+                    }
 #endif
-                  cu.eipModel = cu.eipMerge ? eipMergeCandList[i] : eipModelCandList[i];
-                  if(cu.eipMerge)
-                  {
-                    m_eipMergeModel[i] = cu.eipModel;
+                    cu.eipModel = cu.eipMerge ? eipMergeCandList[i] : eipModelCandList[i];
+                    if (cu.eipMerge)
+                    {
+                      m_eipMergeModel[i] = cu.eipModel;
 #if JVET_AJ0082_MM_EIP
-                    m_eipMergeModel[i].eipDimdMode = -1;
+                      m_eipMergeModel[i].eipDimdMode = -1;
 #endif
-                  }
-                  else
-                  {
-                    m_eipModel[i] = cu.eipModel;
-                  }
-                  eipPred(pu, piPred);
+                    }
+                    else
+                    {
+                      m_eipModel[i] = cu.eipModel;
+                    }
+                    eipPred(pu, piPred);
 #if JVET_AD0208_IBC_ADAPT_FOR_CAM_CAPTURED_CONTENTS
-                  Distortion sadCost = distParamSad.distFunc(distParamSad);
-                  Distortion minSadHad = std::min(sadCost * 2, distParamHad.distFunc(distParamHad));
+                    Distortion sadCost = distParamSad.distFunc(distParamSad);
+                    Distortion minSadHad = std::min(sadCost * 2, distParamHad.distFunc(distParamHad));
 #else
-                  Distortion minSadHad = std::min(distParamSad.distFunc(distParamSad) * 2, distParamHad.distFunc(distParamHad));
+                    Distortion minSadHad = std::min(distParamSad.distFunc(distParamSad) * 2, distParamHad.distFunc(distParamHad));
 #endif
-                  loadStartStates();
+                    loadStartStates();
 #if JVET_AJ0082_MM_EIP
-                  uint64_t fracModeBits = xFracModeBitsIntra(pu, pu.intraDir[0], CHANNEL_TYPE_LUMA);
+                    uint64_t fracModeBits = xFracModeBitsIntra(pu, pu.intraDir[0], CHANNEL_TYPE_LUMA);
 #else
-                  uint64_t fracModeBits = xFracModeBitsIntra(pu, i, CHANNEL_TYPE_LUMA);
+                    uint64_t fracModeBits = xFracModeBitsIntra(pu, i, CHANNEL_TYPE_LUMA);
 #endif
-                  double cost = double(minSadHad) + double(fracModeBits) * sqrtLambdaForFirstPass;
+                    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);
+                    m_bestIntraSADCost = std::min(m_bestIntraSADCost, cost - (double)minSadHad + (double)sadCost);
 #endif
 
-                  ModeInfo modeInfo(false, cu.eipMerge, 0, NOT_INTRA_SUBPARTITIONS, EIP_IDX + i);
-                  PelBuf eipSaveBuf(cu.eipMerge ? m_eipMergePredBuf[i]: m_eipPredBuf[i], pu.Y());
-                  eipSaveBuf.copyFrom(piPred);
+                    ModeInfo modeInfo(false, cu.eipMerge, 0, NOT_INTRA_SUBPARTITIONS, EIP_IDX + i);
+                    PelBuf eipSaveBuf(cu.eipMerge ? m_eipMergePredBuf[i] : m_eipPredBuf[i], pu.Y());
+                    eipSaveBuf.copyFrom(piPred);
 #if JVET_AJ0082_MM_EIP
-                  if(!cu.eipModel.bMm && cost < m_encBestEipCost)
-                  {
-                    m_encBestEipCost = cost;
-                  }
+                    if (!cu.eipModel.bMm && cost < m_encBestEipCost)
+                    {
+                      m_encBestEipCost = cost;
+                    }
 #endif
-                  updateCandList(modeInfo, cost, m_uiSavedRdModeListEip, m_dSavedModeCostEip, numRdEIP);
-                  updateCandList(modeInfo, double(minSadHad * 0.8), m_uiSavedHadModeListEip, m_dSavedHadListEip, numRdEIP);
+                    updateCandList(modeInfo, cost, m_uiSavedRdModeListEip, m_dSavedModeCostEip, numRdEIP);
+                    updateCandList(modeInfo, double(minSadHad * 0.8), m_uiSavedHadModeListEip, m_dSavedHadListEip, numRdEIP);
+                  }
                 }
-              }
 #if !JVET_AJ0082_MM_EIP
-              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") 
-                const auto modeIdx = modeInfo.modeId - EIP_IDX;
-                if(modeInfo.mipTrFlg)
+                for (const auto& modeInfo : m_uiSavedRdModeListEip)
                 {
-                  PelBuf eipSaveBuf(m_eipMergePredBuf[modeIdx], pu.Y());
+                  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")
+                    const auto modeIdx = modeInfo.modeId - EIP_IDX;
+                  if (modeInfo.mipTrFlg)
+                  {
+                    PelBuf eipSaveBuf(m_eipMergePredBuf[modeIdx], pu.Y());
 #if JVET_AI0050_INTER_MTSS
-                  int secondDimdIntraDir = 0;
+                    int secondDimdIntraDir = 0;
 #endif
-                  m_eipMergeModel[modeIdx].eipDimdMode = deriveIpmForTransform(eipSaveBuf, cu
+                    m_eipMergeModel[modeIdx].eipDimdMode = deriveIpmForTransform(eipSaveBuf, cu
 #if JVET_AI0050_INTER_MTSS
-                    , secondDimdIntraDir
+                      , secondDimdIntraDir
 #endif
-                  );
+                    );
 #if JVET_AI0050_INTER_MTSS
-                  cu.dimdDerivedIntraDir2nd = secondDimdIntraDir;
+                    cu.dimdDerivedIntraDir2nd = secondDimdIntraDir;
 #endif
-                  CHECK(modeIdx >= NUM_EIP_MERGE_SIGNAL, "modeIdx >= NUM_EIP_MERGE_SIGNAL");
-                }
-                else
-                {
-                  PelBuf eipSaveBuf(m_eipPredBuf[modeIdx], pu.Y());
+                    CHECK(modeIdx >= NUM_EIP_MERGE_SIGNAL, "modeIdx >= NUM_EIP_MERGE_SIGNAL");
+                  }
+                  else
+                  {
+                    PelBuf eipSaveBuf(m_eipPredBuf[modeIdx], pu.Y());
 #if JVET_AI0050_INTER_MTSS
-                  int secondDimdIntraDir = 0;
+                    int secondDimdIntraDir = 0;
 #endif
-                  m_eipModel[modeIdx].eipDimdMode = deriveIpmForTransform(eipSaveBuf, cu
+                    m_eipModel[modeIdx].eipDimdMode = deriveIpmForTransform(eipSaveBuf, cu
 #if JVET_AI0050_INTER_MTSS
-                    , secondDimdIntraDir
+                      , secondDimdIntraDir
 #endif
-                  );
+                    );
 #if JVET_AI0050_INTER_MTSS
-                  cu.dimdDerivedIntraDir2nd = secondDimdIntraDir;
+                    cu.dimdDerivedIntraDir2nd = secondDimdIntraDir;
 #endif
-                  CHECK(modeIdx >= NUM_DERIVED_EIP, "modeIdx >= NUM_DERIVED_EIP");
-                }
-              }
+                    CHECK(modeIdx >= NUM_DERIVED_EIP, "modeIdx >= NUM_DERIVED_EIP");
+                  }
+                  }
 #endif
 #if JVET_AJ0082_MM_EIP
-              if (m_dSavedModeCostEip.size() > 0)
-              {
-                isEipModeTested = true;
-                eipBestSatdCost = m_dSavedModeCostEip[0];
-              }
+                if (m_dSavedModeCostEip.size() > 0)
+                {
+                  isEipModeTested = true;
+                  eipBestSatdCost = m_dSavedModeCostEip[0];
+                }
 #endif
-              cu.eipFlag = false;
-              cu.eipMerge = false;
+                cu.eipFlag = false;
+                cu.eipMerge = false;
 #if JVET_AJ0082_MM_EIP
-              cu.eipMmFlag = false;
+                cu.eipMmFlag = false;
 #endif
-            }
-            for (auto i = 0; i < m_uiSavedRdModeListEip.size(); i++)
-            {
-              updateCandList(m_uiSavedRdModeListEip[i], m_dSavedModeCostEip[i], uiRdModeList, candCostList, numModesForFullRD);
-              updateCandList(m_uiSavedHadModeListEip[i], m_dSavedHadListEip[i], uiHadModeList, candHadList, numHadCand);
-            }
-            int numEip = 0;
-            for(int i = 0; i < numModesForFullRD - 1; i++)
-            {
-              bool isEip = (uiRdModeList[i].modeId >= EIP_IDX && uiRdModeList[i].modeId < EIP_IDX + std::max(NUM_DERIVED_EIP, MAX_MERGE_EIP));
-              numEip += (isEip ? 1 : 0);
-            }
-            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)) ;
+              }
+              for (auto i = 0; i < m_uiSavedRdModeListEip.size(); i++)
+              {
+                updateCandList(m_uiSavedRdModeListEip[i], m_dSavedModeCostEip[i], uiRdModeList, candCostList, numModesForFullRD);
+                updateCandList(m_uiSavedHadModeListEip[i], m_dSavedHadListEip[i], uiHadModeList, candHadList, numHadCand);
+              }
+              int numEip = 0;
+              for (int i = 0; i < numModesForFullRD - 1; i++)
+              {
+                bool isEip = (uiRdModeList[i].modeId >= EIP_IDX && uiRdModeList[i].modeId < EIP_IDX + std::max(NUM_DERIVED_EIP, MAX_MERGE_EIP));
+                numEip += (isEip ? 1 : 0);
+              }
+              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));
 #if JVET_AJ0082_MM_EIP
-            bool reduceRD =  m_dSavedModeCostEip.size() ? (pu.Y().area() < 256) && (m_encBestEipCost < candCostList[numModesForFullRD - 1]) && (lastModeIsEip || (numNonEip > 1)) : false;
-            if( reduceRD && m_pcEncCfg->getIntraPeriod() == 1)
+              bool reduceRD = m_dSavedModeCostEip.size() ? (pu.Y().area() < 256) && (m_encBestEipCost < candCostList[numModesForFullRD - 1]) && (lastModeIsEip || (numNonEip > 1)) : false;
+              if (reduceRD && m_pcEncCfg->getIntraPeriod() == 1)
 #else
-            bool reduceRD =  m_dSavedModeCostEip.size() ? (pu.Y().area() < 256) && (m_dSavedModeCostEip[0] < candCostList[numModesForFullRD - 1]) && (lastModeIsEip || (numNonEip > 1)) : false;
-            if( reduceRD && pu.cs->slice->isIntra())
+              bool reduceRD = m_dSavedModeCostEip.size() ? (pu.Y().area() < 256) && (m_dSavedModeCostEip[0] < candCostList[numModesForFullRD - 1]) && (lastModeIsEip || (numNonEip > 1)) : false;
+              if (reduceRD && pu.cs->slice->isIntra())
 #endif 
-            {
-              uiRdModeList.pop_back();
-              candCostList.pop_back();
-              numModesForFullRD = int(uiRdModeList.size());
+              {
+                uiRdModeList.pop_back();
+                candCostList.pop_back();
+                numModesForFullRD = int(uiRdModeList.size());
+              }
             }
-          }
 #endif
 #if JVET_AE0169_BIPREDICTIVE_IBC
-          m_bestIntraSADHADCost = candCostList[numModesForFullRD - 1];
+            m_bestIntraSADHADCost = candCostList[numModesForFullRD - 1];
 #if JVET_AH0200_INTRA_TMP_BV_REORDER
-          if(isTmpModeTestd)
-          {
-#if JVET_AI0136_ADAPTIVE_DUAL_TREE
-            if(relatedCU && !relatedCU->skipFracTmp && (tmpBestSatdCost > m_bestIntraSADHADCost * TMP_ENC_REFINE_THRESHOLD))
+            if (isTmpModeTestd)
             {
-              relatedCU->skipFracTmp = true;
-            }
+#if JVET_AI0136_ADAPTIVE_DUAL_TREE
+              if (relatedCU && !relatedCU->skipFracTmp && (tmpBestSatdCost > m_bestIntraSADHADCost * TMP_ENC_REFINE_THRESHOLD))
+              {
+                relatedCU->skipFracTmp = true;
+              }
 #else
-            if(!relatedCU.skipFracTmp && (tmpBestSatdCost > m_bestIntraSADHADCost * TMP_ENC_REFINE_THRESHOLD))
-            {
-              relatedCU.skipFracTmp = true;
-            }
+              if (!relatedCU.skipFracTmp && (tmpBestSatdCost > m_bestIntraSADHADCost * TMP_ENC_REFINE_THRESHOLD))
+              {
+                relatedCU.skipFracTmp = true;
+              }
 #endif
           }
 #endif
 #if JVET_AJ0082_MM_EIP
-          if(isEipModeTested && m_pcEncCfg->getIntraPeriod() == 1)
-          {
-#if JVET_AI0136_ADAPTIVE_DUAL_TREE
-            if(relatedCU && !relatedCU->skipEip && (eipBestSatdCost > m_bestIntraSADHADCost * ENC_EIP_SAD_CHK_RATE))
+            if (isEipModeTested && m_pcEncCfg->getIntraPeriod() == 1)
             {
-              relatedCU->skipEip = true;
-            }
+#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;
-            }
+              if (!relatedCU.skipEip && (eipBestSatdCost > m_bestIntraSADHADCost * ENC_EIP_SAD_CHK_RATE))
+              {
+                relatedCU.skipEip = true;
+              }
 #endif
-          }
+            }
 #endif
 #endif
-          if (m_pcEncCfg->getFastUDIUseMPMEnabled())
-          {
+            if (m_pcEncCfg->getFastUDIUseMPMEnabled())
+            {
 
 #if SECONDARY_MPM
-            auto uiPreds = m_intraMPM;
+              auto uiPreds = m_intraMPM;
 #else
-            const int numMPMs = NUM_MOST_PROBABLE_MODES;
-            unsigned  uiPreds[numMPMs];
+              const int numMPMs = NUM_MOST_PROBABLE_MODES;
+              unsigned  uiPreds[numMPMs];
 #endif
 
-            pu.multiRefIdx = 0;
+              pu.multiRefIdx = 0;
 #if JVET_AB0157_TMRL
-            cu.tmrlFlag = false;;
+              cu.tmrlFlag = false;;
 #endif
 #if SECONDARY_MPM
-            int numCand = m_mpmListSize;
-            numCand = (numCand > 2) ? 2 : numCand;
+              int numCand = m_mpmListSize;
+              numCand = (numCand > 2) ? 2 : numCand;
 #else
-            const int numCand = PU::getIntraMPMs(pu, uiPreds);
+              const int numCand = PU::getIntraMPMs(pu, uiPreds);
 #endif
 
-            for (int j = 0; j < numCand; j++)
-            {
-              bool     mostProbableModeIncluded = false;
-              ModeInfo mostProbableMode( false, false, 0, NOT_INTRA_SUBPARTITIONS, uiPreds[j] );
-
-              for (int i = 0; i < numModesForFullRD; i++)
-              {
-                mostProbableModeIncluded |= (mostProbableMode == uiRdModeList[i]);
-              }
-              if (!mostProbableModeIncluded)
-              {
-                numModesForFullRD++;
-                uiRdModeList.push_back(mostProbableMode);
-                candCostList.push_back(0);
-              }
-            }
-            if (saveDataForISP)
-            {
-              // we add the MPMs to the list that contains only regular intra modes
               for (int j = 0; j < numCand; j++)
               {
                 bool     mostProbableModeIncluded = false;
                 ModeInfo mostProbableMode(false, false, 0, NOT_INTRA_SUBPARTITIONS, uiPreds[j]);
 
-                for (int i = 0; i < m_ispCandListHor.size(); i++)
+                for (int i = 0; i < numModesForFullRD; i++)
                 {
-                  mostProbableModeIncluded |= (mostProbableMode == m_ispCandListHor[i]);
+                  mostProbableModeIncluded |= (mostProbableMode == uiRdModeList[i]);
                 }
                 if (!mostProbableModeIncluded)
                 {
-                  m_ispCandListHor.push_back(mostProbableMode);
+                  numModesForFullRD++;
+                  uiRdModeList.push_back(mostProbableMode);
+                  candCostList.push_back(0);
+                }
+              }
+              if (saveDataForISP)
+              {
+                // we add the MPMs to the list that contains only regular intra modes
+                for (int j = 0; j < numCand; j++)
+                {
+                  bool     mostProbableModeIncluded = false;
+                  ModeInfo mostProbableMode(false, false, 0, NOT_INTRA_SUBPARTITIONS, uiPreds[j]);
+
+                  for (int i = 0; i < m_ispCandListHor.size(); i++)
+                  {
+                    mostProbableModeIncluded |= (mostProbableMode == m_ispCandListHor[i]);
+                  }
+                  if (!mostProbableModeIncluded)
+                  {
+                    m_ispCandListHor.push_back(mostProbableMode);
+                  }
                 }
               }
             }
+#if JVET_AJ0112_REGRESSION_SGPM
           }
+          if (m_pcEncCfg->getUseFastLFNST() && LFNSTLoadFlag && mtsUsageFlag == 1)
+          {
+            if (m_bestModeCostValid[0])
+            {
+              numModesForFullRD = 0;
+              uiRdModeList.clear();
+              std::vector<std::pair<ModeInfo, double>> modeInfoWithDCT2Cost(m_savedNumRdModes[0]);
+              for (int i = 0; i < m_savedNumRdModes[0]; i++)
+              {
+                modeInfoWithDCT2Cost[i] = { m_savedRdModeList[0][i], m_modeCostStore[0][i] };
+              }
+              std::stable_sort(modeInfoWithDCT2Cost.begin(), modeInfoWithDCT2Cost.end(), [](const std::pair<ModeInfo, double> & l, const std::pair<ModeInfo, double> & r) {return l.second < r.second; });
+
+              // **Reorder the modes** and skip checking the modes with much larger R-D cost than the best mode
+              for (int i = 0; i < m_savedNumRdModes[0]; i++)
+              {
+                if (modeInfoWithDCT2Cost[i].second <= 1.3 * modeInfoWithDCT2Cost[0].second)
+                {
+                  uiRdModeList.push_back(modeInfoWithDCT2Cost[i].first);
+                  numModesForFullRD++;
+                }
+              }
+            }
+            else
+            {
+              // Restore the modes to be checked with RD
+              numModesForFullRD = m_savedNumRdModes[0];
+              uiRdModeList.resize(numModesForFullRD);
+              std::copy_n(m_savedRdModeList[0], m_savedNumRdModes[0], uiRdModeList.begin());
+              candCostList.resize(numModesForFullRD);
+            }
+          }
+#endif
         }
         else
         {
@@ -3905,6 +3992,13 @@ bool IntraSearch::estIntraPredLumaQT(CodingUnit &cu, Partitioner &partitioner, c
       cu.sgpm = uiOrgMode.sgpmFlag;
       if (cu.sgpm)
       {
+#if JVET_AJ0112_REGRESSION_SGPM
+        if (m_skipSgpmLfnstMtsPass)
+        {
+          CHECK(!cu.lfnstIdx && !cu.mtsFlag, "invalid logic");
+          continue;
+        }
+#endif
         uiOrgMode.modeId = uiOrgMode.sgpmMode0;
         cu.sgpmSplitDir  = uiOrgMode.sgpmSplitDir;
         cu.sgpmMode0     = uiOrgMode.sgpmMode0;
@@ -3912,6 +4006,9 @@ bool IntraSearch::estIntraPredLumaQT(CodingUnit &cu, Partitioner &partitioner, c
 #if JVET_AG0152_SGPM_ITMP_IBC
         cu.sgpmBv0 = uiOrgMode.sgpmBv0;
         cu.sgpmBv1 = uiOrgMode.sgpmBv1;
+#endif
+#if JVET_AJ0112_REGRESSION_SGPM
+        cu.blendModel = uiOrgMode.sgpmBlendModel;
 #endif
         cu.sgpmIdx       = uiOrgMode.sgpmIdx;
 #if JVET_AG0152_SGPM_ITMP_IBC
@@ -4318,6 +4415,15 @@ bool IntraSearch::estIntraPredLumaQT(CodingUnit &cu, Partitioner &partitioner, c
 #endif
         }
 #endif
+#if JVET_AJ0112_REGRESSION_SGPM
+        if (setSkipSgpmControl && cu.sgpm)
+        {
+          if (csTemp->cost < sgpmCost)
+          {
+            sgpmCost = csTemp->cost;
+          }
+        }
+#endif
 #if JVET_AJ0061_TIMD_MERGE
         if (setSkipTimdMrgControl && cu.timdMrg)
         {
@@ -4398,6 +4504,13 @@ bool IntraSearch::estIntraPredLumaQT(CodingUnit &cu, Partitioner &partitioner, c
             bestMipDimd = curCu->mipDimdMode;
           }
 #endif
+#if JVET_AJ0112_REGRESSION_SGPM
+          if (cu.sgpm && PU::isRegressionSgpm(*cu.firstPU))
+          {
+            CodingUnit* curCu = csBest->getCU(partitioner.currArea().lumaPos(), partitioner.chType);
+            bestSgpmDimd = curCu->sgpmDimdMode;
+          }
+#endif
 
 #if JVET_AH0103_LOW_DELAY_LFNST_NSPT
           if( spsIntraLfnstEnabled && mtsUsageFlag == 1 && !cu.ispMode )
@@ -4480,6 +4593,15 @@ bool IntraSearch::estIntraPredLumaQT(CodingUnit &cu, Partitioner &partitioner, c
       }
     }
 #endif
+#if JVET_AJ0112_REGRESSION_SGPM
+    if (setSkipSgpmControl)
+    {
+      if (regAngCost != MAX_DOUBLE && sgpmCost != MAX_DOUBLE && regAngCost * 1.5 < sgpmCost)
+      {
+        m_skipSgpmLfnstMtsPass = true;
+      }
+    }
+#endif
 #if JVET_AJ0061_TIMD_MERGE
     for (int i = 0; i < NUM_TIMD_MERGE_MODES && setSkipTimdMrgControl; i++)
     {
@@ -4580,6 +4702,9 @@ bool IntraSearch::estIntraPredLumaQT(CodingUnit &cu, Partitioner &partitioner, c
         pu.intraDir[ CHANNEL_TYPE_LUMA ] = cu.obicMode[0];
         cu.dimd = true;
       }
+#endif
+#if JVET_AJ0112_REGRESSION_SGPM
+      cu.sgpmDimdMode = bestSgpmDimd;
 #endif
       cu.bdpcmMode = bestBDPCMMode;
 #if JVET_W0123_TIMD_FUSION
@@ -4623,6 +4748,9 @@ bool IntraSearch::estIntraPredLumaQT(CodingUnit &cu, Partitioner &partitioner, c
 #if JVET_AG0152_SGPM_ITMP_IBC
         cu.sgpmBv0                       = uiBestPUMode.sgpmBv0;
         cu.sgpmBv1                      = uiBestPUMode.sgpmBv1;
+#endif
+#if JVET_AJ0112_REGRESSION_SGPM
+        cu.blendModel                   = uiBestPUMode.sgpmBlendModel;
 #endif
       }
 #endif
@@ -10415,6 +10543,19 @@ void IntraSearch::xSelectAMTForFullRD(TransformUnit &tu
       CompArea tmpArea(COMPONENT_Y, area.chromaFormat, Position(0, 0), area.size());
       PelBuf predBuf(m_sgpmPredBuf[pu.cu->sgpmIdx], tmpArea);
       piPred.copyFrom(predBuf);
+#if JVET_AJ0112_REGRESSION_SGPM
+      if (PU::isRegressionSgpm(pu))
+      {
+#if JVET_AI0050_INTER_MTSS
+        int secondDimdIntraDir = 0;
+#endif
+        deriveIpmForTransform(piPred, *pu.cu
+#if JVET_AI0050_INTER_MTSS
+          , secondDimdIntraDir
+#endif
+        );
+      }
+#endif
     }
     else
 #endif
@@ -10810,6 +10951,19 @@ void IntraSearch::xIntraCodingTUBlock(TransformUnit &tu, const ComponentID &comp
               CompArea tmpArea(COMPONENT_Y, area.chromaFormat, Position(0, 0), area.size());
               PelBuf   predBuf(m_sgpmPredBuf[pu.cu->sgpmIdx], tmpArea);
               piPred.copyFrom(predBuf);
+#if JVET_AJ0112_REGRESSION_SGPM
+              if (PU::isRegressionSgpm(pu))
+              {
+#if JVET_AI0050_INTER_MTSS
+                int secondDimdIntraDir = 0;
+#endif
+                deriveIpmForTransform(piPred, *pu.cu
+#if JVET_AI0050_INTER_MTSS
+                  , secondDimdIntraDir
+#endif
+                );
+              }
+#endif
             }
             else
 #endif
diff --git a/source/Lib/EncoderLib/IntraSearch.h b/source/Lib/EncoderLib/IntraSearch.h
index 48a7ba27a1e591d34b6afdb69eab3f55d3241c41..081574c43ee42f892c47b04dcbe3388518836d93 100644
--- a/source/Lib/EncoderLib/IntraSearch.h
+++ b/source/Lib/EncoderLib/IntraSearch.h
@@ -253,6 +253,10 @@ private:
     Mv       sgpmBv0;
     Mv       sgpmBv1;
 #endif
+#if JVET_AJ0112_REGRESSION_SGPM
+    bool sgpmIsRegression;
+    AffineBlendingModel sgpmBlendModel;
+#endif
 #endif
 #if JVET_AB0155_SGPM
 #if JVET_V0130_INTRA_TMP
@@ -271,6 +275,10 @@ private:
 #if JVET_AG0152_SGPM_ITMP_IBC
   , sgpmBv0(0, 0), sgpmBv1(0, 0)
 #endif
+#if JVET_AJ0112_REGRESSION_SGPM
+  , sgpmIsRegression(false)
+  , sgpmBlendModel(AffineBlendingModel(5,1,31))
+#endif
 {}
     ModeInfo(const bool mipf, const bool miptf, const int mrid, const uint8_t ispm, const uint32_t mode,
              const bool tmpf = 0
@@ -288,6 +296,10 @@ private:
 #if JVET_AG0152_SGPM_ITMP_IBC
     , const Mv sbv0 = Mv(0, 0), const Mv sbv1 = Mv(0, 0)
 #endif
+#if JVET_AJ0112_REGRESSION_SGPM
+      , const bool sir = false
+      , const AffineBlendingModel sbm = AffineBlendingModel(5, 1, 31)
+#endif
 )
 #else
     ModeInfo() : mipFlg(false), mipTrFlg(false), mRefId(0), ispMod(NOT_INTRA_SUBPARTITIONS), modeId(0), sgpmFlag(0), sgpmSplitDir(0), sgpmMode0(0), sgpmMode1(0), sgpmIdx(0){}
@@ -320,6 +332,10 @@ private:
 #if JVET_AG0152_SGPM_ITMP_IBC
       , sgpmBv0(sbv0)
       , sgpmBv1(sbv1)
+#endif
+#if JVET_AJ0112_REGRESSION_SGPM
+      , sgpmIsRegression(sir)
+      , sgpmBlendModel(sbm)
 #endif
     {
     }
@@ -355,6 +371,10 @@ private:
 #if JVET_AG0152_SGPM_ITMP_IBC
       sgpmBv0 = other.sgpmBv0;
       sgpmBv1 = other.sgpmBv1;
+#endif
+#if JVET_AJ0112_REGRESSION_SGPM
+      sgpmIsRegression = other.sgpmIsRegression;
+      sgpmBlendModel = other.sgpmBlendModel;
 #endif
       return *this;
     }
@@ -738,6 +758,9 @@ public:
 #if INTRA_TRANS_ENC_OPT
   bool            m_skipTimdLfnstMtsPass;
 #endif
+#if JVET_AJ0112_REGRESSION_SGPM
+  bool            m_skipSgpmLfnstMtsPass;
+#endif
 #if JVET_AJ0061_TIMD_MERGE
   bool            m_skipTimdMrgLfnstMtsPass;
   bool            m_skipObicMode;