diff --git a/source/Lib/CommonLib/CommonDef.h b/source/Lib/CommonLib/CommonDef.h
index 7fb8e912451565566a46ba98e4ebc1a00826bef4..43cb9d353839ba3f377b8f4cd91b1cbaa374cc7f 100644
--- a/source/Lib/CommonLib/CommonDef.h
+++ b/source/Lib/CommonLib/CommonDef.h
@@ -816,6 +816,20 @@ static const int CCCM_DECIM_BITS          = 22;
 static const int CCCM_DECIM_ROUND         = ( 1 << (CCCM_DECIM_BITS - 1 ) );
 #endif
 
+#if JVET_AA0126_GLM
+#if JVET_AA0057_CCCM
+#define NUM_GLM_WEIGHT                                              0
+static const int NUM_GLM_PATTERN =                                  4;
+static const int NUM_GLM_PATTERN_BITS =                             2;
+static const int NUM_GLM_IDC =                                      5;
+#else
+#define NUM_GLM_WEIGHT                                              2
+static const int NUM_GLM_PATTERN =                                 16;
+static const int NUM_GLM_PATTERN_BITS =                             4;
+static const int NUM_GLM_IDC =                                     33;
+#endif
+#endif
+
 #if JVET_Y0152_TT_ENC_SPEEDUP
 static constexpr int   FAST_METHOD_TT_ENC_SPEEDUP = 0x0001;  ///< Embedding flag, which, if false, de-activates all the following ABT_ENC_SPEEDUP_* modes
 static constexpr int FAST_METHOD_HOR_XOR_VER = 0x0002;
diff --git a/source/Lib/CommonLib/Contexts.cpp b/source/Lib/CommonLib/Contexts.cpp
index 705198c0beea465dadf9cad4e114382a6930fa63..544e8124335118a113837b7d26b6b95e4ad93fef 100644
--- a/source/Lib/CommonLib/Contexts.cpp
+++ b/source/Lib/CommonLib/Contexts.cpp
@@ -2832,6 +2832,23 @@ const CtxSet ContextSetCfg::CclmDeltaFlags = ContextSetCfg::addCtxSet
 });
 #endif
 
+#if JVET_AA0126_GLM
+const CtxSet ContextSetCfg::GlmFlags = ContextSetCfg::addCtxSet
+({
+  {  CNU, CNU, CNU, CNU, CNU, },
+  {  CNU, CNU, CNU, CNU, CNU, },
+  {  CNU, CNU, CNU, CNU, CNU, },
+  {  DWS, DWS, DWS, DWS, DWS, },
+  {  DWS, DWS, DWS, DWS, DWS, },
+  {  DWS, DWS, DWS, DWS, DWS, },
+  {  DWE, DWE, DWE, DWE, DWE, },
+  {  DWE, DWE, DWE, DWE, DWE, },
+  {  DWE, DWE, DWE, DWE, DWE, },
+  {  DWO, DWO, DWO, DWO, DWO, },
+  {  DWO, DWO, DWO, DWO, DWO, },
+});
+#endif
+
 #if JVET_AA0057_CCCM
 const CtxSet ContextSetCfg::CccmFlag = ContextSetCfg::addCtxSet
 ({
@@ -4352,6 +4369,18 @@ const CtxSet ContextSetCfg::CclmDeltaFlags = ContextSetCfg::addCtxSet
 });
 #endif
 
+#if JVET_AA0126_GLM
+const CtxSet ContextSetCfg::GlmFlags = ContextSetCfg::addCtxSet
+({
+  {  CNU, CNU, CNU, CNU, CNU, },
+  {  CNU, CNU, CNU, CNU, CNU, },
+  {  CNU, CNU, CNU, CNU, CNU, },
+  {  DWS, DWS, DWS, DWS, DWS, },
+  {  DWS, DWS, DWS, DWS, DWS, },
+  {  DWS, DWS, DWS, DWS, DWS, },
+});
+#endif
+
 #if JVET_AA0057_CCCM
 const CtxSet ContextSetCfg::CccmFlag = ContextSetCfg::addCtxSet
 ({
@@ -5435,6 +5464,16 @@ const CtxSet ContextSetCfg::CclmDeltaFlags = ContextSetCfg::addCtxSet
 });
 #endif
 
+#if JVET_AA0126_GLM
+const CtxSet ContextSetCfg::GlmFlags = ContextSetCfg::addCtxSet
+({
+  {  CNU, CNU, CNU, CNU, CNU, },
+  {  CNU, CNU, CNU, CNU, CNU, },
+  {  CNU, CNU, CNU, CNU, CNU, },
+  {  DWS, DWS, DWS, DWS, DWS, },
+});
+#endif
+
 #if JVET_AA0057_CCCM
 const CtxSet ContextSetCfg::CccmFlag = ContextSetCfg::addCtxSet
 ({
diff --git a/source/Lib/CommonLib/Contexts.h b/source/Lib/CommonLib/Contexts.h
index fe9c0360a79efb5a162089bb4dd11bdb1a6af5d3..9b051f6e040db202ca019b82d4ced09d6f9f10b2 100644
--- a/source/Lib/CommonLib/Contexts.h
+++ b/source/Lib/CommonLib/Contexts.h
@@ -494,6 +494,9 @@ public:
 #if JVET_Z0050_CCLM_SLOPE
   static const CtxSet   CclmDeltaFlags;
 #endif
+#if JVET_AA0126_GLM
+  static const CtxSet   GlmFlags;
+#endif
 #if JVET_AA0057_CCCM
   static const CtxSet   CccmFlag;
 #endif
diff --git a/source/Lib/CommonLib/IntraPrediction.cpp b/source/Lib/CommonLib/IntraPrediction.cpp
index f8e0a663193035b457196dffb6d478e3cdfc1432..181f18601588648479fd25005766efc33e23e45a 100644
--- a/source/Lib/CommonLib/IntraPrediction.cpp
+++ b/source/Lib/CommonLib/IntraPrediction.cpp
@@ -106,6 +106,13 @@ IntraPrediction::IntraPrediction()
 #endif
   m_piTemp = nullptr;
   m_pMdlmTemp = nullptr;
+#if JVET_AA0126_GLM
+  for (int i = 0; i < NUM_GLM_IDC; i++)
+  {
+    m_glmTempCb[i] = nullptr;
+    m_glmTempCr[i] = nullptr;
+  }
+#endif
 #if MMLM
   m_encPreRDRun = false;
 #endif
@@ -142,6 +149,13 @@ void IntraPrediction::destroy()
   m_piTemp = nullptr;
   delete[] m_pMdlmTemp;
   m_pMdlmTemp = nullptr;
+#if JVET_AA0126_GLM
+  for (int i = 0; i < NUM_GLM_IDC; i++)
+  {
+    delete[] m_glmTempCb[i]; m_glmTempCb[i] = nullptr;
+    delete[] m_glmTempCr[i]; m_glmTempCr[i] = nullptr;
+  }
+#endif
 
   for( auto &buffer : m_tempBuffer )
   {
@@ -234,7 +248,13 @@ void IntraPrediction::init(ChromaFormat chromaFormatIDC, const unsigned bitDepth
   {
     m_pMdlmTemp = new Pel[(2 * MAX_CU_SIZE + 1)*(2 * MAX_CU_SIZE + 1)];//MDLM will use top-above and left-below samples.
   }
-
+#if JVET_AA0126_GLM
+  for (int i = 0; i < NUM_GLM_IDC; i++)
+  {
+    if (m_glmTempCb[i] == nullptr) m_glmTempCb[i] = new Pel[(2 * MAX_CU_SIZE + 1)*(2 * MAX_CU_SIZE + 1)];
+    if (m_glmTempCr[i] == nullptr) m_glmTempCr[i] = new Pel[(2 * MAX_CU_SIZE + 1)*(2 * MAX_CU_SIZE + 1)];
+  }
+#endif
 
   for( auto &buffer : m_tempBuffer )
   {
@@ -1157,6 +1177,17 @@ void IntraPrediction::predIntraChromaLM(const ComponentID compID, PelBuf &piPred
 {
   int  iLumaStride = 0;
   PelBuf Temp;
+#if JVET_AA0126_GLM
+  if (pu.glmIdc.isActive())
+  {
+    int glmIdc = pu.glmIdc.getIdc(compID, 0);
+    Pel* glmTemp = compID == COMPONENT_Cb ? m_glmTempCb[glmIdc] : m_glmTempCr[glmIdc];
+    iLumaStride = 2 * MAX_CU_SIZE + 1;
+    Temp = PelBuf(glmTemp + iLumaStride + 1, iLumaStride, Size(chromaArea));
+  }
+  else
+  {
+#endif
 #if MMLM
   if ((intraDir == MDLM_L_IDX) || (intraDir == MDLM_T_IDX) || (intraDir == MMLM_L_IDX) || (intraDir == MMLM_T_IDX) || (m_encPreRDRun && intraDir == MMLM_CHROMA_IDX))
 #else
@@ -1171,7 +1202,10 @@ void IntraPrediction::predIntraChromaLM(const ComponentID compID, PelBuf &piPred
     iLumaStride = MAX_CU_SIZE + 1;
     Temp = PelBuf(m_piTemp + iLumaStride + 1, iLumaStride, Size(chromaArea));
   }
-  
+#if JVET_AA0126_GLM
+  }
+#endif
+
   CclmModel cclmModel;
 
   if ( createModel )
@@ -4743,6 +4777,223 @@ int isBelowLeftAvailable(const CodingUnit &cu, const ChannelType &chType, const
   return numIntra;
 }
 
+#if JVET_AA0126_GLM
+Pel IntraPrediction::xGlmGetLumaVal(const int s[6], const int c[6], const int glmIdx, const Pel val) const
+{
+  Pel grad = c[0] * s[0] + c[1] * s[1] + c[2] * s[2] 
+           + c[3] * s[3] + c[4] * s[4] + c[5] * s[5];
+#if NUM_GLM_WEIGHT
+  return (glmIdx >= NUM_GLM_PATTERN ? val + grad : grad);
+#else
+  return grad;
+#endif
+}
+
+void IntraPrediction::xGetLumaRecPixelsGlmAll(const PredictionUnit &pu, CompArea chromaArea)
+{
+  int c[6] = { 0 };
+
+  int iDstStride = 2 * MAX_CU_SIZE + 1;
+  Pel* pDst0Cb[NUM_GLM_IDC];
+  Pel* pDst0Cr[NUM_GLM_IDC];
+
+  for (int k = 0; k < NUM_GLM_IDC; k++)
+  {
+    pDst0Cb[k] = m_glmTempCb[k] + iDstStride + 1;
+    pDst0Cr[k] = m_glmTempCr[k] + iDstStride + 1;
+  }
+
+  //assert 420 chroma subsampling
+  CompArea lumaArea = CompArea( COMPONENT_Y, pu.chromaFormat, chromaArea.lumaPos(), recalcSize( pu.chromaFormat, CHANNEL_TYPE_CHROMA, CHANNEL_TYPE_LUMA, chromaArea.size() ) );//needed for correct pos/size (4x4 Tus)
+
+  CHECK(lumaArea.width == chromaArea.width && CHROMA_444 != pu.chromaFormat, "");
+  CHECK(lumaArea.height == chromaArea.height && CHROMA_444 != pu.chromaFormat && CHROMA_422 != pu.chromaFormat, "");
+
+  const SizeType uiCWidth = chromaArea.width;
+  const SizeType uiCHeight = chromaArea.height;
+
+  const CPelBuf Src = pu.cs->picture->getRecoBuf( lumaArea );
+  Pel const* pRecSrc0   = Src.bufAt( 0, 0 );
+  int iRecStride        = Src.stride;
+  int logSubWidthC  = getChannelTypeScaleX(CHANNEL_TYPE_CHROMA, pu.chromaFormat);
+  int logSubHeightC = getChannelTypeScaleY(CHANNEL_TYPE_CHROMA, pu.chromaFormat);
+
+  int iRecStride2       = iRecStride << logSubHeightC;
+
+  const CodingUnit& lumaCU = isChroma( pu.chType ) ? *pu.cs->picture->cs->getCU( lumaArea.pos(), CH_L ) : *pu.cu;
+  const CodingUnit&     cu = *pu.cu;
+
+  const CompArea& area = isChroma( pu.chType ) ? chromaArea : lumaArea;
+
+  const uint32_t uiTuWidth  = area.width;
+  const uint32_t uiTuHeight = area.height;
+
+  int iBaseUnitSize = ( 1 << MIN_CU_LOG2 );
+
+  const int  iUnitWidth       = iBaseUnitSize >> getComponentScaleX( area.compID, area.chromaFormat );
+  const int  iUnitHeight = iBaseUnitSize >> getComponentScaleY(area.compID, area.chromaFormat);
+
+  const int  iTUWidthInUnits = uiTuWidth / iUnitWidth;
+  const int  iTUHeightInUnits = uiTuHeight / iUnitHeight;
+  const int  iAboveUnits      = iTUWidthInUnits;
+  const int  iLeftUnits       = iTUHeightInUnits;
+  const int  chromaUnitWidth = iBaseUnitSize >> getComponentScaleX(COMPONENT_Cb, area.chromaFormat);
+  const int  chromaUnitHeight = iBaseUnitSize >> getComponentScaleY(COMPONENT_Cb, area.chromaFormat);
+  const int  topTemplateSampNum = 2 * uiCWidth; // for MDLM, the number of template samples is 2W or 2H.
+  const int  leftTemplateSampNum = 2 * uiCHeight;
+  assert(m_topRefLength >= topTemplateSampNum);
+  assert(m_leftRefLength >= leftTemplateSampNum);
+  const int  totalAboveUnits = (topTemplateSampNum + (chromaUnitWidth - 1)) / chromaUnitWidth;
+  const int  totalLeftUnits = (leftTemplateSampNum + (chromaUnitHeight - 1)) / chromaUnitHeight;
+  const int  totalUnits = totalLeftUnits + totalAboveUnits + 1;
+  const int  aboveRightUnits = totalAboveUnits - iAboveUnits;
+  const int  leftBelowUnits = totalLeftUnits - iLeftUnits;
+
+  int avaiAboveRightUnits = 0;
+  int avaiLeftBelowUnits = 0;
+  bool  bNeighborFlags[4 * MAX_NUM_PART_IDXS_IN_CTU_WIDTH + 1];
+  memset(bNeighborFlags, 0, totalUnits);
+  bool aboveIsAvailable, leftIsAvailable;
+
+  int availlableUnit = isLeftAvailable(isChroma(pu.chType) ? cu : lumaCU, toChannelType(area.compID), area.pos(),
+                                       iLeftUnits, iUnitHeight, (bNeighborFlags + iLeftUnits + leftBelowUnits - 1));
+
+  leftIsAvailable = availlableUnit == iTUHeightInUnits;
+
+  availlableUnit = isAboveAvailable(isChroma(pu.chType) ? cu : lumaCU, toChannelType(area.compID), area.pos(),
+                                    iAboveUnits, iUnitWidth, (bNeighborFlags + iLeftUnits + leftBelowUnits + 1));
+
+  aboveIsAvailable = availlableUnit == iTUWidthInUnits;
+
+  if (leftIsAvailable)   // if left is not available, then the below left is not available
+  {
+    avaiLeftBelowUnits = isBelowLeftAvailable(isChroma(pu.chType) ? cu : lumaCU, toChannelType(area.compID), area.bottomLeftComp(area.compID), leftBelowUnits, iUnitHeight, (bNeighborFlags + leftBelowUnits - 1));
+  }
+
+  if (aboveIsAvailable)   // if above is not available, then  the above right is not available.
+  {
+    avaiAboveRightUnits = isAboveRightAvailable(isChroma(pu.chType) ? cu : lumaCU, toChannelType(area.compID), area.topRightComp(area.compID), aboveRightUnits, iUnitWidth, (bNeighborFlags + iLeftUnits + leftBelowUnits + iAboveUnits + 1));
+  }
+
+  Pel*       pDstCb[NUM_GLM_IDC];
+  Pel*       pDstCr[NUM_GLM_IDC];
+  Pel const* piSrc = nullptr;
+
+  if (aboveIsAvailable)
+  {
+    for (int k = 0; k < NUM_GLM_IDC; k++)
+    {
+      pDstCb[k]  = pDst0Cb[k]    - iDstStride;
+      pDstCr[k]  = pDst0Cr[k]    - iDstStride;
+    }
+
+    int addedAboveRight = avaiAboveRightUnits*chromaUnitWidth;
+
+    for (int i = 0; i < uiCWidth + addedAboveRight; i++)
+    {
+      const int l = (i == 0 && !leftIsAvailable) ? 0 : 1;
+      {
+        piSrc = pRecSrc0 - iRecStride2;
+        int s[6] = { piSrc[2 * i              - l], piSrc[2 * i                 ], piSrc[2 * i              + 1],
+                     piSrc[2 * i + iRecStride - l], piSrc[2 * i + iRecStride    ], piSrc[2 * i + iRecStride + 1] };
+        int val = (1 * s[0] + 2 * s[1] + 1 * s[2] 
+                 + 1 * s[3] + 2 * s[4] + 1 * s[5] + 4) >> 3;
+        pDstCb[0][i] = val;
+        pDstCr[0][i] = val;
+
+        for (int k = 1; k < NUM_GLM_IDC; k++)
+        {
+          int p = (k - 1 < NUM_GLM_PATTERN) ? (k - 1) : (k - 1 - NUM_GLM_PATTERN);
+          c[0] = g_glmPattern[p][0], c[1] = g_glmPattern[p][1], c[2] = g_glmPattern[p][2];
+          c[3] = g_glmPattern[p][3], c[4] = g_glmPattern[p][4], c[5] = g_glmPattern[p][5];
+          int grad = c[0] * s[0] + c[1] * s[1] + c[2] * s[2] 
+                   + c[3] * s[3] + c[4] * s[4] + c[5] * s[5];
+          pDstCb[k][i] = (k - 1 < NUM_GLM_PATTERN) ? grad : val + grad;
+          pDstCr[k][i] = (k - 1 < NUM_GLM_PATTERN) ? grad : val + grad;
+        }
+      }
+    }
+  }
+
+  if (leftIsAvailable)
+  {
+    for (int k = 0; k < NUM_GLM_IDC; k++)
+    {
+      pDstCb[k]  = pDst0Cb[k]    - 1;
+      pDstCr[k]  = pDst0Cr[k]    - 1;
+    }
+    piSrc = pRecSrc0 - 1 - logSubWidthC;
+
+    int addedLeftBelow = avaiLeftBelowUnits*chromaUnitHeight;
+
+    for (int j = 0; j < uiCHeight + addedLeftBelow; j++)
+    {
+      {
+        int s[6] = { piSrc[           - 1], piSrc[             0], piSrc[             1],
+                     piSrc[iRecStride - 1], piSrc[iRecStride    ], piSrc[iRecStride + 1] };
+        int val = (1 * s[0] + 2 * s[1] + 1 * s[2] 
+                 + 1 * s[3] + 2 * s[4] + 1 * s[5] + 4) >> 3;
+        pDstCb[0][0] = val;
+        pDstCr[0][0] = val;
+
+        for (int k = 1; k < NUM_GLM_IDC; k++)
+        {
+          int p = (k - 1 < NUM_GLM_PATTERN) ? (k - 1) : (k - 1 - NUM_GLM_PATTERN);
+          c[0] = g_glmPattern[p][0], c[1] = g_glmPattern[p][1], c[2] = g_glmPattern[p][2];
+          c[3] = g_glmPattern[p][3], c[4] = g_glmPattern[p][4], c[5] = g_glmPattern[p][5];
+          int grad = c[0] * s[0] + c[1] * s[1] + c[2] * s[2] 
+                   + c[3] * s[3] + c[4] * s[4] + c[5] * s[5];
+          pDstCb[k][0] = (k - 1 < NUM_GLM_PATTERN) ? grad : val + grad;
+          pDstCr[k][0] = (k - 1 < NUM_GLM_PATTERN) ? grad : val + grad;
+        }
+      }
+
+      piSrc += iRecStride2;
+      for (int k = 0; k < NUM_GLM_IDC; k++)
+      {
+        pDstCb[k] += iDstStride;
+        pDstCr[k] += iDstStride;
+      }
+    }
+  }
+
+  // inner part from reconstructed picture buffer
+  for( int j = 0; j < uiCHeight; j++ )
+  {
+    for( int i = 0; i < uiCWidth; i++ )
+    {
+      const int l = (i == 0 && !leftIsAvailable) ? 0 : 1;
+      {
+        int s[6] = { pRecSrc0[2 * i              - l], pRecSrc0[2 * i                 ], pRecSrc0[2 * i              + 1],
+                     pRecSrc0[2 * i + iRecStride - l], pRecSrc0[2 * i + iRecStride    ], pRecSrc0[2 * i + iRecStride + 1] };
+        int val = (1 * s[0] + 2 * s[1] + 1 * s[2] 
+                 + 1 * s[3] + 2 * s[4] + 1 * s[5] + 4) >> 3;
+        pDst0Cb[0][i] = val;
+        pDst0Cr[0][i] = val;
+
+        for (int k = 1; k < NUM_GLM_IDC; k++)
+        {
+          int p = (k - 1 < NUM_GLM_PATTERN) ? (k - 1) : (k - 1 - NUM_GLM_PATTERN);
+          c[0] = g_glmPattern[p][0], c[1] = g_glmPattern[p][1], c[2] = g_glmPattern[p][2];
+          c[3] = g_glmPattern[p][3], c[4] = g_glmPattern[p][4], c[5] = g_glmPattern[p][5];
+          int grad = c[0] * s[0] + c[1] * s[1] + c[2] * s[2] 
+                   + c[3] * s[3] + c[4] * s[4] + c[5] * s[5];
+          pDst0Cb[k][i] = (k - 1 < NUM_GLM_PATTERN) ? grad : val + grad;
+          pDst0Cr[k][i] = (k - 1 < NUM_GLM_PATTERN) ? grad : val + grad;
+        }
+      }
+    }
+
+    for (int k = 0; k < NUM_GLM_IDC; k++)
+    {
+      pDst0Cb[k]    += iDstStride;
+      pDst0Cr[k]    += iDstStride;
+    }
+    pRecSrc0 += iRecStride2;
+  }
+}
+#endif
+
 // LumaRecPixels
 void IntraPrediction::xGetLumaRecPixels(const PredictionUnit &pu, CompArea chromaArea)
 {
@@ -4753,10 +5004,36 @@ void IntraPrediction::xGetLumaRecPixels(const PredictionUnit &pu, CompArea chrom
     return;
   }
 #endif
-  
+
+#if JVET_AA0126_GLM
+  ComponentID compID = chromaArea.compID;
+  int glmIdc = pu.glmIdc.getIdc(compID, 0);
+  int c[6] = { 0 };
+  CHECK(glmIdc < 0 || glmIdc >= NUM_GLM_IDC, "glmIdc out of range");
+
+  if (glmIdc != 0)
+  {
+    int glmIdx = glmIdc - 1;
+    int p = glmIdx >= NUM_GLM_PATTERN ? glmIdx - NUM_GLM_PATTERN : glmIdx;
+    CHECK(p < 0 || p >= NUM_GLM_PATTERN, "glmPattern out of range");
+    c[0] = g_glmPattern[p][0], c[1] = g_glmPattern[p][1], c[2] = g_glmPattern[p][2];
+    c[3] = g_glmPattern[p][3], c[4] = g_glmPattern[p][4], c[5] = g_glmPattern[p][5];
+  }
+#endif
+
   int iDstStride = 0;
   Pel* pDst0 = 0;
   int curChromaMode = pu.intraDir[1];
+#if JVET_AA0126_GLM
+  if (pu.glmIdc.isActive())
+  {
+    iDstStride = 2 * MAX_CU_SIZE + 1;
+    Pel* glmTemp = compID == COMPONENT_Cb ? m_glmTempCb[glmIdc] : m_glmTempCr[glmIdc];
+    pDst0 = glmTemp + iDstStride + 1;
+  }
+  else
+  {
+#endif
 #if MMLM
   if ((curChromaMode == MDLM_L_IDX) || (curChromaMode == MDLM_T_IDX) || (curChromaMode == MMLM_L_IDX) || (curChromaMode == MMLM_T_IDX))
 #else
@@ -4771,6 +5048,9 @@ void IntraPrediction::xGetLumaRecPixels(const PredictionUnit &pu, CompArea chrom
     iDstStride = MAX_CU_SIZE + 1;
     pDst0 = m_piTemp + iDstStride + 1; //MMLM_SAMPLE_NEIGHBOR_LINES;
   }
+#if JVET_AA0126_GLM
+  }
+#endif
   //assert 420 chroma subsampling
   CompArea lumaArea = CompArea( COMPONENT_Y, pu.chromaFormat, chromaArea.lumaPos(), recalcSize( pu.chromaFormat, CHANNEL_TYPE_CHROMA, CHANNEL_TYPE_LUMA, chromaArea.size() ) );//needed for correct pos/size (4x4 Tus)
 
@@ -4907,6 +5187,16 @@ void IntraPrediction::xGetLumaRecPixels(const PredictionUnit &pu, CompArea chrom
         s += piSrc[2 * i + iRecStride - (leftPadding ? 0 : 1)];
         pDst[i] = s >> 3;
       }
+#if JVET_AA0126_GLM
+      if (glmIdc != 0)
+      {
+        piSrc = pRecSrc0 - iRecStride2;
+        int l = leftPadding ? 0 : 1;
+        int s[6] = { piSrc[2 * i              - l], piSrc[2 * i                 ], piSrc[2 * i              + 1],
+                     piSrc[2 * i + iRecStride - l], piSrc[2 * i + iRecStride    ], piSrc[2 * i + iRecStride + 1] };
+        pDst[i] = xGlmGetLumaVal(s, c, glmIdc - 1, pDst[i]);
+      }
+#endif
     }
   }
 
@@ -4962,6 +5252,14 @@ void IntraPrediction::xGetLumaRecPixels(const PredictionUnit &pu, CompArea chrom
         s += piSrc[iRecStride - 1];
         pDst[0] = s >> 3;
       }
+#if JVET_AA0126_GLM
+      if (glmIdc != 0)
+      {
+        int s[6] = { piSrc[           - 1], piSrc[             0], piSrc[             1],
+                     piSrc[iRecStride - 1], piSrc[iRecStride    ], piSrc[iRecStride + 1] };
+        pDst[0] = xGlmGetLumaVal(s, c, glmIdc - 1, pDst[0]);
+      }
+#endif
 
       piSrc += iRecStride2;
       pDst  += iDstStride;
@@ -5014,6 +5312,16 @@ void IntraPrediction::xGetLumaRecPixels(const PredictionUnit &pu, CompArea chrom
         s += pRecSrc0[2 * i + iRecStride - (leftPadding ? 0 : 1)];
         pDst0[i] = s >> 3;
       }
+#if JVET_AA0126_GLM
+      if (glmIdc != 0)
+      {
+        const bool leftPadding = i == 0 && !leftIsAvailable;
+        int l = leftPadding ? 0 : 1;
+        int s[6] = { pRecSrc0[2 * i              - l], pRecSrc0[2 * i                 ], pRecSrc0[2 * i              + 1],
+                     pRecSrc0[2 * i + iRecStride - l], pRecSrc0[2 * i + iRecStride    ], pRecSrc0[2 * i + iRecStride + 1] };
+        pDst0[i] = xGlmGetLumaVal(s, c, glmIdc - 1, pDst0[i]);
+      }
+#endif
     }
 
     pDst0    += iDstStride;
@@ -5956,6 +6264,17 @@ void IntraPrediction::xGetLMParametersLMS(const PredictionUnit &pu, const Compon
   int srcStride;
 
   PelBuf temp;
+#if JVET_AA0126_GLM
+  if (pu.glmIdc.isActive())
+  {
+    int glmIdc = pu.glmIdc.getIdc(compID, 0);
+    Pel* glmTemp = compID == COMPONENT_Cb ? m_glmTempCb[glmIdc] : m_glmTempCr[glmIdc];
+    srcStride = 2 * MAX_CU_SIZE + 1;
+    temp = PelBuf(glmTemp + srcStride + 1, srcStride, Size(chromaArea));
+  }
+  else
+  {
+#endif
 #if MMLM
   if ((curChromaMode == MDLM_L_IDX) || (curChromaMode == MDLM_T_IDX) || (curChromaMode == MMLM_L_IDX) || (curChromaMode == MMLM_T_IDX)
     || (m_encPreRDRun && curChromaMode == MMLM_CHROMA_IDX))
@@ -5971,6 +6290,9 @@ void IntraPrediction::xGetLMParametersLMS(const PredictionUnit &pu, const Compon
     srcStride = MAX_CU_SIZE + 1;
     temp = PelBuf(m_piTemp + srcStride + 1, srcStride, Size(chromaArea));
   }
+#if JVET_AA0126_GLM
+  }
+#endif
   srcColor0 = temp.bufAt(0, 0);
   curChroma0 = getPredictorPtr(compID);
 
diff --git a/source/Lib/CommonLib/IntraPrediction.h b/source/Lib/CommonLib/IntraPrediction.h
index 1dbf3b02ec2889e231e059907532328bb1c18a85..10bc05f7979efd9211ab3db3fc787f75e4a88a5e 100644
--- a/source/Lib/CommonLib/IntraPrediction.h
+++ b/source/Lib/CommonLib/IntraPrediction.h
@@ -219,6 +219,10 @@ private:
 
   Pel* m_piTemp;
   Pel* m_pMdlmTemp; // for MDLM mode
+#if JVET_AA0126_GLM
+  Pel* m_glmTempCb[NUM_GLM_IDC];
+  Pel* m_glmTempCr[NUM_GLM_IDC];
+#endif
   MatrixIntraPrediction m_matrixIntraPred;
 
 
@@ -374,6 +378,10 @@ public:
   // Cross-component Chroma
   void predIntraChromaLM(const ComponentID compID, PelBuf &piPred, const PredictionUnit &pu, const CompArea& chromaArea, int intraDir, bool createModel = true, CclmModel *cclmModelStored = nullptr);
   void xGetLumaRecPixels(const PredictionUnit &pu, CompArea chromaArea);
+#if JVET_AA0126_GLM
+  void xGetLumaRecPixelsGlmAll(const PredictionUnit &pu, CompArea chromaArea);
+  Pel xGlmGetLumaVal    (const int s[6], const int c[6], const int glmIdx, const Pel val) const;
+#endif
   /// set parameters from CU data for accessing intra data
   void initIntraPatternChType     (const CodingUnit &cu, const CompArea &area, const bool forceRefFilterFlag = false); // use forceRefFilterFlag to get both filtered and unfiltered buffers
   void initIntraPatternChTypeISP  (const CodingUnit& cu, const CompArea& area, PelBuf& piReco, const bool forceRefFilterFlag = false); // use forceRefFilterFlag to get both filtered and unfiltered buffers
diff --git a/source/Lib/CommonLib/Rom.cpp b/source/Lib/CommonLib/Rom.cpp
index 4cf51287e5a6f6d48f09d6fcf98188657436325f..475a5bc33ee5764455010dd29ba56fd99bc55952 100644
--- a/source/Lib/CommonLib/Rom.cpp
+++ b/source/Lib/CommonLib/Rom.cpp
@@ -5275,4 +5275,17 @@ int g_gradDivTable[16] = { 0, 7, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 1, 1, 0 };
 #if JVET_AA0107_RMVF_AFFINE_MERGE_DERIVATION
 int g_rmvfMultApproxTbl[3 << sizeof(int64_t)];
 #endif
+#if JVET_AA0126_GLM
+const int8_t g_glmPattern[NUM_GLM_PATTERN][6] =
+{
+  {  1,  0, -1,  1,  0, -1, }, {  1,  2,  1, -1, -2, -1, }, {  2,  1, -1,  1, -1, -2, }, { -1,  1,  2, -2, -1,  1, },
+#if NUM_GLM_PATTERN > 4
+  {  0,  2, -2,  0,  1, -1, }, {  1,  1,  1, -1, -1, -1, }, {  1,  1, -1,  1, -1, -1, }, { -1,  1,  1, -1, -1,  1, },
+#if NUM_GLM_PATTERN > 8
+  {  0,  1, -1,  0,  1, -1, }, {  0,  1,  1,  0, -1, -1, }, {  1,  1,  0,  0, -1, -1, }, {  0,  1,  1, -1, -1,  0, },
+  {  1, -1,  0,  1, -1,  0, }, {  1,  1,  0, -1, -1,  0, }, {  1,  2,  0,  0, -2, -1, }, {  0,  2,  1, -1, -2,  0, },
+#endif
+#endif
+};
+#endif
 //! \}
diff --git a/source/Lib/CommonLib/Rom.h b/source/Lib/CommonLib/Rom.h
index b4e11e8d28b40f270bf4e6e3d2b4e864bea4b637..81cd7ac835d1ee02c6b9f97021e9a8a01619e1be 100644
--- a/source/Lib/CommonLib/Rom.h
+++ b/source/Lib/CommonLib/Rom.h
@@ -383,5 +383,8 @@ extern int g_gradDivTable[16];
 #if JVET_AA0107_RMVF_AFFINE_MERGE_DERIVATION
 extern int g_rmvfMultApproxTbl[3 << sizeof(int64_t)];
 #endif
+#if JVET_AA0126_GLM
+extern const int8_t g_glmPattern[NUM_GLM_PATTERN][6];
+#endif
 #endif  //__TCOMROM__
 
diff --git a/source/Lib/CommonLib/TypeDef.h b/source/Lib/CommonLib/TypeDef.h
index 67201d86365cf5d7001b502058153ac20ee4303e..2b4421ed749839f9dd284359ac50d77a3c5fe252 100644
--- a/source/Lib/CommonLib/TypeDef.h
+++ b/source/Lib/CommonLib/TypeDef.h
@@ -132,7 +132,7 @@
 #define JVET_Z0050_DIMD_CHROMA_FUSION                     1 // JVET-Z0050: DIMD chroma mode and fusion of chroma intra prediction modes
 #define JVET_Z0050_CCLM_SLOPE                             1 // JVET-Z0050: CCLM with slope adjustments
 #define JVET_AA0057_CCCM                                  1 // JVET-AA0057: Convolutional cross-component model (CCCM)
-
+#define JVET_AA0126_GLM                                   1 // JVET-AA0126: Gradient linear model
 
 //IBC
 #define JVET_Y0058_IBC_LIST_MODIFY                        1 // JVET-Y0058: Modifications of IBC merge/AMVP list construction, ARMC-TM-IBC part is included under JVET_W0090_ARMC_TM
@@ -1396,6 +1396,21 @@ struct CclmOffsets
 };
 #endif
 
+#if JVET_AA0126_GLM
+struct GlmIdc
+{
+  int8_t cb0 = 0, cr0 = 0;
+  int8_t cb1 = 0, cr1 = 0;
+  
+  bool isActive()                         const { return cb0 || cr0 || cb1 || cr1;                                                           }
+  bool isActive(ComponentID c)            const { return (c == COMPONENT_Cb) ? (cb0 || cb1) : (cr0 || cr1);                                  }
+  void setAllZero()                             { cb0 = 0;  cr0 = 0;  cb1 = 0;  cr1 = 0;                                                     }
+  void setIdcs(int b0, int r0, int b1, int r1)  { cb0 = b0; cr0 = r0; cb1 = b1; cr1 = r1;                                                    }
+  void setIdc(ComponentID c, int model, int v)  { (c == COMPONENT_Cb) ? (model == 0 ? cb0 = v : cb1 = v) : (model == 0 ? cr0 = v : cr1 = v); }
+  int  getIdc(ComponentID c, int model)   const { return (c == COMPONENT_Cb) ? (model == 0 ? cb0 : cb1) : (model == 0 ? cr0 : cr1);          }
+};
+#endif
+
 struct BitDepths
 {
   int recon[MAX_NUM_CHANNEL_TYPE]; ///< the bit depth as indicated in the SPS
diff --git a/source/Lib/CommonLib/Unit.cpp b/source/Lib/CommonLib/Unit.cpp
index 4b17a450b2f8462a2e1d0f4fc699455670f71cfa..5334899da0caff9cdb7db0b27d523cd68f4ee94d 100644
--- a/source/Lib/CommonLib/Unit.cpp
+++ b/source/Lib/CommonLib/Unit.cpp
@@ -648,6 +648,9 @@ void PredictionUnit::initData()
 #if JVET_Z0050_CCLM_SLOPE
   cclmOffsets = {};
 #endif
+#if JVET_AA0126_GLM
+  glmIdc      = {};
+#endif
 #if JVET_AA0057_CCCM
   cccmFlag    = 0;
 #endif
@@ -769,6 +772,9 @@ PredictionUnit& PredictionUnit::operator=(const IntraPredictionData& predData)
 #if JVET_Z0050_CCLM_SLOPE
   cclmOffsets = predData.cclmOffsets;
 #endif
+#if JVET_AA0126_GLM
+  glmIdc      = predData.glmIdc;
+#endif
 #if JVET_AA0057_CCCM
   cccmFlag    = predData.cccmFlag;
 #endif
@@ -887,6 +893,9 @@ PredictionUnit& PredictionUnit::operator=( const PredictionUnit& other )
 #if JVET_Z0050_CCLM_SLOPE
   cclmOffsets = other.cclmOffsets;
 #endif
+#if JVET_AA0126_GLM
+  glmIdc      = other.glmIdc;
+#endif
 #if JVET_AA0057_CCCM
   cccmFlag    = other.cccmFlag;
 #endif
diff --git a/source/Lib/CommonLib/Unit.h b/source/Lib/CommonLib/Unit.h
index 72b66531d4fa420513d258aa052281d180bea337..b42323711cc317d6d87f3258c5a62e78153163cc 100644
--- a/source/Lib/CommonLib/Unit.h
+++ b/source/Lib/CommonLib/Unit.h
@@ -430,6 +430,9 @@ struct IntraPredictionData
 #if JVET_Z0050_CCLM_SLOPE
   CclmOffsets cclmOffsets;
 #endif
+#if JVET_AA0126_GLM
+  GlmIdc    glmIdc;
+#endif
 #if JVET_AA0057_CCCM
   int       cccmFlag;
 #endif
diff --git a/source/Lib/CommonLib/UnitTools.cpp b/source/Lib/CommonLib/UnitTools.cpp
index 96efd102c1e39b3696602fe30d87b61f0597feb7..ad7ff53e50f9ce158f65c325a4f4a79df754cb9d 100644
--- a/source/Lib/CommonLib/UnitTools.cpp
+++ b/source/Lib/CommonLib/UnitTools.cpp
@@ -1654,11 +1654,27 @@ void PU::getIntraChromaCandModes(const PredictionUnit &pu, unsigned modeList[NUM
   bool hasDeltaFlag = chrMode == LM_CHROMA_IDX;
 #endif
   hasDeltaFlag     &= pu.Cb().width * pu.Cb().height >= 128;
+#if JVET_AA0126_GLM
+  hasDeltaFlag     &= !pu.glmIdc.isActive();
+#endif
 
   return hasDeltaFlag;
 }
 #endif
 
+#if JVET_AA0126_GLM
+bool PU::hasGlmFlag(const PredictionUnit &pu, const int mode)
+{
+  int  chrMode      = mode < 0 ? pu.intraDir[1] : mode;
+  bool hasGlmFlag   = chrMode == LM_CHROMA_IDX || chrMode == MDLM_L_IDX || chrMode == MDLM_T_IDX;
+#if JVET_AA0057_CCCM
+  hasGlmFlag       &= !pu.cccmFlag;
+#endif
+  
+  return hasGlmFlag;
+}
+#endif
+
 bool PU::isLMCMode(unsigned mode)
 {
 #if MMLM
diff --git a/source/Lib/CommonLib/UnitTools.h b/source/Lib/CommonLib/UnitTools.h
index 12f2a13350218a528d9433756bcfec9f07025cf9..45e62908c74bac66460919662fe5234b1140b07e 100644
--- a/source/Lib/CommonLib/UnitTools.h
+++ b/source/Lib/CommonLib/UnitTools.h
@@ -459,6 +459,9 @@ namespace PU
 #if JVET_Z0050_CCLM_SLOPE
   bool hasCclmDeltaFlag(const PredictionUnit &pu, const int mode = -1);
 #endif
+#if JVET_AA0126_GLM
+  bool hasGlmFlag      (const PredictionUnit &pu, const int mode = -1);
+#endif
 #if JVET_AA0057_CCCM
   void getCccmRefLineNum  (const PredictionUnit& pu, int& th, int& tv);
   bool cccmSingleModeAvail(const PredictionUnit& pu, int intraMode);
diff --git a/source/Lib/DecoderLib/CABACReader.cpp b/source/Lib/DecoderLib/CABACReader.cpp
index 7341fc244d9f1dc9ad49031d5a4e509f1c4cdf07..dbb2221d430a793a888ec3a50dffdbcc56bdbffb 100644
--- a/source/Lib/DecoderLib/CABACReader.cpp
+++ b/source/Lib/DecoderLib/CABACReader.cpp
@@ -2239,6 +2239,53 @@ void CABACReader::cclmDeltaSlope(PredictionUnit& pu)
 }
 #endif
 
+#if JVET_AA0126_GLM
+void CABACReader::glmIdc(PredictionUnit& pu)
+{
+  if ( PU::hasGlmFlag( pu ) )
+  {
+    bool glmActive = m_BinDecoder.decodeBin(Ctx::GlmFlags(0));
+
+    if ( glmActive )
+    {
+      bool bothActive = m_BinDecoder.decodeBin(Ctx::GlmFlags(3));
+
+      pu.glmIdc.cb0 = bothActive;
+      pu.glmIdc.cr0 = bothActive;
+      
+      if ( !bothActive )
+      {
+        pu.glmIdc.cb0 = m_BinDecoder.decodeBin(Ctx::GlmFlags(1));
+        pu.glmIdc.cr0 = !pu.glmIdc.cb0;
+      }
+
+      if ( pu.glmIdc.cb0 )
+      {
+#if NUM_GLM_WEIGHT
+        pu.glmIdc.cb0 += m_BinDecoder.decodeBin(Ctx::GlmFlags(2)) ? NUM_GLM_PATTERN : 0;
+#endif
+        pu.glmIdc.cb0 += m_BinDecoder.decodeBinsEP(NUM_GLM_PATTERN_BITS);
+      }
+
+      if ( pu.glmIdc.cr0 )
+      {
+#if NUM_GLM_WEIGHT
+        pu.glmIdc.cr0 += m_BinDecoder.decodeBin(Ctx::GlmFlags(4)) ? NUM_GLM_PATTERN : 0;
+#endif
+        pu.glmIdc.cr0 += m_BinDecoder.decodeBinsEP(NUM_GLM_PATTERN_BITS);
+      }
+
+#if MMLM
+      if ( PU::isMultiModeLM( pu.intraDir[1] ) )
+      {
+        pu.glmIdc.cb1 = pu.glmIdc.cb0;
+        pu.glmIdc.cr1 = pu.glmIdc.cr0;
+      }
+#endif
+    }
+  }
+}
+#endif
 bool CABACReader::intra_chroma_lmc_mode(PredictionUnit& pu)
 {
 #if MMLM
@@ -2285,7 +2332,9 @@ bool CABACReader::intra_chroma_lmc_mode(PredictionUnit& pu)
 #if JVET_AA0057_CCCM
   cccmFlag( pu );
 #endif
-
+#if JVET_AA0126_GLM
+  glmIdc( pu );
+#endif
 #if JVET_Z0050_CCLM_SLOPE
   cclmDeltaSlope( pu );
 #endif
diff --git a/source/Lib/DecoderLib/CABACReader.h b/source/Lib/DecoderLib/CABACReader.h
index 3e04bfaa9d565f66a70da822b123b5bb6ad22315..3825af393018ae55d76c0d3b2ac144af430b7ade 100644
--- a/source/Lib/DecoderLib/CABACReader.h
+++ b/source/Lib/DecoderLib/CABACReader.h
@@ -195,6 +195,9 @@ public:
   void        cclmDelta             ( PredictionUnit&               pu, int8_t &delta );
   void        cclmDeltaSlope       ( PredictionUnit&               pu );
 #endif
+#if JVET_AA0126_GLM
+  void        glmIdc                ( PredictionUnit&               pu );
+#endif
 
 
   // transform tree (clause 7.3.8.8)
diff --git a/source/Lib/EncoderLib/CABACWriter.cpp b/source/Lib/EncoderLib/CABACWriter.cpp
index 431aeb02ec198406c54976929fec121fc055fa60..0997db26016f3abb2f14fa26d7484f39515ae100 100644
--- a/source/Lib/EncoderLib/CABACWriter.cpp
+++ b/source/Lib/EncoderLib/CABACWriter.cpp
@@ -1967,6 +1967,57 @@ void CABACWriter::cclmDeltaSlope(const PredictionUnit& pu)
 }
 #endif
 
+#if JVET_AA0126_GLM
+void CABACWriter::glmIdc(const PredictionUnit& pu)
+{
+  if ( PU::hasGlmFlag( pu ) )
+  {
+    bool glmActive = pu.glmIdc.isActive();
+
+    m_BinEncoder.encodeBin( glmActive ? 1 : 0, Ctx::GlmFlags(0) );
+    
+    if ( glmActive )
+    {
+      bool bothActive = pu.glmIdc.cb0 && pu.glmIdc.cr0;
+
+      m_BinEncoder.encodeBin( bothActive ? 1 : 0, Ctx::GlmFlags(3) );
+
+      if ( !bothActive )
+      {
+        m_BinEncoder.encodeBin( pu.glmIdc.cb0 ? 1 : 0, Ctx::GlmFlags(1) );
+      }
+
+      if ( pu.glmIdc.cb0 ) 
+      {
+        int glmIdx = pu.glmIdc.cb0 - 1;
+#if NUM_GLM_WEIGHT
+        m_BinEncoder.encodeBin( glmIdx >= NUM_GLM_PATTERN ? 1 : 0, Ctx::GlmFlags(2) );
+        glmIdx -= glmIdx >= NUM_GLM_PATTERN ? NUM_GLM_PATTERN : 0;
+#endif
+        m_BinEncoder.encodeBinsEP( glmIdx, NUM_GLM_PATTERN_BITS );
+      }
+      
+      if ( pu.glmIdc.cr0 ) 
+      {
+        int glmIdx = pu.glmIdc.cr0 - 1;
+#if NUM_GLM_WEIGHT
+        m_BinEncoder.encodeBin( glmIdx >= NUM_GLM_PATTERN ? 1 : 0, Ctx::GlmFlags(4) );
+        glmIdx -= glmIdx >= NUM_GLM_PATTERN ? NUM_GLM_PATTERN : 0;
+#endif
+        m_BinEncoder.encodeBinsEP( glmIdx, NUM_GLM_PATTERN_BITS );
+      }
+        
+#if MMLM
+      if ( PU::isMultiModeLM( pu.intraDir[1] ) )
+      {
+        CHECK(pu.glmIdc.cb0 != pu.glmIdc.cb1 
+           || pu.glmIdc.cr0 != pu.glmIdc.cr1, "GLM cb0 != cb1 || cr0 != cr1")
+      }
+#endif
+    }
+  }
+}
+#endif
 void CABACWriter::intra_chroma_lmc_mode(const PredictionUnit& pu)
 {
   const unsigned intraDir = pu.intraDir[1];
@@ -2019,7 +2070,9 @@ void CABACWriter::intra_chroma_lmc_mode(const PredictionUnit& pu)
 #if JVET_AA0057_CCCM
   cccmFlag( pu );
 #endif
-
+#if JVET_AA0126_GLM
+  glmIdc( pu );
+#endif
 #if JVET_Z0050_CCLM_SLOPE
   cclmDeltaSlope( pu );
 #endif
diff --git a/source/Lib/EncoderLib/CABACWriter.h b/source/Lib/EncoderLib/CABACWriter.h
index 706ab6ffc6b27408e7ad102900f941f30c2b19dc..05c2bf5d0c05c19e5e70dbf0bb09b08cd816a831 100644
--- a/source/Lib/EncoderLib/CABACWriter.h
+++ b/source/Lib/EncoderLib/CABACWriter.h
@@ -222,6 +222,9 @@ public:
   void        cclmDelta             ( const PredictionUnit&         pu, int8_t delta);
   void        cclmDeltaSlope       ( const PredictionUnit&         pu );
 #endif
+#if JVET_AA0126_GLM
+  void        glmIdc                    ( const PredictionUnit&         pu );
+#endif
 
   // transform tree (clause 7.3.8.8)
   void        transform_tree            ( const CodingStructure&        cs,       Partitioner&      pm,     CUCtx& cuCtx,                         const PartSplit ispType = TU_NO_ISP, const int subTuIdx = -1 );
diff --git a/source/Lib/EncoderLib/IntraSearch.cpp b/source/Lib/EncoderLib/IntraSearch.cpp
index 5fd007e74fccf2d1c8a421ba5f2839fc13d5408b..297129b1727b571c49dc12866404011ef3b7d72e 100644
--- a/source/Lib/EncoderLib/IntraSearch.cpp
+++ b/source/Lib/EncoderLib/IntraSearch.cpp
@@ -1887,6 +1887,11 @@ void IntraSearch::estIntraPredChromaQT( CodingUnit &cu, Partitioner &partitioner
     CclmOffsets satdCclmOffsetsBest[NUM_CHROMA_MODE];
     int64_t     satdCclmCosts      [NUM_CHROMA_MODE] = { 0 };
 #endif
+#if JVET_AA0126_GLM
+    GlmIdc      bestGlmIdc = {};
+    GlmIdc      satdGlmIdcBest     [NUM_CHROMA_MODE];
+    int64_t     satdGlmCosts       [NUM_CHROMA_MODE] = { 0 };
+#endif
 #if JVET_Z0050_DIMD_CHROMA_FUSION
     bool isChromaFusion = false;
 #endif
@@ -1998,6 +2003,47 @@ void IntraSearch::estIntraPredChromaQT( CodingUnit &cu, Partitioner &partitioner
       initIntraPatternChType(cu, pu.Cb());
       initIntraPatternChType(cu, pu.Cr());
       xGetLumaRecPixels(pu, pu.Cb());
+
+#if JVET_AA0126_GLM
+      if ( PU::isLMCModeEnabled( pu, LM_CHROMA_IDX ) && PU::hasGlmFlag( pu, LM_CHROMA_IDX ) )
+      {
+        // Generate all GLM templates at encoder
+        xGetLumaRecPixelsGlmAll(pu, pu.Cb());
+
+        for ( int mode = LM_CHROMA_IDX; mode <= MMLM_T_IDX; mode++ )
+        {
+          satdGlmIdcBest[mode - LM_CHROMA_IDX].setAllZero();
+          
+          if ( PU::hasGlmFlag( pu, mode ) )
+          {
+            for ( int comp = COMPONENT_Cb; comp <= COMPONENT_Cr; comp++ )
+            {
+              ComponentID       compID = ComponentID( comp );
+              int              idcBest = 0;
+              int64_t         satdBest = 0;
+              GlmIdc&         idcsBest = satdGlmIdcBest[mode - LM_CHROMA_IDX];
+              
+              pu.intraDir[1] = mode;
+              pu.glmIdc.setAllZero();
+
+              xFindBestGlmIdcSATD(pu, compID, idcBest, satdBest );
+
+              idcsBest.setIdc(compID, 0, idcBest);
+              idcsBest.setIdc(compID, 1, idcBest);
+              
+              satdGlmCosts[mode - LM_CHROMA_IDX] += satdBest; // Summing up Cb and Cr cost
+            }
+            
+            if ( !satdGlmIdcBest[0].isActive() )
+            {
+              break;
+            }
+          }
+        }
+      }
+
+      pu.glmIdc.setAllZero();
+#endif
       
 #if JVET_Z0050_CCLM_SLOPE
       if ( PU::isLMCModeEnabled( pu, LM_CHROMA_IDX ) && PU::hasCclmDeltaFlag( pu, LM_CHROMA_IDX ) )
@@ -2256,6 +2302,82 @@ void IntraSearch::estIntraPredChromaQT( CodingUnit &cu, Partitioner &partitioner
 #endif
       }
 
+#if JVET_AA0126_GLM
+      for (int32_t uiMode = 0; uiMode < NUM_LMC_MODE; uiMode++)
+      {
+        int chromaIntraMode = LM_CHROMA_IDX + uiMode;
+        if ( PU::isLMCModeEnabled( pu, chromaIntraMode ) && PU::hasGlmFlag( pu, chromaIntraMode ) )
+        {
+          if ( satdGlmIdcBest[chromaIntraMode - LM_CHROMA_IDX].isActive() )
+          {
+            pu.intraDir[1] = chromaIntraMode;
+            pu.glmIdc      = satdGlmIdcBest[chromaIntraMode - LM_CHROMA_IDX];
+
+            // RD search replicated from above
+            cs.setDecomp( pu.Cb(), false );
+            cs.dist = baseDist;
+            //----- restore context models -----
+            m_CABACEstimator->getCtx() = ctxStart;
+
+            xRecurIntraChromaCodingQT( cs, partitioner, bestCostSoFar, ispType );
+            if( lumaUsesISP && cs.dist == MAX_UINT )
+            {
+              continue;
+            }
+
+            if (cs.sps->getTransformSkipEnabledFlag())
+            {
+              m_CABACEstimator->getCtx() = ctxStart;
+            }
+
+            uint64_t fracBits = xGetIntraFracBitsQT( cs, partitioner, false, true, -1, ispType );
+            Distortion uiDist = cs.dist;
+            double    dCost   = m_pcRdCost->calcRdCost( fracBits, uiDist - baseDist );
+
+            //----- compare -----
+            if( dCost < dBestCost )
+            {
+              if( lumaUsesISP && dCost < bestCostSoFar )
+              {
+                bestCostSoFar = dCost;
+              }
+              for( uint32_t i = getFirstComponentOfChannel( CHANNEL_TYPE_CHROMA ); i < numberValidComponents; i++ )
+              {
+                const CompArea &area = pu.blocks[i];
+
+                saveCS.getRecoBuf     ( area ).copyFrom( cs.getRecoBuf   ( area ) );
+#if KEEP_PRED_AND_RESI_SIGNALS
+                saveCS.getPredBuf     ( area ).copyFrom( cs.getPredBuf   ( area ) );
+                saveCS.getResiBuf     ( area ).copyFrom( cs.getResiBuf   ( area ) );
+#endif
+                saveCS.getPredBuf     ( area ).copyFrom( cs.getPredBuf   (area ) );
+                cs.picture->getPredBuf( area ).copyFrom( cs.getPredBuf   (area ) );
+                cs.picture->getRecoBuf( area ).copyFrom( cs.getRecoBuf( area ) );
+
+                for( uint32_t j = 0; j < saveCS.tus.size(); j++ )
+                {
+                  saveCS.tus[j]->copyComponentFrom( *orgTUs[j], area.compID );
+                }
+              }
+
+              dBestCost       = dCost;
+              uiBestDist      = uiDist;
+              uiBestMode      = chromaIntraMode;
+              bestBDPCMMode   = cu.bdpcmModeChroma;
+              bestGlmIdc      = pu.glmIdc;
+            }
+            
+            if ( chromaIntraMode == LM_CHROMA_IDX && !bestGlmIdc.isActive() )
+            {
+              break;
+            }
+          }
+        }
+      }
+      
+      pu.glmIdc.setAllZero();
+#endif
+
 #if JVET_Z0050_DIMD_CHROMA_FUSION
       // RDO for chroma fusion mode
       for (int32_t uiMode = 0; uiMode < 1; uiMode++)
@@ -2321,6 +2443,9 @@ void IntraSearch::estIntraPredChromaQT( CodingUnit &cu, Partitioner &partitioner
           uiBestMode = chromaIntraMode;
           bestBDPCMMode = cu.bdpcmModeChroma;
           isChromaFusion = pu.isChromaFusion;
+#if JVET_AA0126_GLM
+          bestGlmIdc = pu.glmIdc;
+#endif
         }
       }
       pu.isChromaFusion = false;
@@ -2401,6 +2526,9 @@ void IntraSearch::estIntraPredChromaQT( CodingUnit &cu, Partitioner &partitioner
               bestCclmOffsets = pu.cclmOffsets;
 #if JVET_Z0050_DIMD_CHROMA_FUSION
               isChromaFusion  = pu.isChromaFusion;
+#endif
+#if JVET_AA0126_GLM
+              bestGlmIdc      = pu.glmIdc;
 #endif
             }
           }
@@ -2486,6 +2614,9 @@ void IntraSearch::estIntraPredChromaQT( CodingUnit &cu, Partitioner &partitioner
             bestCclmOffsets = pu.cclmOffsets;
 #endif
             cccmModeBest    = pu.cccmFlag;
+#if JVET_AA0126_GLM
+            bestGlmIdc      = pu.glmIdc;
+#endif 
           }
         }
       }
@@ -2528,6 +2659,9 @@ void IntraSearch::estIntraPredChromaQT( CodingUnit &cu, Partitioner &partitioner
 #endif
 #if JVET_Z0050_DIMD_CHROMA_FUSION
     pu.isChromaFusion = isChromaFusion;
+#endif
+#if JVET_AA0126_GLM
+    pu.glmIdc          = bestGlmIdc;
 #endif
   }
 
@@ -2612,6 +2746,48 @@ void IntraSearch::xFindBestCclmDeltaSlopeSATD(PredictionUnit &pu, ComponentID co
 }
 #endif
 
+#if JVET_AA0126_GLM
+void IntraSearch::xFindBestGlmIdcSATD(PredictionUnit &pu, ComponentID compID, int &idcBest, int64_t &sadBest )
+{
+  CodingStructure& cs = *(pu.cs);
+  CompArea       area = compID == COMPONENT_Cb ? pu.Cb() : pu.Cr();
+  PelBuf       orgBuf = cs.getOrgBuf(area);
+  PelBuf      predBuf = cs.getPredBuf(area);
+  int          maxIdc = NUM_GLM_IDC - 1;
+  int            mode = pu.intraDir[1];
+
+  DistParam distParamSad;
+  DistParam distParamSatd;
+
+  m_pcRdCost->setDistParam(distParamSad,  orgBuf, predBuf, pu.cs->sps->getBitDepth(CHANNEL_TYPE_CHROMA), compID, false);
+  m_pcRdCost->setDistParam(distParamSatd, orgBuf, predBuf, pu.cs->sps->getBitDepth(CHANNEL_TYPE_CHROMA), compID, true);
+  
+  distParamSad.applyWeight  = false;
+  distParamSatd.applyWeight = false;
+  
+  sadBest = -1;
+
+  // Search positive idcs
+  for ( int idc = 0; idc <= maxIdc; idc++ )
+  {
+    pu.glmIdc.setIdc(compID, 0, idc);
+    pu.glmIdc.setIdc(compID, 1, idc);
+
+    predIntraChromaLM( compID, predBuf, pu, area, mode );
+    
+    int64_t sad     = distParamSad.distFunc(distParamSad) * 2;
+    int64_t satd    = distParamSatd.distFunc(distParamSatd);
+    int64_t sadThis = std::min(sad, satd);
+    
+    if ( sadBest == -1 || sadThis < sadBest )
+    {
+      sadBest   = sadThis;
+      idcBest   = idc;
+    }
+  }
+}
+#endif
+
 #if !INTRA_RM_SMALL_BLOCK_SIZE_CONSTRAINTS
 void IntraSearch::saveCuAreaCostInSCIPU( Area area, double cost )
 {
@@ -6770,6 +6946,9 @@ ChromaCbfs IntraSearch::xRecurIntraChromaCodingQT( CodingStructure &cs, Partitio
     {
       xGetLumaRecPixels( pu, cbArea );
       predIntraChromaLM( COMPONENT_Cb, piPredCb, pu, cbArea, predMode );
+#if JVET_AA0126_GLM
+      xGetLumaRecPixels( pu, crArea ); // generate GLM luma samples for Cr prediction
+#endif
       predIntraChromaLM( COMPONENT_Cr, piPredCr, pu, crArea, predMode );
     }
     else if (PU::isMIP(pu, CHANNEL_TYPE_CHROMA))
diff --git a/source/Lib/EncoderLib/IntraSearch.h b/source/Lib/EncoderLib/IntraSearch.h
index 2cd402227ce590389ca828ae66b2c79a52580e53..b3a1280a0877e75078e113a6c95f9ebaee3fb84f 100644
--- a/source/Lib/EncoderLib/IntraSearch.h
+++ b/source/Lib/EncoderLib/IntraSearch.h
@@ -543,6 +543,9 @@ protected:
 #if JVET_Z0050_CCLM_SLOPE
   void xFindBestCclmDeltaSlopeSATD        ( PredictionUnit &pu, ComponentID compID, int cclmModel, int &deltaBest, int64_t &satdBest );
 #endif
+#if JVET_AA0126_GLM
+  void xFindBestGlmIdcSATD                ( PredictionUnit &pu, ComponentID compID, int &idcBest, int64_t &satdBest );
+#endif
 };// END CLASS DEFINITION EncSearch
 
 //! \}