diff --git a/cfg/encoder_lowdelay_P_vtm.cfg b/cfg/encoder_lowdelay_P_vtm.cfg
index bc59427700e7dcbeba9cb66dc684022bfc8de389..4124deae0b70f48abfe4aaa4bdbf4e2f78763d54 100644
--- a/cfg/encoder_lowdelay_P_vtm.cfg
+++ b/cfg/encoder_lowdelay_P_vtm.cfg
@@ -127,6 +127,7 @@ LMChroma                     : 1      # use CCLM only
 DepQuant                     : 1
 IMV                          : 2
 ALF                          : 1
+MHIntra                      : 1
 
 # Fast tools
 PBIntraFast                  : 1
diff --git a/cfg/encoder_lowdelay_vtm.cfg b/cfg/encoder_lowdelay_vtm.cfg
index 9737a2854824f560bfef606585e012ef738f0307..2e9644e55279a9aa6b47d7f7869839f1435a35c2 100644
--- a/cfg/encoder_lowdelay_vtm.cfg
+++ b/cfg/encoder_lowdelay_vtm.cfg
@@ -129,6 +129,7 @@ IMV                          : 2
 ALF                          : 1
 GBi                          : 1 
 GBiFast                      : 1 
+MHIntra                      : 1
 
 # Fast tools
 PBIntraFast                  : 1
diff --git a/cfg/encoder_randomaccess_vtm.cfg b/cfg/encoder_randomaccess_vtm.cfg
index 63ff58df9358f54e7567d150a6e8993a48ca04ea..bd07173bfae4208481be445f6eb35f6a1ab52e41 100644
--- a/cfg/encoder_randomaccess_vtm.cfg
+++ b/cfg/encoder_randomaccess_vtm.cfg
@@ -143,6 +143,7 @@ IMV                          : 2
 ALF                          : 1
 GBi                          : 1 
 GBiFast                      : 1 
+MHIntra                      : 1
 
 # Fast tools
 PBIntraFast                  : 1
diff --git a/source/App/EncoderApp/EncApp.cpp b/source/App/EncoderApp/EncApp.cpp
index 3c6efc53764d3d8d710daab9abefc93c43b8926d..73a5645b272303590a3b1aa2c59f6335dbfa67d4 100644
--- a/source/App/EncoderApp/EncApp.cpp
+++ b/source/App/EncoderApp/EncApp.cpp
@@ -242,6 +242,9 @@ void EncApp::xInitLibCfg()
 #if JVET_L0646_GBI
   m_cEncLib.setUseGBi                                            ( m_GBi );
   m_cEncLib.setUseGBiFast                                        ( m_GBiFast );
+#endif
+#if JVET_L0100_MULTI_HYPOTHESIS_INTRA
+  m_cEncLib.setUseMHIntra                                        ( m_MHIntra );
 #endif
   // ADD_NEW_TOOL : (encoder app) add setting of tool enabling flags and associated parameters here
 
diff --git a/source/App/EncoderApp/EncAppCfg.cpp b/source/App/EncoderApp/EncAppCfg.cpp
index 0b7da7327be4a55d7219ebc19efa06771ef3f98a..f65ef71a760598ccb93c2b3a1a44c2d6cfe15595 100644
--- a/source/App/EncoderApp/EncAppCfg.cpp
+++ b/source/App/EncoderApp/EncAppCfg.cpp
@@ -847,6 +847,9 @@ bool EncAppCfg::parseCfg( int argc, char* argv[] )
 #if JVET_L0646_GBI
   ("GBi",                                             m_GBi,                                            false, "Enable Generalized Bi-prediction(GBi)")
   ("GBiFast",                                         m_GBiFast,                                        false, "Fast methods for Generalized Bi-prediction(GBi)\n")
+#endif
+#if JVET_L0100_MULTI_HYPOTHESIS_INTRA
+  ("MHIntra",                                         m_MHIntra,                                        false, "Enable MHIntra mode")
 #endif
   // ADD_NEW_TOOL : (encoder app) add parsing parameters here
 
@@ -3122,6 +3125,9 @@ void EncAppCfg::xPrintParameter()
 #if JVET_L0646_GBI
     msg( VERBOSE, "GBi:%d ", m_GBi );
     msg( VERBOSE, "GBiFast:%d ", m_GBiFast );
+#endif
+#if JVET_L0100_MULTI_HYPOTHESIS_INTRA
+    msg(VERBOSE, "MHIntra:%d ", m_MHIntra);
 #endif
   }
   // ADD_NEW_TOOL (add some output indicating the usage of tools)
diff --git a/source/App/EncoderApp/EncAppCfg.h b/source/App/EncoderApp/EncAppCfg.h
index a9eb00f22b16e1ccb7a754209c6b9110af5a4229..c1b61937f00af454b2e2340644c826c9167b4b05 100644
--- a/source/App/EncoderApp/EncAppCfg.h
+++ b/source/App/EncoderApp/EncAppCfg.h
@@ -225,6 +225,10 @@ protected:
   bool      m_GBi;
   bool      m_GBiFast;
 #endif
+
+#if JVET_L0100_MULTI_HYPOTHESIS_INTRA
+  bool      m_MHIntra;
+#endif
   // ADD_NEW_TOOL : (encoder app) add tool enabling flags and associated parameters here
 
   unsigned  m_uiMaxCUWidth;                                   ///< max. CU width in pixel
diff --git a/source/Lib/CommonLib/CodingStatistics.h b/source/Lib/CommonLib/CodingStatistics.h
index be99dbbe7dc88237915100a14a634d2dcc445f33..3c82f4abe427a601accff05b0727b5253c64edec 100644
--- a/source/Lib/CommonLib/CodingStatistics.h
+++ b/source/Lib/CommonLib/CodingStatistics.h
@@ -106,6 +106,9 @@ enum CodingStatisticsType
   STATS__CABAC_BITS__EMT_CU_FLAG,
   STATS__CABAC_BITS__EMT_TU_INDEX,
   STATS__TOOL_EMT,
+#if JVET_L0100_MULTI_HYPOTHESIS_INTRA
+  STATS__CABAC_BITS__MH_INTRA_FLAG,
+#endif
   STATS__TOOL_TOTAL,
   STATS__NUM_STATS
 };
@@ -178,6 +181,9 @@ static inline const char* getName(CodingStatisticsType name)
 #endif
     "CABAC_BITS__EMT_CU_FLAG",
     "CABAC_BITS__EMT_TU_INDX",
+#if JVET_L0100_MULTI_HYPOTHESIS_INTRA
+    "CABAC_BITS__MH_INTRA_FLAG",
+#endif
     "CABAC_BITS__OTHER",
     "CABAC_BITS__INVALID",
     "TOOL_FRAME",
diff --git a/source/Lib/CommonLib/Contexts.cpp b/source/Lib/CommonLib/Contexts.cpp
index 434e66e89c9de59b81126a4def2fbfdc28370c46..ce7b6d551e03936a0f1f6ea4d49f50fb4da51e6c 100644
--- a/source/Lib/CommonLib/Contexts.cpp
+++ b/source/Lib/CommonLib/Contexts.cpp
@@ -777,6 +777,21 @@ const CtxSet ContextSetCfg::ctbAlfFlag =
     } )
 };
 
+#if JVET_L0100_MULTI_HYPOTHESIS_INTRA
+const CtxSet ContextSetCfg::MHIntraFlag = ContextSetCfg::addCtxSet
+({
+  { 154, },
+  { 110, },
+  { CNU, },
+});
+
+const CtxSet ContextSetCfg::MHIntraPredMode = ContextSetCfg::addCtxSet
+({
+  { 183, CNU, CNU, CNU, },
+  { 154, CNU, CNU, CNU, },
+  { 184, CNU, CNU, CNU, },
+});
+#endif
 
 const unsigned ContextSetCfg::NumberOfContexts = (unsigned)ContextSetCfg::sm_InitTables[0].size();
 
diff --git a/source/Lib/CommonLib/Contexts.h b/source/Lib/CommonLib/Contexts.h
index 3e4679c06bd64c00f0f7611e8ca52e739902a135..d9f2538be3971446205a6c3a1b63123bcea2eaf0 100644
--- a/source/Lib/CommonLib/Contexts.h
+++ b/source/Lib/CommonLib/Contexts.h
@@ -195,6 +195,10 @@ public:
   static const CtxSet   GBiIdx;
 #endif
   static const CtxSet   ctbAlfFlag;
+#if JVET_L0100_MULTI_HYPOTHESIS_INTRA
+  static const CtxSet   MHIntraFlag;
+  static const CtxSet   MHIntraPredMode;
+#endif
   static const unsigned NumberOfContexts;
 
   // combined sets for less complex copying
diff --git a/source/Lib/CommonLib/IntraPrediction.cpp b/source/Lib/CommonLib/IntraPrediction.cpp
index 45a66514af173d329092688d2a6d9e9691f31022..a71f12c4a7d903ae8613704ea34e3cd7330e3162 100644
--- a/source/Lib/CommonLib/IntraPrediction.cpp
+++ b/source/Lib/CommonLib/IntraPrediction.cpp
@@ -141,6 +141,15 @@ IntraPrediction::IntraPrediction()
       m_piYuvExt[ch][buf] = nullptr;
     }
   }
+#if JVET_L0100_MULTI_HYPOTHESIS_INTRA
+  for (uint32_t ch = 0; ch < MAX_NUM_COMPONENT; ch++)
+  {
+    for (uint32_t buf = 0; buf < 4; buf++)
+    {
+      m_yuvExt2[ch][buf] = nullptr;
+    }
+  }
+#endif
 
   m_piTemp = nullptr;
 }
@@ -160,6 +169,16 @@ void IntraPrediction::destroy()
       m_piYuvExt[ch][buf] = nullptr;
     }
   }
+#if JVET_L0100_MULTI_HYPOTHESIS_INTRA
+  for (uint32_t ch = 0; ch < MAX_NUM_COMPONENT; ch++)
+  {
+    for (uint32_t buf = 0; buf < 4; buf++)
+    {
+      delete[] m_yuvExt2[ch][buf];
+      m_yuvExt2[ch][buf] = nullptr;
+    }
+  }
+#endif
 
   delete[] m_piTemp;
   m_piTemp = nullptr;
@@ -173,6 +192,13 @@ void IntraPrediction::init(ChromaFormat chromaFormatIDC, const unsigned bitDepth
     destroy();
   }
 
+#if JVET_L0100_MULTI_HYPOTHESIS_INTRA
+  if (m_yuvExt2[COMPONENT_Y][0] != nullptr && m_currChromaFormat != chromaFormatIDC)
+  {
+    destroy();
+  }
+#endif
+
   m_currChromaFormat = chromaFormatIDC;
 
   if (m_piYuvExt[COMPONENT_Y][PRED_BUF_UNFILTERED] == nullptr) // check if first is null (in which case, nothing initialised yet)
@@ -188,6 +214,21 @@ void IntraPrediction::init(ChromaFormat chromaFormatIDC, const unsigned bitDepth
     }
   }
 
+#if JVET_L0100_MULTI_HYPOTHESIS_INTRA
+  if (m_yuvExt2[COMPONENT_Y][0] == nullptr) // check if first is null (in which case, nothing initialised yet)
+  {
+    m_yuvExtSize2 = (MAX_CU_SIZE) * (MAX_CU_SIZE);
+
+    for (uint32_t ch = 0; ch < MAX_NUM_COMPONENT; ch++)
+    {
+      for (uint32_t buf = 0; buf < 4; buf++)
+      {
+        m_yuvExt2[ch][buf] = new Pel[m_yuvExtSize2];
+      }
+    }
+  }
+#endif
+
   int shift = bitDepthY + 4;
   for (int i = 32; i < 64; i++)
   {
@@ -796,6 +837,133 @@ bool IntraPrediction::useDPCMForFirstPassIntraEstimation(const PredictionUnit &p
   return CU::isRDPCMEnabled(*pu.cu) && pu.cu->transQuantBypass && (uiDirMode == HOR_IDX || uiDirMode == VER_IDX);
 }
 
+#if JVET_L0100_MULTI_HYPOTHESIS_INTRA
+void IntraPrediction::geneWeightedPred(const ComponentID compId, PelBuf &pred, const PredictionUnit &pu, Pel *srcBuf)
+{
+  const int            width = pred.width;
+  const int            height = pred.height;
+  const int            srcStride = width;
+  const int            dstStride = pred.stride;
+
+  const uint32_t       dirMode = PU::getFinalIntraMode(pu, toChannelType(compId));
+  const ClpRng&        clpRng(pu.cu->cs->slice->clpRng(compId));
+  Pel*                 dstBuf = pred.buf;
+  int                  k, l;
+
+  bool                 modeDC = (dirMode <= DC_IDX);
+  Pel                  wIntra1 = 6, wInter1 = 2, wIntra2 = 5, wInter2 = 3, wIntra3 = 3, wInter3 = 5, wIntra4 = 2, wInter4 = 6;
+
+  if (modeDC || width < 4 || height < 4)
+  {
+    for (k = 0; k<height; k++)
+    {
+      for (l = 0; l<width; l++)
+      {
+        dstBuf[k*dstStride + l] = ClipPel((((dstBuf[k*dstStride + l] * 4) + (srcBuf[k*srcStride + l] * 4)) >> 3), clpRng);
+      }
+    }
+  }
+  else
+  {
+    if (dirMode <= DIA_IDX)
+    {
+      int interval = (width >> 2);
+
+      for (k = 0; k<height; k++)
+      {
+        for (l = 0; l<width; l++)
+        {
+          if (l<interval)
+          {
+            dstBuf[k*dstStride + l] = ClipPel((((dstBuf[k*dstStride + l] * wInter1) + (srcBuf[k*srcStride + l] * wIntra1)) >> 3), clpRng);
+          }
+          else if (l >= interval && l < (2 * interval))
+          {
+            dstBuf[k*dstStride + l] = ClipPel((((dstBuf[k*dstStride + l] * wInter2) + (srcBuf[k*srcStride + l] * wIntra2)) >> 3), clpRng);
+          }
+          else if (l >= (interval * 2) && l < (3 * interval))
+          {
+            dstBuf[k*dstStride + l] = ClipPel((((dstBuf[k*dstStride + l] * wInter3) + (srcBuf[k*srcStride + l] * wIntra3)) >> 3), clpRng);
+          }
+          else
+          {
+            dstBuf[k*dstStride + l] = ClipPel((((dstBuf[k*dstStride + l] * wInter4) + (srcBuf[k*srcStride + l] * wIntra4)) >> 3), clpRng);
+          }
+        }
+      }
+    }
+    else
+    {
+      int interval = (height >> 2);
+      for (k = 0; k<height; k++)
+      {
+        for (l = 0; l<width; l++)
+        {
+          if (k<interval)
+          {
+            dstBuf[k*dstStride + l] = ClipPel((((dstBuf[k*dstStride + l] * wInter1) + (srcBuf[k*srcStride + l] * wIntra1)) >> 3), clpRng);
+          }
+          else if (k >= interval && k < (2 * interval))
+          {
+            dstBuf[k*dstStride + l] = ClipPel((((dstBuf[k*dstStride + l] * wInter2) + (srcBuf[k*srcStride + l] * wIntra2)) >> 3), clpRng);
+          }
+          else if (k >= (interval * 2) && k < (3 * interval))
+          {
+            dstBuf[k*dstStride + l] = ClipPel((((dstBuf[k*dstStride + l] * wInter3) + (srcBuf[k*srcStride + l] * wIntra3)) >> 3), clpRng);
+          }
+          else
+          {
+            dstBuf[k*dstStride + l] = ClipPel((((dstBuf[k*dstStride + l] * wInter4) + (srcBuf[k*srcStride + l] * wIntra4)) >> 3), clpRng);
+          }
+        }
+      }
+    }
+  }
+}
+void IntraPrediction::switchBuffer(const PredictionUnit &pu, ComponentID compID, PelBuf srcBuff, Pel *dst)
+{
+  Pel  *src = srcBuff.bufAt(0, 0);
+  int compWidth = compID == COMPONENT_Y ? pu.Y().width : pu.Cb().width;
+  int compHeight = compID == COMPONENT_Y ? pu.Y().height : pu.Cb().height;
+  for (int i = 0; i < compHeight; i++)
+  {
+    for (int j = 0; j < compWidth; j++)
+    {
+      dst[j] = src[j];
+    }
+    src += srcBuff.stride;
+    dst += compWidth;
+  }
+}
+
+void IntraPrediction::geneIntrainterPred(const CodingUnit &cu)
+{
+  if (!cu.firstPU->MHIntraFlag)
+  {
+    return;
+  }
+
+  const PredictionUnit* pu = cu.firstPU;
+
+  bool isUseFilter = IntraPrediction::useFilteredIntraRefSamples(COMPONENT_Y, *pu, true, *pu);
+  initIntraPatternChType(cu, pu->Y(), isUseFilter);
+  predIntraAng(COMPONENT_Y, cu.cs->getPredBuf(*pu).Y(), *pu, isUseFilter);
+  isUseFilter = IntraPrediction::useFilteredIntraRefSamples(COMPONENT_Cb, *pu, true, *pu);
+  initIntraPatternChType(cu, pu->Cb(), isUseFilter);
+  predIntraAng(COMPONENT_Cb, cu.cs->getPredBuf(*pu).Cb(), *pu, isUseFilter);
+  isUseFilter = IntraPrediction::useFilteredIntraRefSamples(COMPONENT_Cr, *pu, true, *pu);
+  initIntraPatternChType(cu, pu->Cr(), isUseFilter);
+  predIntraAng(COMPONENT_Cr, cu.cs->getPredBuf(*pu).Cr(), *pu, isUseFilter);
+
+  for (int currCompID = 0; currCompID < 3; currCompID++)
+  {
+    ComponentID currCompID2 = (ComponentID)currCompID;
+    PelBuf tmpBuf = currCompID == 0 ? cu.cs->getPredBuf(*pu).Y() : (currCompID == 1 ? cu.cs->getPredBuf(*pu).Cb() : cu.cs->getPredBuf(*pu).Cr());
+    switchBuffer(*pu, currCompID2, tmpBuf, getPredictorPtr2(currCompID2, 0));
+  }
+}
+#endif
+
 inline bool isAboveLeftAvailable  ( const CodingUnit &cu, const ChannelType &chType, const Position &posLT );
 inline int  isAboveAvailable      ( const CodingUnit &cu, const ChannelType &chType, const Position &posLT, const uint32_t uiNumUnitsInPU, const uint32_t unitWidth, bool *validFlags );
 inline int  isLeftAvailable       ( const CodingUnit &cu, const ChannelType &chType, const Position &posLT, const uint32_t uiNumUnitsInPU, const uint32_t unitWidth, bool *validFlags );
diff --git a/source/Lib/CommonLib/IntraPrediction.h b/source/Lib/CommonLib/IntraPrediction.h
index 9d8002a8ce3d02c536ae836cb7c4fb8202fd241f..856a145bf42a37a9a1ca394d838c94211c061bbb 100644
--- a/source/Lib/CommonLib/IntraPrediction.h
+++ b/source/Lib/CommonLib/IntraPrediction.h
@@ -69,6 +69,10 @@ private:
   Pel* m_piYuvExt[MAX_NUM_COMPONENT][NUM_PRED_BUF];
   int  m_iYuvExtSize;
 
+#if JVET_L0100_MULTI_HYPOTHESIS_INTRA
+  Pel* m_yuvExt2[MAX_NUM_COMPONENT][4];
+  int  m_yuvExtSize2;
+#endif
 
   static const uint8_t m_aucIntraFilter[MAX_NUM_CHANNEL_TYPE][MAX_INTRA_FILTER_DEPTHS];
 
@@ -126,6 +130,13 @@ public:
 
 static bool useFilteredIntraRefSamples( const ComponentID &compID, const PredictionUnit &pu, bool modeSpecific, const UnitArea &tuArea );
   static bool useDPCMForFirstPassIntraEstimation(const PredictionUnit &pu, const uint32_t &uiDirMode);
+
+#if JVET_L0100_MULTI_HYPOTHESIS_INTRA
+  void geneWeightedPred           (const ComponentID compId, PelBuf &pred, const PredictionUnit &pu, Pel *srcBuf);
+  Pel* getPredictorPtr2           (const ComponentID compID, uint32_t idx) { return m_yuvExt2[compID][idx]; }
+  void switchBuffer               (const PredictionUnit &pu, ComponentID compID, PelBuf srcBuff, Pel *dst);
+  void geneIntrainterPred         (const CodingUnit &cu);
+#endif
 };
 
 //! \}
diff --git a/source/Lib/CommonLib/Rom.cpp b/source/Lib/CommonLib/Rom.cpp
index 8a2ac97f5aa77a099147081b89a9110d5130b39a..d85c6668b62b17c32bd8c97d9023ac71d3bbffc9 100644
--- a/source/Lib/CommonLib/Rom.cpp
+++ b/source/Lib/CommonLib/Rom.cpp
@@ -353,7 +353,11 @@ void initROM()
   // g_aucLog2[ x ]: log2(x), if x=1 -> 0, x=2 -> 1, x=4 -> 2, x=8 -> 3, x=16 -> 4, ...
   ::memset(g_aucLog2, 0, sizeof(g_aucLog2));
   c = 0;
+#if JVET_L0100_MULTI_HYPOTHESIS_INTRA
+  for( int i = 0, n = 0; i <= (1 << (MAX_CU_DEPTH + 1)); i++ )
+#else
   for( int i = 0, n = 0; i <= MAX_CU_SIZE; i++ )
+#endif
   {
     g_aucNextLog2[i] = i <= 1 ? 0 : c + 1;
 
@@ -697,9 +701,15 @@ const DecisionTreeTemplate g_mtSplitDTT = compile(
 // ====================================================================================================================
 SizeIndexInfo*           gp_sizeIdxInfo = NULL;
 int                      g_BlockSizeTrafoScale[MAX_CU_SIZE + 1][MAX_CU_SIZE + 1][2];
+#if JVET_L0100_MULTI_HYPOTHESIS_INTRA
+int8_t                    g_aucLog2    [(1 << (MAX_CU_DEPTH + 1)) + 1];
+int8_t                    g_aucNextLog2[(1 << (MAX_CU_DEPTH + 1)) + 1];
+int8_t                    g_aucPrevLog2[(1 << (MAX_CU_DEPTH + 1)) + 1];
+#else
 int8_t                    g_aucLog2    [MAX_CU_SIZE + 1];
 int8_t                    g_aucNextLog2[MAX_CU_SIZE + 1];
 int8_t                    g_aucPrevLog2[MAX_CU_SIZE + 1];
+#endif
 
 UnitScale g_miScaling( MIN_CU_LOG2, MIN_CU_LOG2 );
 
diff --git a/source/Lib/CommonLib/Rom.h b/source/Lib/CommonLib/Rom.h
index afbb242da407759e18c6aeeb871eb063a13e4678..ff5de8fd7f0acb28ad9c69a9a37ea1bd779c47b1 100644
--- a/source/Lib/CommonLib/Rom.h
+++ b/source/Lib/CommonLib/Rom.h
@@ -165,9 +165,15 @@ extern const DecisionTreeTemplate g_qtbtSplitDTT;
 // ====================================================================================================================
 extern SizeIndexInfo* gp_sizeIdxInfo;
 extern int            g_BlockSizeTrafoScale           [MAX_CU_SIZE + 1][MAX_CU_SIZE + 1][2];
+#if JVET_L0100_MULTI_HYPOTHESIS_INTRA
+extern int8_t          g_aucLog2            [(1 << (MAX_CU_DEPTH + 1)) + 1];
+extern int8_t          g_aucNextLog2        [(1 << (MAX_CU_DEPTH + 1)) + 1];
+extern int8_t          g_aucPrevLog2        [(1 << (MAX_CU_DEPTH + 1)) + 1];
+#else
 extern int8_t          g_aucLog2                       [MAX_CU_SIZE + 1];
 extern int8_t          g_aucNextLog2        [MAX_CU_SIZE + 1];
 extern int8_t          g_aucPrevLog2        [MAX_CU_SIZE + 1];
+#endif
 extern const int8_t    i2Log2Tab[257];
 
 inline bool is34( const SizeType& size )
diff --git a/source/Lib/CommonLib/Slice.cpp b/source/Lib/CommonLib/Slice.cpp
index 6023e01bc877a3e7a47d9a804bb897eb9b9e5323..f7fe9121b7231759861b20df52b4b5e35ff1293e 100644
--- a/source/Lib/CommonLib/Slice.cpp
+++ b/source/Lib/CommonLib/Slice.cpp
@@ -1640,6 +1640,9 @@ SPSNext::SPSNext( SPS& sps )
   , m_Affine                    ( false )
   , m_AffineType                ( false )
   , m_MTTEnabled                ( false )
+#if JVET_L0100_MULTI_HYPOTHESIS_INTRA
+  , m_MHIntra                   ( false )
+#endif
 #if ENABLE_WPP_PARALLELISM
   , m_NextDQP                   ( false )
 #endif
diff --git a/source/Lib/CommonLib/Slice.h b/source/Lib/CommonLib/Slice.h
index d6ef610c7304e8361981d2524201642290e29fd9..53031e977d979db5dff1c4fbcecf7b0668977642 100644
--- a/source/Lib/CommonLib/Slice.h
+++ b/source/Lib/CommonLib/Slice.h
@@ -814,6 +814,9 @@ private:
   bool              m_GBi;                        //
 #endif
   bool              m_MTTEnabled;                 //
+#if JVET_L0100_MULTI_HYPOTHESIS_INTRA
+  bool              m_MHIntra;
+#endif
 #if ENABLE_WPP_PARALLELISM
   bool              m_NextDQP;
 #endif
@@ -921,6 +924,11 @@ public:
 
   void      setUseCompositeRef(bool b) { m_compositeRefEnabled = b; }
   bool      getUseCompositeRef()                                      const { return m_compositeRefEnabled; }
+
+#if JVET_L0100_MULTI_HYPOTHESIS_INTRA
+  void      setUseMHIntra         ( bool b )                                        { m_MHIntra = b; }
+  bool      getUseMHIntra         ()                                      const     { return m_MHIntra; }
+#endif
   // ADD_NEW_TOOL : (sps extension) add access functions for tool enabling flags and associated parameters here
 
 };
diff --git a/source/Lib/CommonLib/TypeDef.h b/source/Lib/CommonLib/TypeDef.h
index 200162158bc6ef93b6edf73ce576cf2c8c19a69b..695ab7031aee892a4cc1e35f0dfe6c6c17b3271d 100644
--- a/source/Lib/CommonLib/TypeDef.h
+++ b/source/Lib/CommonLib/TypeDef.h
@@ -50,6 +50,8 @@
 #include <assert.h>
 #include <cassert>
 
+#define JVET_L0100_MULTI_HYPOTHESIS_INTRA                 1 // Combine intra mode with an extra merge indexed prediction
+
 #define JVET_L0553_FIX_INITQP                             1
 
 #define JVET_L0147_ALF_SUBSAMPLED_LAPLACIAN               1 // Subsampled Laplacian calculation
diff --git a/source/Lib/CommonLib/Unit.cpp b/source/Lib/CommonLib/Unit.cpp
index 6152c93f57b58f37865eb29ad338f314b8b6babd..6965bbb77f8a1f2aaddde66fab7570ac25d9cb14 100644
--- a/source/Lib/CommonLib/Unit.cpp
+++ b/source/Lib/CommonLib/Unit.cpp
@@ -331,6 +331,11 @@ void PredictionUnit::initData()
       mvdAffi[i][j].setZero();
     }
   }
+#if JVET_L0100_MULTI_HYPOTHESIS_INTRA
+  MHIntraFlag = false;
+  intraDir2[0] = PLANAR_IDX;
+  intraDir2[1] = DM_CHROMA_IDX;
+#endif
 }
 
 PredictionUnit& PredictionUnit::operator=(const IntraPredictionData& predData)
@@ -361,6 +366,11 @@ PredictionUnit& PredictionUnit::operator=(const InterPredictionData& predData)
       mvdAffi[i][j] = predData.mvdAffi[i][j];
     }
   }
+#if JVET_L0100_MULTI_HYPOTHESIS_INTRA
+  MHIntraFlag = predData.MHIntraFlag;
+  intraDir2[0] = predData.intraDir2[0];
+  intraDir2[1] = predData.intraDir2[1];
+#endif
 
   return *this;
 }
@@ -388,6 +398,11 @@ PredictionUnit& PredictionUnit::operator=( const PredictionUnit& other )
       mvdAffi[i][j] = other.mvdAffi[i][j];
     }
   }
+#if JVET_L0100_MULTI_HYPOTHESIS_INTRA
+  MHIntraFlag = other.MHIntraFlag;
+  intraDir2[0] = other.intraDir2[0];
+  intraDir2[1] = other.intraDir2[1];
+#endif
 
   return *this;
 }
diff --git a/source/Lib/CommonLib/Unit.h b/source/Lib/CommonLib/Unit.h
index 93924a20065a649ddfe4e80adaf1b4553766616c..e0c850ce39c71254f141fea1bfc33217bc0c01b0 100644
--- a/source/Lib/CommonLib/Unit.h
+++ b/source/Lib/CommonLib/Unit.h
@@ -357,6 +357,10 @@ struct InterPredictionData
   int16_t     refIdx  [NUM_REF_PIC_LIST_01];
   MergeType mergeType;
   Mv        mvdAffi [NUM_REF_PIC_LIST_01][3];
+#if JVET_L0100_MULTI_HYPOTHESIS_INTRA
+  bool      MHIntraFlag;
+  uint32_t  intraDir2[MAX_NUM_CHANNEL_TYPE];
+#endif
 };
 
 struct PredictionUnit : public UnitArea, public IntraPredictionData, public InterPredictionData
diff --git a/source/Lib/CommonLib/UnitTools.cpp b/source/Lib/CommonLib/UnitTools.cpp
index 065e4fe39742efeb66e7cff63714dc3909d5d84f..3a4ec7bad57529facf17b880fb14af334227d530 100644
--- a/source/Lib/CommonLib/UnitTools.cpp
+++ b/source/Lib/CommonLib/UnitTools.cpp
@@ -309,22 +309,42 @@ int PU::getIntraMPMs( const PredictionUnit &pu, unsigned* mpm, const ChannelType
     // Get intra direction of left PU
     const PredictionUnit *puLeft = pu.cs->getPURestricted(pos.offset(-1, 0), pu, channelType);
 
+#if JVET_L0100_MULTI_HYPOTHESIS_INTRA
+    if (puLeft && (CU::isIntra(*puLeft->cu) || (channelType == CHANNEL_TYPE_LUMA && puLeft->MHIntraFlag)))
+#else
     if (puLeft && CU::isIntra(*puLeft->cu))
+#endif
     {
+#if JVET_L0100_MULTI_HYPOTHESIS_INTRA
+      leftIntraDir = puLeft->MHIntraFlag ? puLeft->intraDir2[channelType] : puLeft->intraDir[channelType];
+#else
       leftIntraDir = puLeft->intraDir[channelType];
+#endif
 
       if (isChroma(channelType) && leftIntraDir == DM_CHROMA_IDX)
       {
+#if JVET_L0100_MULTI_HYPOTHESIS_INTRA
+        leftIntraDir = puLeft->MHIntraFlag ? puLeft->intraDir2[0] : puLeft->intraDir[0];
+#else
         leftIntraDir = puLeft->intraDir[0];
+#endif
       }
     }
 
     // Get intra direction of above PU
     const PredictionUnit *puAbove = pu.cs->getPURestricted(pos.offset(0, -1), pu, channelType);
 
+#if JVET_L0100_MULTI_HYPOTHESIS_INTRA
+    if (puAbove && (CU::isIntra(*puAbove->cu) || (channelType == CHANNEL_TYPE_LUMA && puAbove->MHIntraFlag)) && CU::isSameCtu(*pu.cu, *puAbove->cu))
+#else
     if (puAbove && CU::isIntra(*puAbove->cu) && CU::isSameCtu(*pu.cu, *puAbove->cu))
+#endif
     {
+#if JVET_L0100_MULTI_HYPOTHESIS_INTRA
+      aboveIntraDir = puAbove->MHIntraFlag ? puAbove->intraDir2[channelType] : puAbove->intraDir[channelType];
+#else
       aboveIntraDir = puAbove->intraDir[channelType];
+#endif
 
       if (isChroma(channelType) && aboveIntraDir == DM_CHROMA_IDX)
       {
@@ -478,14 +498,168 @@ bool PU::isChromaIntraModeCrossCheckMode( const PredictionUnit &pu )
   return pu.intraDir[CHANNEL_TYPE_CHROMA] == DM_CHROMA_IDX;
 }
 
+#if JVET_L0100_MULTI_HYPOTHESIS_INTRA
+int PU::getMHIntraMPMs(const PredictionUnit &pu, unsigned* mpm, const ChannelType &channelType /*= CHANNEL_TYPE_LUMA*/, const bool isChromaMDMS /*= false*/, const unsigned startIdx /*= 0*/)
+{
+  const unsigned numMPMs = 3;
+  {
+    int numCand = -1;
+    uint32_t leftIntraDir = DC_IDX, aboveIntraDir = DC_IDX;
+
+    const CompArea& area = pu.block(getFirstComponentOfChannel(channelType));
+    const Position& pos = area.pos();
+
+    // Get intra direction of left PU
+    const PredictionUnit *puLeft = pu.cs->getPURestricted(pos.offset(-1, 0), pu, channelType);
+
+    if (puLeft && (CU::isIntra(*puLeft->cu) || puLeft->MHIntraFlag))
+    {
+      leftIntraDir = puLeft->MHIntraFlag ? puLeft->intraDir2[channelType] : puLeft->intraDir[channelType];
+
+      if (isChroma(channelType) && leftIntraDir == DM_CHROMA_IDX)
+      {
+        leftIntraDir = puLeft->MHIntraFlag ? puLeft->intraDir2[0] : puLeft->intraDir[0];
+      }
+    }
+
+    // Get intra direction of above PU
+    const PredictionUnit* puAbove = pu.cs->getPURestricted(pos.offset(0, -1), pu, channelType);
+
+    if (puAbove && (CU::isIntra(*puAbove->cu) || puAbove->MHIntraFlag) && CU::isSameCtu(*pu.cu, *puAbove->cu))
+    {
+      aboveIntraDir = puAbove->MHIntraFlag ? puAbove->intraDir2[channelType] : puAbove->intraDir[channelType];
+
+      if (isChroma(channelType) && aboveIntraDir == DM_CHROMA_IDX)
+      {
+        aboveIntraDir = puAbove->MHIntraFlag ? puAbove->intraDir2[0] : puAbove->intraDir[0];
+      }
+    }
+
+    CHECK(2 >= numMPMs, "Invalid number of most probable modes");
+
+    uint32_t leftIntraDir2 = leftIntraDir;
+    uint32_t aboveIntraDir2 = aboveIntraDir;
+
+    leftIntraDir2 = (leftIntraDir2 > DC_IDX) ? ((leftIntraDir2 <= DIA_IDX) ? HOR_IDX : VER_IDX) : leftIntraDir2;
+    aboveIntraDir2 = (aboveIntraDir2 > DC_IDX) ? ((aboveIntraDir2 <= DIA_IDX) ? HOR_IDX : VER_IDX) : aboveIntraDir2;
+
+    if (leftIntraDir2 == aboveIntraDir2)
+    {
+      numCand = 1;
+
+      if (leftIntraDir2 > DC_IDX) // angular modes
+      {
+        mpm[0] = leftIntraDir2;
+        mpm[1] = PLANAR_IDX;
+        mpm[2] = DC_IDX;
+      }
+      else //non-angular
+      {
+        mpm[0] = PLANAR_IDX;
+        mpm[1] = DC_IDX;
+        mpm[2] = VER_IDX;
+      }
+    }
+    else
+    {
+      numCand = 2;
+
+      mpm[0] = leftIntraDir2;
+      mpm[1] = aboveIntraDir2;
+
+      if (leftIntraDir2 && aboveIntraDir2) //both modes are non-planar
+      {
+        mpm[2] = PLANAR_IDX;
+      }
+      else
+      {
+        mpm[2] = (leftIntraDir2 + aboveIntraDir2) < 2 ? VER_IDX : DC_IDX;
+      }
+    }
+    int narrowCase = getNarrowShape(pu.lwidth(), pu.lheight());
+    if (narrowCase > 0)
+    {
+      bool isMPM[NUM_LUMA_MODE];
+      for (int idx = 0; idx < NUM_LUMA_MODE; idx++)
+      {
+        isMPM[idx] = false;
+      }
+      for (int idx = 0; idx < numMPMs; idx++)
+      {
+        isMPM[mpm[idx]] = true;
+      }
+      if (narrowCase == 1 && isMPM[HOR_IDX])
+      {
+        for (int idx = 0; idx < numMPMs; idx++)
+        {
+          if (mpm[idx] == HOR_IDX)
+          {
+            if (!isMPM[PLANAR_IDX])
+              mpm[idx] = PLANAR_IDX;
+            else if (!isMPM[DC_IDX])
+              mpm[idx] = DC_IDX;
+            else if (!isMPM[VER_IDX])
+              mpm[idx] = VER_IDX;
+            break;
+          }
+        }
+      }
+      if (narrowCase == 2 && isMPM[VER_IDX])
+      {
+        for (int idx = 0; idx < numMPMs; idx++)
+        {
+          if (mpm[idx] == VER_IDX)
+          {
+            if (!isMPM[PLANAR_IDX])
+              mpm[idx] = PLANAR_IDX;
+            else if (!isMPM[DC_IDX])
+              mpm[idx] = DC_IDX;
+            else if (!isMPM[HOR_IDX])
+              mpm[idx] = HOR_IDX;
+            break;
+          }
+        }
+      }
+    }
+    CHECK(numCand == 0, "No candidates found");
+    CHECK(mpm[0] == mpm[1] || mpm[0] == mpm[2] || mpm[2] == mpm[1], "redundant MPM");
+    return numCand;
+  }
+}
+int PU::getNarrowShape(const int width, const int height)
+{
+  int longSide = (width > height) ? width : height;
+  int shortSide = (width > height) ? height : width;
+  if (longSide > (2 * shortSide))
+  {
+    if (longSide == width)
+      return 1;
+    else
+      return 2;
+  }
+  else
+  {
+    return 0;
+  }
+}
+#endif
+
 uint32_t PU::getFinalIntraMode( const PredictionUnit &pu, const ChannelType &chType )
 {
+#if JVET_L0100_MULTI_HYPOTHESIS_INTRA
+  uint32_t uiIntraMode = pu.MHIntraFlag ? pu.intraDir2[chType] : pu.intraDir[chType];
+#else
   uint32_t uiIntraMode = pu.intraDir[chType];
+#endif
 
   if( uiIntraMode == DM_CHROMA_IDX && !isLuma( chType ) )
   {
     const PredictionUnit &lumaPU = CS::isDualITree( *pu.cs ) ? *pu.cs->picture->cs->getPU( pu.blocks[chType].lumaPos(), CHANNEL_TYPE_LUMA ) : *pu.cs->getPU( pu.blocks[chType].lumaPos(), CHANNEL_TYPE_LUMA );
+#if JVET_L0100_MULTI_HYPOTHESIS_INTRA
+    uiIntraMode = pu.MHIntraFlag ? pu.intraDir2[0] : lumaPU.intraDir[0];
+#else 
     uiIntraMode = lumaPU.intraDir[0];
+#endif
   }
   if( pu.chromaFormat == CHROMA_422 && !isLuma( chType ) )
   {
diff --git a/source/Lib/CommonLib/UnitTools.h b/source/Lib/CommonLib/UnitTools.h
index 3247170c6f357a977fdc39d16be213adee1a28ee..9a01baa46eb750e86e499c4f8a342572271c1c34 100644
--- a/source/Lib/CommonLib/UnitTools.h
+++ b/source/Lib/CommonLib/UnitTools.h
@@ -143,6 +143,10 @@ namespace PU
   bool isLMCMode                      (                          unsigned mode);
   bool isLMCModeEnabled               (const PredictionUnit &pu, unsigned mode);
   bool isChromaIntraModeCrossCheckMode(const PredictionUnit &pu);
+#if JVET_L0100_MULTI_HYPOTHESIS_INTRA
+  int  getMHIntraMPMs                 (const PredictionUnit &pu, unsigned *mpm, const ChannelType &channelType = CHANNEL_TYPE_LUMA, const bool isChromaMDMS = false, const unsigned startIdx = 0);
+  int  getNarrowShape                 (const int width, const int height);
+#endif
 }
 
 // TU tools
@@ -213,6 +217,47 @@ uint32_t updateCandList( T uiMode, double uiCost, static_vector<T, N>& candModeL
   return 0;
 }
 
+#if JVET_L0100_MULTI_HYPOTHESIS_INTRA
+template<typename T, size_t N>
+uint32_t updateDoubleCandList(T mode, double cost, static_vector<T, N>& candModeList, static_vector<double, N>& candCostList, static_vector<T, N>& candModeList2, T mode2, size_t fastCandNum = N)
+{
+  CHECK(std::min(fastCandNum, candModeList.size()) != std::min(fastCandNum, candCostList.size()), "Sizes do not match!");
+  CHECK(fastCandNum > candModeList.capacity(), "The vector is to small to hold all the candidates!");
+
+  size_t i;
+  size_t shift = 0;
+  size_t currSize = std::min(fastCandNum, candCostList.size());
+
+  while (shift < fastCandNum && shift < currSize && cost < candCostList[currSize - 1 - shift])
+  {
+    shift++;
+  }
+
+  if (candModeList.size() >= fastCandNum && shift != 0)
+  {
+    for (i = 1; i < shift; i++)
+    {
+      candModeList[currSize - i] = candModeList[currSize - 1 - i];
+      candModeList2[currSize - i] = candModeList2[currSize - 1 - i];
+      candCostList[currSize - i] = candCostList[currSize - 1 - i];
+    }
+    candModeList[currSize - shift] = mode;
+    candModeList2[currSize - shift] = mode2;
+    candCostList[currSize - shift] = cost;
+    return 1;
+  }
+  else if (currSize < fastCandNum)
+  {
+    candModeList.insert(candModeList.end() - shift, mode);
+    candModeList2.insert(candModeList2.end() - shift, mode2);
+    candCostList.insert(candCostList.end() - shift, cost);
+    return 1;
+  }
+
+  return 0;
+}
+#endif
+
 
 #endif
 
diff --git a/source/Lib/DecoderLib/CABACReader.cpp b/source/Lib/DecoderLib/CABACReader.cpp
index 70aae0890cdc0a0bd6a842586cf89ef874d7ad75..12d1d1bc9957d5d66cde55ce33a8cd5b31cd9c80 100644
--- a/source/Lib/DecoderLib/CABACReader.cpp
+++ b/source/Lib/DecoderLib/CABACReader.cpp
@@ -1155,6 +1155,14 @@ void CABACReader::prediction_unit( PredictionUnit& pu, MergeCtx& mrgCtx )
   if( pu.mergeFlag )
   {
     affine_flag  ( *pu.cu );
+#if JVET_L0100_MULTI_HYPOTHESIS_INTRA
+    MHIntra_flag(pu);
+    if (pu.MHIntraFlag)
+    {
+      MHIntra_luma_pred_modes(*pu.cu);
+      pu.intraDir2[1] = DM_CHROMA_IDX;
+    }
+#endif
     merge_data   ( pu );
   }
   else
@@ -1395,6 +1403,129 @@ void CABACReader::mvp_flag( PredictionUnit& pu, RefPicList eRefList )
 }
 
 
+#if JVET_L0100_MULTI_HYPOTHESIS_INTRA
+void CABACReader::MHIntra_flag(PredictionUnit& pu)
+{
+  if (!pu.cs->sps->getSpsNext().getUseMHIntra())
+  {
+    pu.MHIntraFlag = false;
+    return;
+  }
+  if (pu.cu->skip)
+  {
+    pu.MHIntraFlag = false;
+    return;
+  }
+
+  if (pu.cu->lwidth() * pu.cu->lheight() < 64 || pu.cu->lwidth() >= MAX_CU_SIZE || pu.cu->lheight() >= MAX_CU_SIZE)
+  {
+    pu.MHIntraFlag = false;
+    return;
+  }
+  RExt__DECODER_DEBUG_BIT_STATISTICS_CREATE_SET(STATS__CABAC_BITS__MH_INTRA_FLAG);
+
+  pu.MHIntraFlag = (m_BinDecoder.decodeBin(Ctx::MHIntraFlag()));
+  DTRACE(g_trace_ctx, D_SYNTAX, "MHIntra_flag() MHIntra=%d pos=(%d,%d) size=%dx%d\n", pu.MHIntraFlag ? 1 : 0, pu.lumaPos().x, pu.lumaPos().y, pu.lumaSize().width, pu.lumaSize().height);
+}
+
+void CABACReader::MHIntra_luma_pred_modes(CodingUnit &cu)
+{
+  if (!cu.Y().valid())
+  {
+    return;
+  }
+
+  RExt__DECODER_DEBUG_BIT_STATISTICS_CREATE_SET_SIZE2(STATS__CABAC_BITS__INTRA_DIR_ANG, cu.lumaSize(), CHANNEL_TYPE_LUMA);
+
+  const uint32_t numMPMs = 3;
+
+  // prev_intra_luma_pred_flag
+  int numBlocks = CU::getNumPUs(cu);
+  int mpmFlag[4];
+  PredictionUnit *pu = cu.firstPU;
+  for (int k = 0; k < numBlocks; k++)
+  {
+    if (PU::getNarrowShape(pu->lwidth(), pu->lheight()) == 0)
+    {
+      mpmFlag[k] = m_BinDecoder.decodeBin(Ctx::MHIntraPredMode());
+    }
+    else
+    {
+      mpmFlag[k] = 1;
+    }
+  }
+
+  // mpm_idx / rem_intra_luma_pred_mode
+  for (int k = 0; k < numBlocks; k++)
+  {
+    unsigned *mpm_pred = (unsigned*)alloca(numMPMs * sizeof(unsigned));
+    PU::getMHIntraMPMs(*pu, mpm_pred);
+
+    if (mpmFlag[k])
+    {
+      unsigned pred_idx = 0;
+
+      pred_idx = m_BinDecoder.decodeBinEP();
+      if (pred_idx)
+      {
+        pred_idx += m_BinDecoder.decodeBinEP();
+      }
+      pu->intraDir2[0] = mpm_pred[pred_idx];
+    }
+    else
+    {
+      unsigned pred_mode = 0;
+
+      bool isMPMCand[4];
+      for (unsigned i = 0; i < 4; i++)
+      {
+        isMPMCand[i] = false;
+      }
+      for (unsigned i = 0; i < 3; i++)
+      {
+        if (mpm_pred[i] == PLANAR_IDX)
+        {
+          isMPMCand[0] = true;
+        }
+        else if (mpm_pred[i] == DC_IDX)
+        {
+          isMPMCand[1] = true;
+        }
+        else if (mpm_pred[i] == HOR_IDX)
+        {
+          isMPMCand[2] = true;
+        }
+        else if (mpm_pred[i] == VER_IDX)
+        {
+          isMPMCand[3] = true;
+        }
+      }
+      if (!isMPMCand[0])
+      {
+        pred_mode = PLANAR_IDX;
+      }
+      if (!isMPMCand[1])
+      {
+        pred_mode = DC_IDX;
+      }
+      if (!isMPMCand[2])
+      {
+        pred_mode = HOR_IDX;
+      }
+      if (!isMPMCand[3])
+      {
+        pred_mode = VER_IDX;
+      }
+      pu->intraDir2[0] = pred_mode;
+    }
+
+    DTRACE(g_trace_ctx, D_SYNTAX, "intra_luma_pred_modes() idx=%d pos=(%d,%d) mode=%d\n", k, pu->lumaPos().x, pu->lumaPos().y, pu->intraDir2[0]);
+    pu = pu->next;
+  }
+}
+#endif
+
+
 //================================================================================
 //  clause 7.3.8.7
 //--------------------------------------------------------------------------------
diff --git a/source/Lib/DecoderLib/CABACReader.h b/source/Lib/DecoderLib/CABACReader.h
index fd194650c2275ac547e9cff453b7c7f04d167b64..aabf26f8246ea9ad231bf14c7c1265d6225c2589 100644
--- a/source/Lib/DecoderLib/CABACReader.h
+++ b/source/Lib/DecoderLib/CABACReader.h
@@ -101,6 +101,10 @@ public:
   void        inter_pred_idc            ( PredictionUnit&               pu );
   void        ref_idx                   ( PredictionUnit&               pu,     RefPicList      eRefList );
   void        mvp_flag                  ( PredictionUnit&               pu,     RefPicList      eRefList );
+#if JVET_L0100_MULTI_HYPOTHESIS_INTRA
+  void        MHIntra_flag              ( PredictionUnit&               pu );
+  void        MHIntra_luma_pred_modes   ( CodingUnit&                   cu );
+#endif
 
   // pcm samples (clause 7.3.8.7)
   void        pcm_samples               ( TransformUnit&                tu );
diff --git a/source/Lib/DecoderLib/DecCu.cpp b/source/Lib/DecoderLib/DecCu.cpp
index 8fb1c75ba89f278c1a621440ce9e8fd16f45bade..aa112c2b56212d7c96a40a4f126aa3047cf901ca 100644
--- a/source/Lib/DecoderLib/DecCu.cpp
+++ b/source/Lib/DecoderLib/DecCu.cpp
@@ -314,9 +314,22 @@ void DecCu::xFillPCMBuffer(CodingUnit &cu)
 
 void DecCu::xReconInter(CodingUnit &cu)
 {
+#if JVET_L0100_MULTI_HYPOTHESIS_INTRA
+  m_pcIntraPred->geneIntrainterPred(cu);
+#endif
+
   // inter prediction
   m_pcInterPred->motionCompensation( cu );
 
+#if JVET_L0100_MULTI_HYPOTHESIS_INTRA
+  if (cu.firstPU->MHIntraFlag)
+  {
+    m_pcIntraPred->geneWeightedPred(COMPONENT_Y, cu.cs->getPredBuf(*cu.firstPU).Y(), *cu.firstPU, m_pcIntraPred->getPredictorPtr2(COMPONENT_Y, 0));
+    m_pcIntraPred->geneWeightedPred(COMPONENT_Cb, cu.cs->getPredBuf(*cu.firstPU).Cb(), *cu.firstPU, m_pcIntraPred->getPredictorPtr2(COMPONENT_Cb, 0));
+    m_pcIntraPred->geneWeightedPred(COMPONENT_Cr, cu.cs->getPredBuf(*cu.firstPU).Cr(), *cu.firstPU, m_pcIntraPred->getPredictorPtr2(COMPONENT_Cr, 0));
+  }
+#endif
+
   DTRACE    ( g_trace_ctx, D_TMP, "pred " );
   DTRACE_CRC( g_trace_ctx, D_TMP, *cu.cs, cu.cs->getPredBuf( cu ), &cu.Y() );
     
diff --git a/source/Lib/DecoderLib/VLCReader.cpp b/source/Lib/DecoderLib/VLCReader.cpp
index 6411f40925591b25135a41bca89c1ade35e90d73..90b1f4379fa99b2493a614cf16c1d5c6af6d6514 100644
--- a/source/Lib/DecoderLib/VLCReader.cpp
+++ b/source/Lib/DecoderLib/VLCReader.cpp
@@ -812,6 +812,9 @@ void HLSyntaxReader::parseSPSNext( SPSNext& spsNext, const bool usePCM )
     READ_FLAG( symbol,  "reserved_flag" );                          if( symbol != 0 ) EXIT("Incompatible version: SPSNext reserved flag not equal to zero (bitstream was probably created with newer software version)" );
   }
   READ_FLAG( symbol,  "mtt_enabled_flag" );                       spsNext.setMTTMode                ( symbol );
+#if JVET_L0100_MULTI_HYPOTHESIS_INTRA
+  READ_FLAG( symbol,  "MHIntra_flag" );                           spsNext.setUseMHIntra             ( symbol != 0 );
+#endif
 #if ENABLE_WPP_PARALLELISM
   READ_FLAG( symbol,  "next_dqp_enabled_flag" );                  spsNext.setUseNextDQP             ( symbol != 0 );
 #else
diff --git a/source/Lib/EncoderLib/CABACWriter.cpp b/source/Lib/EncoderLib/CABACWriter.cpp
index 6eb95048a8d34be9fdb4d2bd1d56368420e34b0e..b94bf2712b0e320a8118f2dde1eb740491498e37 100644
--- a/source/Lib/EncoderLib/CABACWriter.cpp
+++ b/source/Lib/EncoderLib/CABACWriter.cpp
@@ -1114,6 +1114,13 @@ void CABACWriter::prediction_unit( const PredictionUnit& pu )
   if( pu.mergeFlag )
   {
     affine_flag  ( *pu.cu );
+#if JVET_L0100_MULTI_HYPOTHESIS_INTRA
+    MHIntra_flag( pu );
+    if ( pu.MHIntraFlag )
+    {
+      MHIntra_luma_pred_modes( *pu.cu );
+    }
+#endif
     merge_idx    ( pu );
   }
   else
@@ -1347,7 +1354,90 @@ void CABACWriter::mvp_flag( const PredictionUnit& pu, RefPicList eRefList )
   DTRACE( g_trace_ctx, D_SYNTAX, "mvpIdx(refList:%d)=%d\n", eRefList, pu.mvpIdx[eRefList] );
 }
 
+#if JVET_L0100_MULTI_HYPOTHESIS_INTRA
+void CABACWriter::MHIntra_flag(const PredictionUnit& pu)
+{
+  if (!pu.cs->sps->getSpsNext().getUseMHIntra())
+  {
+    CHECK(pu.MHIntraFlag == true, "invalid MHIntra SPS");
+    return;
+  }
+  if (pu.cu->skip)
+  {
+    CHECK(pu.MHIntraFlag == true, "invalid MHIntra and skip");
+    return;
+  }
+  if (pu.cu->lwidth() * pu.cu->lheight() < 64 || pu.cu->lwidth() >= MAX_CU_SIZE || pu.cu->lheight() >= MAX_CU_SIZE)
+  {
+    CHECK(pu.MHIntraFlag == true, "invalid MHIntra and blk");
+    return;
+  }
+  m_BinEncoder.encodeBin(pu.MHIntraFlag, Ctx::MHIntraFlag());
+  DTRACE(g_trace_ctx, D_SYNTAX, "MHIntra_flag() intrainter=%d pos=(%d,%d) size=%dx%d\n", pu.MHIntraFlag ? 1 : 0, pu.lumaPos().x, pu.lumaPos().y, pu.lumaSize().width, pu.lumaSize().height);
+}
+
+void CABACWriter::MHIntra_luma_pred_modes(const CodingUnit& cu)
+{
+  if (!cu.Y().valid())
+  {
+    return;
+  }
+
+  unsigned numMPMs = 3;
+  int      numBlocks = CU::getNumPUs(cu);
+  unsigned *mpm_preds[4];
+  unsigned mpm_idxs[4];
+  unsigned pred_modes[4];
+
+  const PredictionUnit* pu = cu.firstPU;
+
+  for (int k = 0; k < numBlocks; k++)
+  {
+    unsigned*& mpm_pred = mpm_preds[k];
+    unsigned&  mpm_idx = mpm_idxs[k];
+    unsigned&  pred_mode = pred_modes[k];
+
+    mpm_pred = (unsigned*)alloca(numMPMs * sizeof(unsigned));
+    PU::getMHIntraMPMs(*pu, mpm_pred);
+
+    pred_mode = pu->intraDir2[0];
 
+    mpm_idx = numMPMs;
+
+    for (unsigned idx = 0; idx < numMPMs; idx++)
+    {
+      if (pred_mode == mpm_pred[idx])
+      {
+        mpm_idx = idx;
+        break;
+      }
+    }
+    if (PU::getNarrowShape(pu->lwidth(), pu->lheight()) == 0)
+    {
+      m_BinEncoder.encodeBin(mpm_idx < numMPMs, Ctx::MHIntraPredMode());
+    }
+    pu = pu->next;
+  }
+
+  pu = cu.firstPU;
+
+  // mpm_idx / rem_intra_luma_pred_mode
+  for (int k = 0; k < numBlocks; k++)
+  {
+    const unsigned& mpm_idx = mpm_idxs[k];
+    if (mpm_idx < numMPMs)
+    {
+      m_BinEncoder.encodeBinEP(mpm_idx > 0);
+      if (mpm_idx)
+      {
+        m_BinEncoder.encodeBinEP(mpm_idx > 1);
+      }
+    }
+    DTRACE(g_trace_ctx, D_SYNTAX, "intra_luma_pred_modes() idx=%d pos=(%d,%d) mode=%d\n", k, pu->lumaPos().x, pu->lumaPos().y, pu->intraDir2[0]);
+    pu = pu->next;
+  }
+}
+#endif
 
 //================================================================================
 //  clause 7.3.8.7
diff --git a/source/Lib/EncoderLib/CABACWriter.h b/source/Lib/EncoderLib/CABACWriter.h
index b17bfadeef0a8c6034921de77b5838e451e51257..9b76813fad28fc16d6dc869e0c38c7d84ed84b3f 100644
--- a/source/Lib/EncoderLib/CABACWriter.h
+++ b/source/Lib/EncoderLib/CABACWriter.h
@@ -115,6 +115,10 @@ public:
   void        ref_idx                   ( const PredictionUnit&         pu,       RefPicList        eRefList );
   void        mvp_flag                  ( const PredictionUnit&         pu,       RefPicList        eRefList );
 
+#if JVET_L0100_MULTI_HYPOTHESIS_INTRA
+  void        MHIntra_flag              ( const PredictionUnit&         pu );
+  void        MHIntra_luma_pred_modes   ( const CodingUnit&             cu );
+#endif
 
   // pcm samples (clause 7.3.8.7)
   void        pcm_samples               ( const TransformUnit&          tu );
diff --git a/source/Lib/EncoderLib/EncCfg.h b/source/Lib/EncoderLib/EncCfg.h
index 71f50a481222dc17cdb80a41932e80d642a51ef8..7ffd5475d782ba1c4a453c606460863c02271d93 100644
--- a/source/Lib/EncoderLib/EncCfg.h
+++ b/source/Lib/EncoderLib/EncCfg.h
@@ -208,6 +208,10 @@ protected:
   bool      m_GBi;
   bool      m_GBiFast;
 #endif
+
+#if JVET_L0100_MULTI_HYPOTHESIS_INTRA
+  bool      m_MHIntra;
+#endif
   // ADD_NEW_TOOL : (encoder lib) add tool enabling flags and associated parameters here
 
   bool      m_useFastLCTU;
@@ -651,6 +655,12 @@ public:
   void      setUseGBiFast                   ( uint32_t b )   { m_GBiFast = b; }
   bool      getUseGBiFast                   ()         const { return m_GBiFast; }
 #endif
+
+#if JVET_L0100_MULTI_HYPOTHESIS_INTRA
+  void      setUseMHIntra                   ( bool b )       { m_MHIntra = b; }
+  bool      getUseMHIntra                   ()         const { return m_MHIntra; }
+#endif
+
   // ADD_NEW_TOOL : (encoder lib) add access functions here
 
   void      setMaxCUWidth                   ( uint32_t  u )      { m_maxCUWidth  = u; }
diff --git a/source/Lib/EncoderLib/EncCu.cpp b/source/Lib/EncoderLib/EncCu.cpp
index 926841c1057fb2740c447752eab355c7ab3f2bc3..4bb7573714e5c545067379c5ed3565b569fe0eb3 100644
--- a/source/Lib/EncoderLib/EncCu.cpp
+++ b/source/Lib/EncoderLib/EncCu.cpp
@@ -1439,9 +1439,33 @@ void EncCu::xCheckRDCostMerge2Nx2N( CodingStructure *&tempCS, CodingStructure *&
     RdModeList.push_back( i );
   }
 
+#if JVET_L0100_MULTI_HYPOTHESIS_INTRA
+  static_vector<unsigned, MRG_MAX_NUM_CANDS>  RdModeList2; // store the Intra mode for Intrainter
+  RdModeList2.clear();
+  bool isIntrainterEnabled = sps.getSpsNext().getUseMHIntra();
+  if (bestCS->area.lwidth() * bestCS->area.lheight() < 64 || bestCS->area.lwidth() >= MAX_CU_SIZE || bestCS->area.lheight() >= MAX_CU_SIZE)
+  {
+    isIntrainterEnabled = false;
+  }
+  bool isTestSkipMerge[MRG_MAX_NUM_CANDS]; // record if the merge candidate has tried skip mode 
+  for (uint32_t uiMerge = 0; uiMerge < MRG_MAX_NUM_CANDS; uiMerge++)
+  {
+    isTestSkipMerge[uiMerge] = false;
+  }
+#endif
+#if JVET_L0100_MULTI_HYPOTHESIS_INTRA
+  if( m_pcEncCfg->getUseFastMerge() || isIntrainterEnabled)
+#else
   if( m_pcEncCfg->getUseFastMerge() )
+#endif
   {
     uiNumMrgSATDCand = NUM_MRG_SATD_CAND;
+#if JVET_L0100_MULTI_HYPOTHESIS_INTRA
+    if (isIntrainterEnabled)
+    {
+      uiNumMrgSATDCand += 1;
+    }
+#endif
     bestIsSkip       = false;
 
     if( auto blkCache = dynamic_cast< CacheBlkInfoCtrl* >( m_modeCtrl ) )
@@ -1449,8 +1473,21 @@ void EncCu::xCheckRDCostMerge2Nx2N( CodingStructure *&tempCS, CodingStructure *&
       bestIsSkip = blkCache->isSkip( tempCS->area );
     }
 
+#if JVET_L0100_MULTI_HYPOTHESIS_INTRA
+    if (isIntrainterEnabled) // always perform low complexity check
+    {
+      bestIsSkip = false;
+    }
+#endif
+
     static_vector<double, MRG_MAX_NUM_CANDS> candCostList;
 
+#if JVET_L0100_MULTI_HYPOTHESIS_INTRA
+    for (uint32_t idx = 0; idx < MRG_MAX_NUM_CANDS; idx++)
+    {
+      candCostList[idx] = MAX_DOUBLE;
+    }
+#endif
     // 1. Pass: get SATD-cost for selected candidates and reduce their count
     if( !bestIsSkip )
     {
@@ -1459,6 +1496,9 @@ void EncCu::xCheckRDCostMerge2Nx2N( CodingStructure *&tempCS, CodingStructure *&
       const double sqrtLambdaForFirstPass = m_pcRdCost->getMotionLambda( encTestMode.lossless );
 
       CodingUnit &cu      = tempCS->addCU( tempCS->area, partitioner.chType );
+#if JVET_L0100_MULTI_HYPOTHESIS_INTRA
+      const double sqrtLambdaForFirstPassIntra = m_pcRdCost->getMotionLambda(cu.transQuantBypass) / double(1 << SCALE_BITS);
+#endif
 
       partitioner.setCUData( cu );
       cu.slice            = tempCS->slice;
@@ -1511,9 +1551,21 @@ void EncCu::xCheckRDCostMerge2Nx2N( CodingStructure *&tempCS, CodingStructure *&
         }
         double cost     = (double)uiSad + (double)uiBitsCand * sqrtLambdaForFirstPass;
 
+#if JVET_L0100_MULTI_HYPOTHESIS_INTRA
+        updateDoubleCandList(uiMergeCand, cost, RdModeList, candCostList, RdModeList2, (uint32_t)NUM_LUMA_MODE, uiNumMrgSATDCand);
+#else
         updateCandList( uiMergeCand, cost, RdModeList, candCostList, uiNumMrgSATDCand );
+#endif
         CHECK( std::min( uiMergeCand + 1, uiNumMrgSATDCand ) != RdModeList.size(), "" );
       }
+
+#if JVET_L0100_MULTI_HYPOTHESIS_INTRA
+      if (isIntrainterEnabled)
+      {
+        getMHIntraLCCand(pu, RdModeList, candCostList, RdModeList2, uiNumMrgSATDCand, sqrtLambdaForFirstPass, sqrtLambdaForFirstPassIntra, distParam, localUnitArea);
+      }
+#endif
+
       // Try to limit number of candidates using SATD-costs
       for( uint32_t i = 1; i < uiNumMrgSATDCand; i++ )
       {
@@ -1524,6 +1576,31 @@ void EncCu::xCheckRDCostMerge2Nx2N( CodingStructure *&tempCS, CodingStructure *&
         }
       }
 
+#if JVET_L0100_MULTI_HYPOTHESIS_INTRA
+      if (isIntrainterEnabled)
+      {
+        pu.MHIntraFlag = true;
+        for (uint32_t mergeCnt = 0; mergeCnt < uiNumMrgSATDCand; mergeCnt++)
+        {
+          if (RdModeList[mergeCnt] >= (MRG_MAX_NUM_CANDS + MRG_MAX_NUM_CANDS))
+          {
+            pu.intraDir2[0] = RdModeList2[mergeCnt];
+            pu.intraDir2[1] = DM_CHROMA_IDX;
+            uint32_t bufIdx = (pu.intraDir2[0] > 1) ? (pu.intraDir2[0] == HOR_IDX ? 2 : 3) : pu.intraDir2[0];
+            bool isUseFilter = IntraPrediction::useFilteredIntraRefSamples(COMPONENT_Cb, pu, true, pu);
+            m_pcIntraSearch->initIntraPatternChType(*pu.cu, pu.Cb(), isUseFilter);
+            m_pcIntraSearch->predIntraAng(COMPONENT_Cb, pu.cs->getPredBuf(pu).Cb(), pu, isUseFilter);
+            m_pcIntraSearch->switchBuffer(pu, COMPONENT_Cb, pu.cs->getPredBuf(pu).Cb(), m_pcIntraSearch->getPredictorPtr2(COMPONENT_Cb, bufIdx));
+            isUseFilter = IntraPrediction::useFilteredIntraRefSamples(COMPONENT_Cr, pu, true, pu);
+            m_pcIntraSearch->initIntraPatternChType(*pu.cu, pu.Cr(), isUseFilter);
+            m_pcIntraSearch->predIntraAng(COMPONENT_Cr, pu.cs->getPredBuf(pu).Cr(), pu, isUseFilter);
+            m_pcIntraSearch->switchBuffer(pu, COMPONENT_Cr, pu.cs->getPredBuf(pu).Cr(), m_pcIntraSearch->getPredictorPtr2(COMPONENT_Cr, bufIdx));
+          }
+        }
+        pu.MHIntraFlag = false;
+      }
+#endif
+
       tempCS->initStructData( encTestMode.qp, encTestMode.lossless );
     }
     else
@@ -1540,6 +1617,18 @@ void EncCu::xCheckRDCostMerge2Nx2N( CodingStructure *&tempCS, CodingStructure *&
     for( uint32_t uiMrgHADIdx = 0; uiMrgHADIdx < uiNumMrgSATDCand; uiMrgHADIdx++ )
     {
       uint32_t uiMergeCand = RdModeList[uiMrgHADIdx];
+
+#if JVET_L0100_MULTI_HYPOTHESIS_INTRA
+      if (uiNoResidualPass != 0 && uiMergeCand >= (MRG_MAX_NUM_CANDS + MRG_MAX_NUM_CANDS)) // intrainter does not support skip mode
+      {
+        uiMergeCand -= (MRG_MAX_NUM_CANDS + MRG_MAX_NUM_CANDS); // for skip, map back to normal merge candidate idx and try RDO
+        if (isTestSkipMerge[uiMergeCand]) // avoid redundancy
+        {
+          continue;
+        }
+      }
+#endif
+
       if( ( (uiNoResidualPass != 0) && candHasNoResidual[uiMergeCand] )
        || ( (uiNoResidualPass == 0) && bestIsSkip ) )
       {
@@ -1564,6 +1653,17 @@ void EncCu::xCheckRDCostMerge2Nx2N( CodingStructure *&tempCS, CodingStructure *&
       cu.qp               = encTestMode.qp;
       PredictionUnit &pu  = tempCS->addPU( cu, partitioner.chType );
 
+#if JVET_L0100_MULTI_HYPOTHESIS_INTRA
+      if (uiNoResidualPass == 0 && uiMergeCand >= (MRG_MAX_NUM_CANDS + MRG_MAX_NUM_CANDS))
+      {
+        uiMergeCand -= (MRG_MAX_NUM_CANDS + MRG_MAX_NUM_CANDS);
+        pu.MHIntraFlag = true;
+        pu.intraDir2[0] = RdModeList2[uiMrgHADIdx];
+        CHECK(pu.intraDir2[0]<0 || pu.intraDir2[0]>(NUM_LUMA_MODE - 1), "out of intra mode");
+        pu.intraDir2[1] = DM_CHROMA_IDX;
+      }
+#endif
+
       mergeCtx.setMergeInfo( pu, uiMergeCand );
       PU::spanMotionInfo( pu, mergeCtx );
 
@@ -1572,7 +1672,27 @@ void EncCu::xCheckRDCostMerge2Nx2N( CodingStructure *&tempCS, CodingStructure *&
 #if DMVR_JVET_LOW_LATENCY_K0217
         pu.mvd[0] = refinedMvdL0[uiMergeCand];
 #endif
+#if JVET_L0100_MULTI_HYPOTHESIS_INTRA
+        if (pu.MHIntraFlag)
+        {
+          uint32_t bufIdx = (pu.intraDir2[0] > 1) ? (pu.intraDir2[0] == HOR_IDX ? 2 : 3) : pu.intraDir2[0];
+          PelBuf tmpBuf = tempCS->getPredBuf(pu).Y();
+          tmpBuf.copyFrom(acMergeBuffer[uiMergeCand].Y());
+          m_pcIntraSearch->geneWeightedPred(COMPONENT_Y, tmpBuf, pu, m_pcIntraSearch->getPredictorPtr2(COMPONENT_Y, bufIdx));
+          tmpBuf = tempCS->getPredBuf(pu).Cb();
+          tmpBuf.copyFrom(acMergeBuffer[uiMergeCand].Cb());
+          m_pcIntraSearch->geneWeightedPred(COMPONENT_Cb, tmpBuf, pu, m_pcIntraSearch->getPredictorPtr2(COMPONENT_Cb, bufIdx));
+          tmpBuf = tempCS->getPredBuf(pu).Cr();
+          tmpBuf.copyFrom(acMergeBuffer[uiMergeCand].Cr());
+          m_pcIntraSearch->geneWeightedPred(COMPONENT_Cr, tmpBuf, pu, m_pcIntraSearch->getPredictorPtr2(COMPONENT_Cr, bufIdx));
+        }
+        else
+        {
+          tempCS->getPredBuf().copyFrom(acMergeBuffer[uiMergeCand]);
+        }
+#else
         tempCS->getPredBuf().copyFrom( acMergeBuffer[ uiMergeCand ]);
+#endif
       }
       else
       {
@@ -1580,12 +1700,23 @@ void EncCu::xCheckRDCostMerge2Nx2N( CodingStructure *&tempCS, CodingStructure *&
         
       }
 
+#if JVET_L0100_MULTI_HYPOTHESIS_INTRA
+      if (!pu.MHIntraFlag && uiNoResidualPass != 0)
+      {
+        isTestSkipMerge[uiMergeCand] = true;
+      }
+#endif
+
       xEncodeInterResidual( tempCS, bestCS, partitioner, encTestMode, uiNoResidualPass
         , NULL
         , 1
         , uiNoResidualPass == 0 ? &candHasNoResidual[uiMergeCand] : NULL );
 
+#if JVET_L0100_MULTI_HYPOTHESIS_INTRA
+      if( m_pcEncCfg->getUseFastDecisionForMerge() && !bestIsSkip && !pu.MHIntraFlag)
+#else
       if( m_pcEncCfg->getUseFastDecisionForMerge() && !bestIsSkip )
+#endif
       {
         bestIsSkip = bestCS->getCU( partitioner.chType )->rootCbf == 0;
       }
@@ -2171,9 +2302,23 @@ void EncCu::xEncodeInterResidual( CodingStructure *&tempCS, CodingStructure *&be
     if( NULL != bestHasNonResi && (bestCostInternal > tempCS->cost) )
     {
       bestCostInternal = tempCS->cost;
+#if JVET_L0100_MULTI_HYPOTHESIS_INTRA
+      if (!(tempCS->getPU(partitioner.chType)->MHIntraFlag))
+#endif
       *bestHasNonResi  = !cu->rootCbf;
     }
 
+#if JVET_L0100_MULTI_HYPOTHESIS_INTRA
+    if (cu->rootCbf == false)
+    {
+      if (tempCS->getPU(partitioner.chType)->MHIntraFlag)
+      {
+        tempCS->cost = MAX_DOUBLE;
+        return;
+      }
+    }
+#endif
+
 #if WCG_EXT
     DTRACE_MODE_COST( *tempCS, m_pcRdCost->getLambda( true ) );
 #else
@@ -2292,5 +2437,96 @@ void EncCu::xReuseCachedResult( CodingStructure *&tempCS, CodingStructure *&best
 
 #endif
 
+#if JVET_L0100_MULTI_HYPOTHESIS_INTRA
+template<typename T, size_t N>
+void EncCu::getMHIntraLCCand(PredictionUnit &pu, static_vector<T, N>& RdModeList, static_vector<double, N>& candCostList, static_vector<T, N>& RdModeList2, uint32_t uiNumMrgSATDCand, const double sqrtLambdaForFirstPass, const double sqrtLambdaForFirstPassIntra, DistParam distParam, const UnitArea localUnitArea)
+{
+  PelUnitBuf acMergeBuffer[MRG_MAX_NUM_CANDS]; // acquire normal merge pred buffer
+
+  int numTestIntraMode = 4;
+  // prepare for Intra bits calculation
+  const TempCtx ctxStart(m_CtxCache, m_CABACEstimator->getCtx());
+  const TempCtx ctxStartIntraMode(m_CtxCache, SubCtx(Ctx::MHIntraPredMode, m_CABACEstimator->getCtx()));
+
+  // for Intrainter fast, recored the best intra mode during the first round for mrege 0
+  int bestMHIntraMode = -1;
+  double bestMHIntraCost = MAX_DOUBLE;
+
+  pu.MHIntraFlag = true;
+
+  // save the to-be-tested merge candidates
+  uint32_t MHIntraMergeCand[NUM_MRG_SATD_CAND];
+
+  for (uint32_t mergeCnt = 0; mergeCnt < NUM_MRG_SATD_CAND; mergeCnt++)
+  {
+    MHIntraMergeCand[mergeCnt] = RdModeList[mergeCnt];
+  }
+
+  for (uint32_t mergeCnt = 0; mergeCnt < std::min(NUM_MRG_SATD_CAND, 4); mergeCnt++)
+  {
+    uint32_t mergeCand = MHIntraMergeCand[mergeCnt];
+    acMergeBuffer[mergeCand] = m_acMergeBuffer[mergeCand].getBuf(localUnitArea);
+
+    // estimate merge bits
+    uint32_t bitsCand = mergeCand + 1;
+    if (mergeCand == pu.cs->slice->getMaxNumMergeCand() - 1)
+    {
+      bitsCand--;
+    }
+
+    // first round
+    for (uint32_t intraCnt = 0; intraCnt < numTestIntraMode; intraCnt++)
+    {
+      pu.intraDir2[0] = (intraCnt < 2) ? intraCnt : ((intraCnt == 2) ? HOR_IDX : VER_IDX);
+
+      // fast 2
+      if (mergeCnt > 0 && bestMHIntraMode != pu.intraDir2[0])
+      {
+        continue;
+      }
+      int narrowCase = PU::getNarrowShape(pu.lwidth(), pu.lheight());
+      if (narrowCase == 1 && pu.intraDir2[0] == HOR_IDX)
+      {
+        continue;
+      }
+      if (narrowCase == 2 && pu.intraDir2[0] == VER_IDX)
+      {
+        continue;
+      }
+      // generate intrainter Y prediction
+      if (mergeCnt == 0)
+      {
+        bool isUseFilter = IntraPrediction::useFilteredIntraRefSamples(COMPONENT_Y, pu, true, pu);
+        m_pcIntraSearch->initIntraPatternChType(*pu.cu, pu.Y(), isUseFilter);
+        m_pcIntraSearch->predIntraAng(COMPONENT_Y, pu.cs->getPredBuf(pu).Y(), pu, isUseFilter);
+        m_pcIntraSearch->switchBuffer(pu, COMPONENT_Y, pu.cs->getPredBuf(pu).Y(), m_pcIntraSearch->getPredictorPtr2(COMPONENT_Y, intraCnt));
+      }
+
+      pu.cs->getPredBuf(pu).copyFrom(acMergeBuffer[mergeCand]);
+      m_pcIntraSearch->geneWeightedPred(COMPONENT_Y, pu.cs->getPredBuf(pu).Y(), pu, m_pcIntraSearch->getPredictorPtr2(COMPONENT_Y, intraCnt));
+
+      // calculate cost
+      distParam.cur = pu.cs->getPredBuf(pu).Y();
+      Distortion sadValue = distParam.distFunc(distParam);
+      m_CABACEstimator->getCtx() = SubCtx(Ctx::MHIntraPredMode, ctxStartIntraMode);
+      uint64_t fracModeBits = m_pcIntraSearch->xFracModeBitsIntra(pu, pu.intraDir2[0], CHANNEL_TYPE_LUMA);
+      double cost = (double)sadValue + (double)(bitsCand + 1) * sqrtLambdaForFirstPass + (double)fracModeBits * sqrtLambdaForFirstPassIntra;
+
+      updateDoubleCandList(mergeCand + MRG_MAX_NUM_CANDS + MRG_MAX_NUM_CANDS, cost, RdModeList, candCostList, RdModeList2, pu.intraDir2[0], uiNumMrgSATDCand);
+
+      // fast 2
+      if (mergeCnt == 0 && cost < bestMHIntraCost)
+      {
+        bestMHIntraMode = pu.intraDir2[0];
+        bestMHIntraCost = cost;
+      }
+    }
+  }
+  pu.MHIntraFlag = false;
+  m_CABACEstimator->getCtx() = ctxStart;
+  return;
+}
+#endif
+
 
 //! \}
diff --git a/source/Lib/EncoderLib/EncCu.h b/source/Lib/EncoderLib/EncCu.h
index d8131f4532a7f5b5e278e8284af1d8cf9275b42c..e75d96b3381b6761ce78519aaf22c2f8e26df4fc 100644
--- a/source/Lib/EncoderLib/EncCu.h
+++ b/source/Lib/EncoderLib/EncCu.h
@@ -212,6 +212,10 @@ protected:
        ||  abs(cu.slice->getPOC() - cu.slice->getRefPOC(REF_PIC_LIST_1, cu.refIdxBi[1])) == 1))));
   }
 #endif
+#if JVET_L0100_MULTI_HYPOTHESIS_INTRA
+  template<typename T, size_t N>
+  void getMHIntraLCCand       (PredictionUnit &pu, static_vector<T, N>& RdModeList, static_vector<double, N>& candCostList, static_vector<T, N>& RdModeList2, uint32_t uiNumMrgSATDCand, const double sqrtLambdaForFirstPass, const double sqrtLambdaForFirstPassIntra, DistParam distParam, const UnitArea localUnitArea);
+#endif
 };
 
 //! \}
diff --git a/source/Lib/EncoderLib/EncLib.cpp b/source/Lib/EncoderLib/EncLib.cpp
index 43573b1ab2c70f9b90ba700d5c50ae660e4a65ec..1fd74956212c5c23442bcde04b571cea7b3fed75 100644
--- a/source/Lib/EncoderLib/EncLib.cpp
+++ b/source/Lib/EncoderLib/EncLib.cpp
@@ -855,6 +855,10 @@ void EncLib::xInitSPS(SPS &sps)
 #if JVET_L0646_GBI
   sps.getSpsNext().setUseGBi                ( m_GBi );
 #endif
+#if JVET_L0100_MULTI_HYPOTHESIS_INTRA
+  sps.getSpsNext().setUseMHIntra            ( m_MHIntra );
+#endif
+
   // ADD_NEW_TOOL : (encoder lib) set tool enabling flags and associated parameters here
 
   int minCUSize = ( /*sps.getSpsNext().getUseQTBT() ? 1 << MIN_CU_LOG2 :*/ sps.getMaxCUWidth() >> sps.getLog2DiffMaxMinCodingBlockSize() );
diff --git a/source/Lib/EncoderLib/IntraSearch.cpp b/source/Lib/EncoderLib/IntraSearch.cpp
index 0dffa353696d65086cb5c22f0d5f165770d5489a..17f5e5b9beb774fb1a49befa3baab9f47f45d6ac 100644
--- a/source/Lib/EncoderLib/IntraSearch.cpp
+++ b/source/Lib/EncoderLib/IntraSearch.cpp
@@ -1786,19 +1786,34 @@ uint64_t IntraSearch::xFracModeBitsIntra(PredictionUnit &pu, const uint32_t &uiM
 {
   uint32_t orgMode = uiMode;
 
+#if JVET_L0100_MULTI_HYPOTHESIS_INTRA
+  if (!pu.MHIntraFlag)
+#endif
   std::swap(orgMode, pu.intraDir[chType]);
 
   m_CABACEstimator->resetBits();
 
   if( isLuma( chType ) )
   {
+#if JVET_L0100_MULTI_HYPOTHESIS_INTRA
+    if ( pu.MHIntraFlag )
+      m_CABACEstimator->MHIntra_luma_pred_modes(*pu.cu);
+    else
+    {
+      m_CABACEstimator->intra_luma_pred_mode(pu);
+    }
+#else
     m_CABACEstimator->intra_luma_pred_mode( pu );
+#endif
   }
   else
   {
     m_CABACEstimator->intra_chroma_pred_mode( pu );
   }
 
+#if JVET_L0100_MULTI_HYPOTHESIS_INTRA
+  if ( !pu.MHIntraFlag )
+#endif
   std::swap(orgMode, pu.intraDir[chType]);
 
   return m_CABACEstimator->getEstFracBits();
diff --git a/source/Lib/EncoderLib/IntraSearch.h b/source/Lib/EncoderLib/IntraSearch.h
index e71c42a8dad89e1525ac39eea946c49dc64cc0d3..424f78da79fb42c1554a766a6494ae446f8d175d 100644
--- a/source/Lib/EncoderLib/IntraSearch.h
+++ b/source/Lib/EncoderLib/IntraSearch.h
@@ -119,6 +119,9 @@ public:
   void estIntraPredLumaQT         ( CodingUnit &cu, Partitioner& pm );
   void estIntraPredChromaQT       (CodingUnit &cu, Partitioner& pm);
   void IPCMSearch                 (CodingStructure &cs, Partitioner& partitioner);
+#if JVET_L0100_MULTI_HYPOTHESIS_INTRA
+  uint64_t xFracModeBitsIntra     (PredictionUnit &pu, const uint32_t &uiMode, const ChannelType &compID);
+#endif
 
 protected:
 
@@ -139,7 +142,9 @@ protected:
   uint64_t xGetIntraFracBitsQTChroma(TransformUnit& tu, const ComponentID &compID);
   void xEncCoeffQT                (CodingStructure &cs, Partitioner& pm, const ComponentID &compID);
 
+#if !JVET_L0100_MULTI_HYPOTHESIS_INTRA
   uint64_t xFracModeBitsIntra       (PredictionUnit &pu, const uint32_t &uiMode, const ChannelType &compID);
+#endif
 
   void xIntraCodingTUBlock        (TransformUnit &tu, const ComponentID &compID, const bool &checkCrossCPrediction, Distortion& ruiDist, const int &default0Save1Load2 = 0, uint32_t* numSig = nullptr );
 
diff --git a/source/Lib/EncoderLib/VLCWriter.cpp b/source/Lib/EncoderLib/VLCWriter.cpp
index 29f4b427fe6f4718638c486259f4b19778f63dd5..27d000978040bb22afb39b4508133785e42e37cb 100644
--- a/source/Lib/EncoderLib/VLCWriter.cpp
+++ b/source/Lib/EncoderLib/VLCWriter.cpp
@@ -553,6 +553,9 @@ void HLSWriter::codeSPSNext( const SPSNext& spsNext, const bool usePCM )
   }
 
   WRITE_FLAG( spsNext.getMTTEnabled() ? 1 : 0,                                                  "mtt_enabled_flag" );
+#if JVET_L0100_MULTI_HYPOTHESIS_INTRA
+  WRITE_FLAG( spsNext.getUseMHIntra() ? 1 : 0,                                                  "MHIntra_flag" );
+#endif
 #if ENABLE_WPP_PARALLELISM
   WRITE_FLAG( spsNext.getUseNextDQP(),                                                          "next_dqp_enabled_flag" );
 #else