diff --git a/source/Lib/CommonLib/MotionInfo.h b/source/Lib/CommonLib/MotionInfo.h
index 26fcb5a712797ddf09b3b2b262617542e8c8269d..4b89dade0c12af8e8e1a0bebb8036f13ffa639bf 100644
--- a/source/Lib/CommonLib/MotionInfo.h
+++ b/source/Lib/CommonLib/MotionInfo.h
@@ -208,5 +208,11 @@ public:
   }
 };
 #endif
-
+#if  JVET_L0266_HMVP
+struct LuTMotionCand
+{
+  MotionInfo*   m_MotionCand;
+  int  currCnt;
+};
+#endif
 #endif // __MOTIONINFO__
diff --git a/source/Lib/CommonLib/Slice.cpp b/source/Lib/CommonLib/Slice.cpp
index 6023e01bc877a3e7a47d9a804bb897eb9b9e5323..f51ef38985416363b46350c3eb6e346dfd764305 100644
--- a/source/Lib/CommonLib/Slice.cpp
+++ b/source/Lib/CommonLib/Slice.cpp
@@ -41,6 +41,9 @@
 #include "Picture.h"
 #include "dtrace_next.h"
 
+#if  JVET_L0266_HMVP
+#include "UnitTools.h"
+#endif
 
 //! \ingroup CommonLib
 //! \{
@@ -125,6 +128,9 @@ Slice::Slice()
 , m_iProcessingStartTime          ( 0 )
 , m_dProcessingTime               ( 0 )
 , m_uiMaxBTSize                   ( 0 )
+#if  JVET_L0266_HMVP
+, m_MotionCandLuTs                (NULL)
+#endif
 {
   for(uint32_t i=0; i<NUM_REF_PIC_LIST_01; i++)
   {
@@ -161,11 +167,16 @@ Slice::Slice()
     m_saoEnabledFlag[ch] = false;
   }
 
+#if  JVET_L0266_HMVP
+  initMotionLUTs();
+#endif
 }
 
 Slice::~Slice()
 {
-
+#if  JVET_L0266_HMVP
+  destroyMotionLUTs();
+#endif
 }
 
 
@@ -197,6 +208,9 @@ void Slice::initSlice()
   m_enableTMVPFlag       = true;
   m_subPuMvpSubBlkSizeSliceEnable = false;
   m_subPuMvpSubBlkLog2Size        = 2;
+#if  JVET_L0266_HMVP
+  resetMotionLUTs();
+#endif
 }
 
 void Slice::setDefaultClpRng( const SPS& sps )
@@ -1550,7 +1564,75 @@ void Slice::stopProcessingTimer()
   m_dProcessingTime += (double)(clock()-m_iProcessingStartTime) / CLOCKS_PER_SEC;
   m_iProcessingStartTime = 0;
 }
+#if  JVET_L0266_HMVP
+void Slice::initMotionLUTs()
+{
+  m_MotionCandLuTs = new LuTMotionCand;
+  m_MotionCandLuTs->currCnt = 0;
+  m_MotionCandLuTs->m_MotionCand = nullptr;
+  m_MotionCandLuTs->m_MotionCand = new MotionInfo[MAX_NUM_HMVP_CANDS];
+}
+void Slice::destroyMotionLUTs()
+{
+  delete[] m_MotionCandLuTs->m_MotionCand;
+  m_MotionCandLuTs->m_MotionCand = nullptr;
+  delete[] m_MotionCandLuTs;
+  m_MotionCandLuTs = NULL;
+}
+void Slice::resetMotionLUTs()
+{
+   m_MotionCandLuTs->currCnt = 0;
+}
+
+MotionInfo Slice::getMotionInfoFromLUTs(int MotCandIdx) const
+{
+  return m_MotionCandLuTs->m_MotionCand[MotCandIdx];
+}
+
+
+
+void Slice::addMotionInfoToLUTs(LuTMotionCand* lutMC, MotionInfo newMi)
+{
+  int currCnt = lutMC->currCnt ;
+
+  bool bPruned = false;
+  int  sameCandIdx = -1;
+  for (int idx = 0; idx < currCnt; idx++)
+  {
+    if (lutMC->m_MotionCand[idx] == newMi)
+    {
+      sameCandIdx = idx;
+      bPruned = true;
+      break;
+    }
+  }
+  if (bPruned || lutMC->currCnt == MAX_NUM_HMVP_CANDS)
+  {
+    int startIdx = bPruned ? sameCandIdx : 0;
+    memmove(&lutMC->m_MotionCand[startIdx], &lutMC->m_MotionCand[startIdx+1], sizeof(MotionInfo)*(currCnt - sameCandIdx - 1));
+    memcpy(&lutMC->m_MotionCand[lutMC->currCnt-1], &newMi, sizeof(MotionInfo));
+  }
+  else
+  {
+    memcpy(&lutMC->m_MotionCand[lutMC->currCnt++], &newMi, sizeof(MotionInfo));
+  }
+}
 
+void Slice::updateMotionLUTs(LuTMotionCand* lutMC, CodingUnit & cu)
+{
+  PredictionUnit *selectedPU = cu.firstPU;
+  if (cu.affine) { return; }
+
+  MotionInfo newMi = selectedPU->getMotionInfo(); 
+  addMotionInfoToLUTs(lutMC, newMi);
+}
+
+void Slice::copyMotionLUTs(LuTMotionCand* Src, LuTMotionCand* Dst)
+{
+   memcpy(Dst->m_MotionCand, Src->m_MotionCand, sizeof(MotionInfo)*(std::min(Src->currCnt, MAX_NUM_HMVP_CANDS)));
+   Dst->currCnt = Src->currCnt;
+}
+#endif
 
 unsigned Slice::getMinPictureDistance() const
 {
diff --git a/source/Lib/CommonLib/Slice.h b/source/Lib/CommonLib/Slice.h
index d6ef610c7304e8361981d2524201642290e29fd9..f66d5f5a7bdd1e2b50452c25dda856630a1c18bf 100644
--- a/source/Lib/CommonLib/Slice.h
+++ b/source/Lib/CommonLib/Slice.h
@@ -49,7 +49,10 @@
 
 //! \ingroup CommonLib
 //! \{
-
+#if JVET_L0266_HMVP
+#include "CommonLib/MotionInfo.h"
+struct MotionInfo;
+#endif
 
 
 struct Picture;
@@ -1553,6 +1556,9 @@ private:
   uint32_t                       m_uiMaxBTSize;
 
   AlfSliceParam              m_alfSliceParam;
+#if  JVET_L0266_HMVP
+  LuTMotionCand*             m_MotionCandLuTs;
+#endif
 
 public:
                               Slice();
@@ -1810,6 +1816,20 @@ public:
 
   void                        setAlfSliceParam( AlfSliceParam& alfSliceParam ) { m_alfSliceParam = alfSliceParam; }
   AlfSliceParam&              getAlfSliceParam() { return m_alfSliceParam; }
+#if  JVET_L0266_HMVP
+  void                        initMotionLUTs       ();
+  void                        destroyMotionLUTs    ();
+  void                        resetMotionLUTs();
+  int                         getAvailableLUTMrgNum() const  { return m_MotionCandLuTs->currCnt; }
+  MotionInfo                  getMotionInfoFromLUTs(int MotCandIdx) const;
+  LuTMotionCand*              getMotionLUTs() { return m_MotionCandLuTs; }
+
+
+  void                        addMotionInfoToLUTs(LuTMotionCand* lutMC, MotionInfo newMi);
+
+  void                        updateMotionLUTs(LuTMotionCand* lutMC, CodingUnit & cu);
+  void                        copyMotionLUTs(LuTMotionCand* Src, LuTMotionCand* Dst);
+#endif
 
 protected:
   Picture*              xGetRefPic        (PicList& rcListPic, int poc);
diff --git a/source/Lib/CommonLib/TypeDef.h b/source/Lib/CommonLib/TypeDef.h
index 1c7469a6d0fad7564d5a4457b6fd423e700bce30..56026e5456505714d7dbc1443cf12c038631da04 100644
--- a/source/Lib/CommonLib/TypeDef.h
+++ b/source/Lib/CommonLib/TypeDef.h
@@ -50,6 +50,8 @@
 #include <assert.h>
 #include <cassert>
 
+#define JVET_L0266_HMVP                                   1 //History-based MVP
+
 #define JVET_L0553_FIX_INITQP                             1
 
 #define JVET_L0147_ALF_SUBSAMPLED_LAPLACIAN               1 // Subsampled Laplacian calculation
@@ -307,6 +309,10 @@
 #define DISTORTION_ESTIMATION_BITS                        8
 #define DISTORTION_PRECISION_ADJUSTMENT(x)                ((x>DISTORTION_ESTIMATION_BITS)? ((x)-DISTORTION_ESTIMATION_BITS) : 0)
 #endif
+#if JVET_L0266_HMVP
+#define MAX_NUM_HMVP_CANDS                                6
+#define MAX_NUM_HMVP_AVMPCANDS                            4  //should NOT be larger than MAX_NUM_HMVP_CANDS
+#endif
 
 // ====================================================================================================================
 // Error checks
diff --git a/source/Lib/CommonLib/UnitTools.cpp b/source/Lib/CommonLib/UnitTools.cpp
index 50a027e071af64297cd7c24ed9ea49e4c8c4366e..68f916b0e0e74710b89193fe9e7b3a19e7011de5 100644
--- a/source/Lib/CommonLib/UnitTools.cpp
+++ b/source/Lib/CommonLib/UnitTools.cpp
@@ -494,7 +494,90 @@ uint32_t PU::getFinalIntraMode( const PredictionUnit &pu, const ChannelType &chT
   return uiIntraMode;
 }
 
+#if JVET_L0266_HMVP
+bool PU::xCheckSimilarMotion(const int mergeCandIndex, const int prevCnt, const MergeCtx mergeCandList, bool hasPruned[MRG_MAX_NUM_CANDS])
+{
+  for (uint32_t ui = 0; ui < prevCnt; ui++)
+  {
+    if (hasPruned[ui])
+    {
+      continue;
+    }
+    if ( (mergeCandList.interDirNeighbours[ui] == mergeCandList.interDirNeighbours[mergeCandIndex]))
+    {
+      if (mergeCandList.interDirNeighbours[ui] == 3)
+      {
+        int offset0 = (ui << 1);
+        int offset1 = (mergeCandIndex << 1);
+        if (mergeCandList.mvFieldNeighbours[offset0].refIdx == mergeCandList.mvFieldNeighbours[offset1].refIdx &&
+            mergeCandList.mvFieldNeighbours[offset0 + 1].refIdx == mergeCandList.mvFieldNeighbours[offset1 + 1].refIdx &&
+            mergeCandList.mvFieldNeighbours[offset0].mv == mergeCandList.mvFieldNeighbours[offset1].mv &&
+            mergeCandList.mvFieldNeighbours[offset0 + 1].mv == mergeCandList.mvFieldNeighbours[offset1 + 1].mv
+          )
+        {
+          hasPruned[ui] = true;
+          return true;
+        }
+      }
+      else
+      {
+        int offset0 = (ui << 1) + mergeCandList.interDirNeighbours[ui] - 1;
+        int offset1 = (mergeCandIndex << 1) + mergeCandList.interDirNeighbours[ui] - 1;
+        if (mergeCandList.mvFieldNeighbours[offset0].refIdx == mergeCandList.mvFieldNeighbours[offset1].refIdx &&
+          mergeCandList.mvFieldNeighbours[offset0].mv == mergeCandList.mvFieldNeighbours[offset1].mv
+          )
+        {
+          hasPruned[ui] = true;
+          return true;
+        }
+      }
+    }
+  }
 
+  return false;
+}
+#if JVET_L0090_PAIR_AVG
+bool PU::addMergeHMVPCand(const Slice &slice, MergeCtx& mrgCtx, bool canFastExit, const int& mrgCandIdx, const uint32_t maxNumMergeCandMin1, int &cnt, const int prevCnt, bool isAvailableSubPu, unsigned subPuMvpPos)
+#else
+bool PU::addMergeHMVPCand(const Slice &slice, MergeCtx& mrgCtx, bool isCandInter[MRG_MAX_NUM_CANDS], bool canFastExit, const int& mrgCandIdx, const uint32_t maxNumMergeCandMin1, int &cnt, const int prevCnt, bool isAvailableSubPu, unsigned subPuMvpPos)
+#endif
+{
+  MotionInfo miNeighbor;
+  bool hasPruned[MRG_MAX_NUM_CANDS];
+  memset(hasPruned, false, MRG_MAX_NUM_CANDS * sizeof(bool));
+  if (isAvailableSubPu)
+  {
+    hasPruned[subPuMvpPos] = true;
+  }
+  int num_avai_candInLUT = slice.getAvailableLUTMrgNum();
+  for (int mrgIdx = 1; mrgIdx <= num_avai_candInLUT; mrgIdx++)
+  {
+    miNeighbor = slice.getMotionInfoFromLUTs(num_avai_candInLUT - mrgIdx);
+    mrgCtx.interDirNeighbours[cnt] = miNeighbor.interDir;
+    mrgCtx.mvFieldNeighbours[cnt << 1].setMvField(miNeighbor.mv[0], miNeighbor.refIdx[0]);
+    if (slice.isInterB())
+    {
+      mrgCtx.mvFieldNeighbours[(cnt << 1) + 1].setMvField(miNeighbor.mv[1], miNeighbor.refIdx[1]);
+    }
+    if (!xCheckSimilarMotion(cnt, prevCnt, mrgCtx, hasPruned))
+    {
+#if !JVET_L0090_PAIR_AVG
+      isCandInter[cnt] = true;
+#endif
+      if (mrgCandIdx == cnt && canFastExit)
+      {
+        return true;
+      }
+      cnt ++;
+      if (cnt  == maxNumMergeCandMin1)
+      {
+        break;
+      }
+    }
+  }
+  return false;
+}
+#endif
 void PU::getInterMergeCandidates( const PredictionUnit &pu, MergeCtx& mrgCtx, const int& mrgCandIdx )
 {
   const CodingStructure &cs  = *pu.cs;
@@ -923,7 +1006,21 @@ void PU::getInterMergeCandidates( const PredictionUnit &pu, MergeCtx& mrgCtx, co
   {
     return;
   }
-
+#if JVET_L0266_HMVP
+  int maxNumMergeCandMin1 = maxNumMergeCand - 1;
+  if (cnt != maxNumMergeCandMin1)
+  {
+#if JVET_L0090_PAIR_AVG
+    bool bFound = addMergeHMVPCand(slice, mrgCtx, canFastExit, mrgCandIdx, maxNumMergeCandMin1, cnt, cnt, isAvailableSubPu, subPuMvpPos);
+#else
+    bool bFound = addMergeHMVPCand(slice, mrgCtx, isCandInter, canFastExit, mrgCandIdx, maxNumMergeCandMin1, cnt, cnt, isAvailableSubPu, subPuMvpPos);
+#endif
+    if (bFound)
+    {
+      return;
+    }
+  }
+#endif
 #if JVET_L0090_PAIR_AVG
   // pairwise-average candidates
   {
@@ -1015,8 +1112,11 @@ void PU::getInterMergeCandidates( const PredictionUnit &pu, MergeCtx& mrgCtx, co
 
   uint32_t uiArrayAddr = cnt;
 #if !JVET_L0090_PAIR_AVG
+#if JVET_L0266_HMVP
+  uint32_t uiCutoff    = std::min( uiArrayAddr, 3u );
+#else
   uint32_t uiCutoff    = std::min( uiArrayAddr, 4u );
-
+#endif
   if (slice.isInterB())
   {
     static const uint32_t NUM_PRIORITY_LIST = 12;
@@ -1391,9 +1491,37 @@ void PU::fillMvpCand(PredictionUnit &pu, const RefPicList &eRefPicList, const in
 
     if ((C0Avail && getColocatedMVP(pu, eRefPicList, posC0, cColMv, refIdx_Col)) || getColocatedMVP(pu, eRefPicList, posC1, cColMv, refIdx_Col))
     {
+#if JVET_L0266_HMVP
+      if (pu.cu->imv != 0)
+      {
+        unsigned imvShift = pu.cu->imv << 1;
+        roundMV(cColMv, imvShift);
+      }
+      int i = 0;
+      for (i = 0; i < pInfo->numCand; i++)
+      {
+        if (cColMv == pInfo->mvCand[i])
+        {
+          break;
+        }
+      }
+      if (i == pInfo->numCand)
+      {
+        pInfo->mvCand[pInfo->numCand++] = cColMv;
+      }
+#else
       pInfo->mvCand[pInfo->numCand++] = cColMv;
+#endif
     }
   }
+#if JVET_L0266_HMVP
+  if (pInfo->numCand < AMVP_MAX_NUM_CANDS)
+  {
+    const int        currRefPOC = cs.slice->getRefPic(eRefPicList, refIdx)->getPOC();
+    const RefPicList eRefPicList2nd = (eRefPicList == REF_PIC_LIST_0) ? REF_PIC_LIST_1 : REF_PIC_LIST_0;
+    addAMVPHMVPCand(pu, eRefPicList, eRefPicList2nd, currRefPOC, *pInfo, pu.cu->imv);
+  }
+#endif
   if (pInfo->numCand > AMVP_MAX_NUM_CANDS)
   {
     pInfo->numCand = AMVP_MAX_NUM_CANDS;
@@ -1952,6 +2080,58 @@ bool PU::addMVPCandWithScaling( const PredictionUnit &pu, const RefPicList &eRef
   return false;
 }
 
+#if JVET_L0266_HMVP
+void PU::addAMVPHMVPCand(const PredictionUnit &pu, const RefPicList eRefPicList, const RefPicList eRefPicList2nd, const int currRefPOC, AMVPInfo &info, uint8_t imv)
+{
+  const Slice &slice = *(*pu.cs).slice;
+
+  MotionInfo neibMi;
+  int i = 0;
+  unsigned imvShift = imv << 1;
+
+  int num_avai_candInLUT = slice.getAvailableLUTMrgNum();
+  int num_allowedCand = std::min(MAX_NUM_HMVP_AVMPCANDS, num_avai_candInLUT);
+
+  for (int mrgIdx = 1; mrgIdx <= num_allowedCand; mrgIdx++)
+  {
+    if (info.numCand >= AMVP_MAX_NUM_CANDS)
+    {
+      return;
+    }
+    neibMi = slice.getMotionInfoFromLUTs(num_avai_candInLUT - mrgIdx);
+
+    for (int predictorSource = 0; predictorSource < 2; predictorSource++) // examine the indicated reference picture list, then if not available, examine the other list.
+    {
+      const RefPicList eRefPicListIndex = (predictorSource == 0) ? eRefPicList : eRefPicList2nd;
+      const int        neibRefIdx = neibMi.refIdx[eRefPicListIndex];
+
+      if (neibRefIdx >= 0 && currRefPOC == slice.getRefPOC(eRefPicListIndex, neibRefIdx))
+      {
+        Mv pmv = neibMi.mv[eRefPicListIndex];
+        if (imv != 0)
+        {
+          roundMV(pmv, imvShift);
+        }
+        for (i = 0; i < info.numCand; i++)
+        {
+          if (pmv == info.mvCand[i])
+          {
+            break;
+          }
+        }
+        if (i == info.numCand)
+        {
+          info.mvCand[info.numCand++] = pmv;
+          if (info.numCand >= AMVP_MAX_NUM_CANDS)
+          {
+            return;
+          }
+        }
+      }
+    }
+  }
+}
+#endif
 bool PU::isBipredRestriction(const PredictionUnit &pu)
 {
   const SPSNext &spsNext = pu.cs->sps->getSpsNext();
diff --git a/source/Lib/CommonLib/UnitTools.h b/source/Lib/CommonLib/UnitTools.h
index 3247170c6f357a977fdc39d16be213adee1a28ee..24ac8613dfa77fefc71444d3259dc400383b352b 100644
--- a/source/Lib/CommonLib/UnitTools.h
+++ b/source/Lib/CommonLib/UnitTools.h
@@ -118,6 +118,15 @@ namespace PU
   bool addMVPCandUnscaled             (const PredictionUnit &pu, const RefPicList &eRefPicList, const int &iRefIdx, const Position &pos, const MvpDir &eDir, AMVPInfo &amvpInfo, bool affine = false);
   bool addMVPCandWithScaling          (const PredictionUnit &pu, const RefPicList &eRefPicList, const int &iRefIdx, const Position &pos, const MvpDir &eDir, AMVPInfo &amvpInfo, bool affine = false);
   void xInheritedAffineMv             ( const PredictionUnit &pu, const PredictionUnit* puNeighbour, RefPicList eRefPicList, Mv rcMv[3] );
+#if JVET_L0266_HMVP
+  bool xCheckSimilarMotion(const int mergeCandIndex, const int prevCnt, const MergeCtx mergeCandList, bool hasPruned[MRG_MAX_NUM_CANDS]);
+#if JVET_L0090_PAIR_AVG
+  bool addMergeHMVPCand(const Slice &slice, MergeCtx& mrgCtx, bool canFastExit, const int& mrgCandIdx, const uint32_t maxNumMergeCandMin1, int &cnt, const int prevCnt, bool isAvailableSubPu, unsigned subPuMvpPos);
+#else
+  bool addMergeHMVPCand(const Slice &slice, MergeCtx& mrgCtx, bool isCandInter[MRG_MAX_NUM_CANDS], bool canFastExit, const int& mrgCandIdx, const uint32_t maxNumMergeCandMin1, int &cnt, const int prevCnt, bool isAvailableSubPu, unsigned subPuMvpPos);
+#endif
+  void addAMVPHMVPCand(const PredictionUnit &pu, const RefPicList eRefPicList, const RefPicList eRefPicList2nd, const int currRefPOC, AMVPInfo &info, uint8_t imv);
+#endif
   bool isBipredRestriction            (const PredictionUnit &pu);
   void spanMotionInfo                 (      PredictionUnit &pu, const MergeCtx &mrgCtx = MergeCtx() );
   void applyImv                       (      PredictionUnit &pu, MergeCtx &mrgCtx, InterPrediction *interPred = NULL );
diff --git a/source/Lib/DecoderLib/DecCu.cpp b/source/Lib/DecoderLib/DecCu.cpp
index 8fb1c75ba89f278c1a621440ce9e8fd16f45bade..19a8c333be7a94f264fae385d01f72b380b5e6cd 100644
--- a/source/Lib/DecoderLib/DecCu.cpp
+++ b/source/Lib/DecoderLib/DecCu.cpp
@@ -316,6 +316,9 @@ void DecCu::xReconInter(CodingUnit &cu)
 {
   // inter prediction
   m_pcInterPred->motionCompensation( cu );
+#if JVET_L0266_HMVP
+  cu.slice->updateMotionLUTs(cu.slice->getMotionLUTs(), cu);
+#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/EncoderLib/EncCu.cpp b/source/Lib/EncoderLib/EncCu.cpp
index 926841c1057fb2740c447752eab355c7ab3f2bc3..1030f4050aa563fe1f37c6959aa860946fd3faa5 100644
--- a/source/Lib/EncoderLib/EncCu.cpp
+++ b/source/Lib/EncoderLib/EncCu.cpp
@@ -78,10 +78,21 @@ void EncCu::create( EncCfg* encCfg )
   m_pTempCS = new CodingStructure**  [numWidths];
   m_pBestCS = new CodingStructure**  [numWidths];
 
+#if JVET_L0266_HMVP
+  m_pTempMotLUTs = new LuTMotionCand**[numWidths];
+  m_pBestMotLUTs = new LuTMotionCand**[numWidths];
+  m_pSplitTempMotLUTs = new LuTMotionCand**[numWidths];
+#endif
+
   for( unsigned w = 0; w < numWidths; w++ )
   {
     m_pTempCS[w] = new CodingStructure*  [numHeights];
     m_pBestCS[w] = new CodingStructure*  [numHeights];
+#if JVET_L0266_HMVP
+    m_pTempMotLUTs[w] = new LuTMotionCand*[numHeights];
+    m_pBestMotLUTs[w] = new LuTMotionCand*[numHeights];
+    m_pSplitTempMotLUTs[w] = new LuTMotionCand*[numHeights];
+#endif
 
     for( unsigned h = 0; h < numHeights; h++ )
     {
@@ -95,11 +106,32 @@ void EncCu::create( EncCfg* encCfg )
 
         m_pTempCS[w][h]->create( chromaFormat, Area( 0, 0, width, height ), false );
         m_pBestCS[w][h]->create( chromaFormat, Area( 0, 0, width, height ), false );
+#if JVET_L0266_HMVP
+        m_pTempMotLUTs[w][h] = new LuTMotionCand ;
+        m_pBestMotLUTs[w][h] = new LuTMotionCand ;
+        m_pSplitTempMotLUTs[w][h] = new LuTMotionCand;
+          m_pSplitTempMotLUTs[w][h]->currCnt = 0;
+          m_pSplitTempMotLUTs[w][h]->m_MotionCand = nullptr;
+          m_pSplitTempMotLUTs[w][h]->m_MotionCand = new MotionInfo[MAX_NUM_HMVP_CANDS];
+
+         m_pTempMotLUTs[w][h]->currCnt = 0;
+          m_pTempMotLUTs[w][h]->m_MotionCand = nullptr;
+          m_pTempMotLUTs[w][h]->m_MotionCand = new MotionInfo[MAX_NUM_HMVP_CANDS];
+
+          m_pBestMotLUTs[w][h]->currCnt = 0;
+          m_pBestMotLUTs[w][h]->m_MotionCand = nullptr;
+          m_pBestMotLUTs[w][h]->m_MotionCand = new MotionInfo[MAX_NUM_HMVP_CANDS];
+#endif
       }
       else
       {
         m_pTempCS[w][h] = nullptr;
         m_pBestCS[w][h] = nullptr;
+#if JVET_L0266_HMVP
+        m_pTempMotLUTs[w][h] = nullptr;
+        m_pBestMotLUTs[w][h] = nullptr;
+        m_pSplitTempMotLUTs[w][h] = nullptr;
+#endif
       }
     }
   }
@@ -180,15 +212,46 @@ void EncCu::destroy()
 
         delete m_pBestCS[w][h];
         delete m_pTempCS[w][h];
+#if JVET_L0266_HMVP
+        if (m_pTempMotLUTs[w][h])
+        {
+          delete[] m_pTempMotLUTs[w][h]->m_MotionCand;
+          m_pTempMotLUTs[w][h]->m_MotionCand = nullptr;
+          delete[] m_pTempMotLUTs[w][h];
+        }
+        if (m_pBestMotLUTs[w][h])
+        {
+          delete[] m_pBestMotLUTs[w][h]->m_MotionCand;
+          m_pBestMotLUTs[w][h]->m_MotionCand = nullptr;
+          delete[] m_pBestMotLUTs[w][h];
+        }
+
+        if (m_pSplitTempMotLUTs[w][h])
+        {
+          delete[] m_pSplitTempMotLUTs[w][h]->m_MotionCand;
+          m_pSplitTempMotLUTs[w][h]->m_MotionCand = nullptr;
+          delete[] m_pSplitTempMotLUTs[w][h];
+        }
+#endif
       }
     }
 
     delete[] m_pTempCS[w];
     delete[] m_pBestCS[w];
+#if JVET_L0266_HMVP
+    delete[] m_pBestMotLUTs[w];
+    delete[] m_pTempMotLUTs[w];
+    delete[] m_pSplitTempMotLUTs[w];
+#endif
   }
 
   delete[] m_pBestCS; m_pBestCS = nullptr;
   delete[] m_pTempCS; m_pTempCS = nullptr;
+#if JVET_L0266_HMVP
+  delete[] m_pSplitTempMotLUTs; m_pSplitTempMotLUTs = nullptr;
+  delete[] m_pBestMotLUTs; m_pBestMotLUTs = nullptr;
+  delete[] m_pTempMotLUTs; m_pTempMotLUTs = nullptr;
+#endif
 
 #if REUSE_CU_RESULTS
   m_modeCtrl->destroy();
@@ -292,6 +355,12 @@ void EncCu::compressCtu( CodingStructure& cs, const UnitArea& area, const unsign
 
   CodingStructure *tempCS = m_pTempCS[gp_sizeIdxInfo->idxFrom( area.lumaSize().width )][gp_sizeIdxInfo->idxFrom( area.lumaSize().height )];
   CodingStructure *bestCS = m_pBestCS[gp_sizeIdxInfo->idxFrom( area.lumaSize().width )][gp_sizeIdxInfo->idxFrom( area.lumaSize().height )];
+#if JVET_L0266_HMVP
+  LuTMotionCand *tempMotCandLUTs = m_pTempMotLUTs[gp_sizeIdxInfo->idxFrom(area.lumaSize().width)][gp_sizeIdxInfo->idxFrom(area.lumaSize().height)];
+  LuTMotionCand *bestMotCandLUTs = m_pBestMotLUTs[gp_sizeIdxInfo->idxFrom(area.lumaSize().width)][gp_sizeIdxInfo->idxFrom(area.lumaSize().height)];
+  cs.slice->copyMotionLUTs(cs.slice->getMotionLUTs(), tempMotCandLUTs);
+  cs.slice->copyMotionLUTs(cs.slice->getMotionLUTs(), bestMotCandLUTs);
+#endif
 
   cs.initSubStructure( *tempCS, partitioner->chType, partitioner->currArea(), false );
   cs.initSubStructure( *bestCS, partitioner->chType, partitioner->currArea(), false );
@@ -299,12 +368,20 @@ void EncCu::compressCtu( CodingStructure& cs, const UnitArea& area, const unsign
   tempCS->baseQP       = bestCS->baseQP       = currQP[CH_L];
   tempCS->prevQP[CH_L] = bestCS->prevQP[CH_L] = prevQP[CH_L];
 
-  xCompressCU( tempCS, bestCS, *partitioner );
+  xCompressCU( tempCS, bestCS, *partitioner
+#if JVET_L0266_HMVP
+    , tempMotCandLUTs
+    , bestMotCandLUTs
+#endif
+  );
 
 
   // all signals were already copied during compression if the CTU was split - at this point only the structures are copied to the top level CS
   const bool copyUnsplitCTUSignals = bestCS->cus.size() == 1 && KEEP_PRED_AND_RESI_SIGNALS;
   cs.useSubStructure( *bestCS, partitioner->chType, CS::getArea( *bestCS, area, partitioner->chType ), copyUnsplitCTUSignals, false, false, copyUnsplitCTUSignals );
+#if JVET_L0266_HMVP
+  cs.slice->copyMotionLUTs(bestMotCandLUTs, cs.slice->getMotionLUTs());
+#endif
 
   if( !cs.pcv->ISingleTree && cs.slice->isIRAP() && cs.pcv->chrFormat != CHROMA_400 )
   {
@@ -318,7 +395,12 @@ void EncCu::compressCtu( CodingStructure& cs, const UnitArea& area, const unsign
     tempCS->baseQP       = bestCS->baseQP       = currQP[CH_C];
     tempCS->prevQP[CH_C] = bestCS->prevQP[CH_C] = prevQP[CH_C];
 
-    xCompressCU( tempCS, bestCS, *partitioner );
+    xCompressCU( tempCS, bestCS, *partitioner
+#if JVET_L0266_HMVP
+      , tempMotCandLUTs
+      , bestMotCandLUTs
+#endif
+    );
 
     const bool copyUnsplitCTUSignals = bestCS->cus.size() == 1 && KEEP_PRED_AND_RESI_SIGNALS;
     cs.useSubStructure( *bestCS, partitioner->chType, CS::getArea( *bestCS, area, partitioner->chType ), copyUnsplitCTUSignals, false, false, copyUnsplitCTUSignals );
@@ -465,8 +547,16 @@ int  EncCu::updateCtuDataISlice(const CPelBuf buf)
   return( iSumHad );
 }
 
+#if JVET_L0266_HMVP
+bool EncCu::xCheckBestMode( CodingStructure *&tempCS, CodingStructure *&bestCS, Partitioner &partitioner, const EncTestMode& encTestMode )
+#else
 void EncCu::xCheckBestMode( CodingStructure *&tempCS, CodingStructure *&bestCS, Partitioner &partitioner, const EncTestMode& encTestMode )
+#endif
 {
+#if JVET_L0266_HMVP
+  bool bSwitch = false;
+#endif
+
   if( !tempCS->cus.empty() )
   {
     if( tempCS->cus.size() == 1 )
@@ -497,14 +587,26 @@ void EncCu::xCheckBestMode( CodingStructure *&tempCS, CodingStructure *&bestCS,
       std::swap( tempCS, bestCS );
       // store temp best CI for next CU coding
       m_CurrCtx->best = m_CABACEstimator->getCtx();
+#if JVET_L0266_HMVP
+      bSwitch = true;
+#endif
     }
   }
 
   // reset context states
   m_CABACEstimator->getCtx() = m_CurrCtx->start;
+#if JVET_L0266_HMVP
+  return bSwitch;
+#endif
+
 }
 
-void EncCu::xCompressCU( CodingStructure *&tempCS, CodingStructure *&bestCS, Partitioner &partitioner )
+void EncCu::xCompressCU( CodingStructure *&tempCS, CodingStructure *&bestCS, Partitioner &partitioner
+#if JVET_L0266_HMVP
+  , LuTMotionCand* &tempMotCandLUTs
+  , LuTMotionCand* &bestMotCandLUTs
+#endif
+)
 {
 #if ENABLE_SPLIT_PARALLELISM
   CHECK( m_dataId != tempCS->picture->scheduler.getDataId(), "Working in the wrong dataId!" );
@@ -557,6 +659,12 @@ void EncCu::xCompressCU( CodingStructure *&tempCS, CodingStructure *&bestCS, Par
     m_modeCtrl->finishCULevel( partitioner );
     return;
   }
+#if JVET_L0266_HMVP
+	if (!slice.isIntra())
+	{
+		tempCS->slice->copyMotionLUTs(tempMotCandLUTs, tempCS->slice->getMotionLUTs());
+	}
+#endif
 
   DTRACE_UPDATE( g_trace_ctx, std::make_pair( "cux", uiLPelX ) );
   DTRACE_UPDATE( g_trace_ctx, std::make_pair( "cuy", uiTPelY ) );
@@ -618,7 +726,13 @@ void EncCu::xCompressCU( CodingStructure *&tempCS, CodingStructure *&bestCS, Par
     else if( isModeSplit( currTestMode ) )
     {
 
-      xCheckModeSplit( tempCS, bestCS, partitioner, currTestMode );
+      xCheckModeSplit( tempCS, bestCS, partitioner, currTestMode
+#if JVET_L0266_HMVP
+        , tempMotCandLUTs
+        , bestMotCandLUTs
+        , partitioner.currArea()
+#endif
+      );
     }
     else
     {
@@ -644,7 +758,12 @@ void EncCu::xCompressCU( CodingStructure *&tempCS, CodingStructure *&bestCS, Par
 
   // QP from last processed CU for further processing
   bestCS->prevQP[partitioner.chType] = bestCS->cus.back()->qp;
-
+#if JVET_L0266_HMVP
+  if (!slice.isIntra() && bestCS->cus.size() == 1 && bestCS->cus.back()->predMode == MODE_INTER && bestCS->area == *bestCS->cus.back())
+  {
+    bestCS->slice->updateMotionLUTs(bestMotCandLUTs, (*bestCS->cus.back()));
+  }
+#endif
   bestCS->picture->getRecoBuf( currCsArea ).copyFrom( bestCS->getRecoBuf( currCsArea ) );
   m_modeCtrl->finishCULevel( partitioner );
 
@@ -892,7 +1011,13 @@ void EncCu::copyState( EncCu* other, Partitioner& partitioner, const UnitArea& c
 }
 #endif
 
-void EncCu::xCheckModeSplit(CodingStructure *&tempCS, CodingStructure *&bestCS, Partitioner &partitioner, const EncTestMode& encTestMode)
+void EncCu::xCheckModeSplit(CodingStructure *&tempCS, CodingStructure *&bestCS, Partitioner &partitioner, const EncTestMode& encTestMode
+#if JVET_L0266_HMVP
+  , LuTMotionCand* &tempMotCandLUTs
+  , LuTMotionCand* &bestMotCandLUTs
+  , UnitArea  parArea
+#endif
+)
 {
   const int qp                = encTestMode.qp;
   const PPS &pps              = *tempCS->pps;
@@ -901,6 +1026,13 @@ void EncCu::xCheckModeSplit(CodingStructure *&tempCS, CodingStructure *&bestCS,
   const int oldPrevQp         = tempCS->prevQP[partitioner.chType];
   const uint32_t currDepth        = partitioner.currDepth;
 
+#if JVET_L0266_HMVP
+  const unsigned wParIdx = gp_sizeIdxInfo->idxFrom(parArea.lwidth());
+  const unsigned hParIdx = gp_sizeIdxInfo->idxFrom(parArea.lheight());
+
+  tempCS->slice->copyMotionLUTs(tempMotCandLUTs, m_pSplitTempMotLUTs[wParIdx][hParIdx]);
+#endif
+
   const PartSplit split = getPartSplit( encTestMode );
 
   CHECK( split == CU_DONT_SPLIT, "No proper split provided!" );
@@ -961,8 +1093,19 @@ void EncCu::xCheckModeSplit(CodingStructure *&tempCS, CodingStructure *&bestCS,
 
       tempCS->initSubStructure( *tempSubCS, partitioner.chType, subCUArea, false );
       tempCS->initSubStructure( *bestSubCS, partitioner.chType, subCUArea, false );
+#if JVET_L0266_HMVP
+      LuTMotionCand *tempSubMotCandLUTs = m_pTempMotLUTs[wIdx][hIdx];
+      LuTMotionCand *bestSubMotCandLUTs = m_pBestMotLUTs[wIdx][hIdx];
+      tempCS->slice->copyMotionLUTs(tempMotCandLUTs, tempSubMotCandLUTs);
+      tempCS->slice->copyMotionLUTs(tempMotCandLUTs, bestSubMotCandLUTs);
+#endif
 
-      xCompressCU( tempSubCS, bestSubCS, partitioner );
+      xCompressCU( tempSubCS, bestSubCS, partitioner
+#if JVET_L0266_HMVP
+        , tempSubMotCandLUTs
+        , bestSubMotCandLUTs
+#endif
+      );
 
       if( bestSubCS->cost == MAX_DOUBLE )
       {
@@ -970,12 +1113,25 @@ void EncCu::xCheckModeSplit(CodingStructure *&tempCS, CodingStructure *&bestCS,
         tempCS->cost = MAX_DOUBLE;
         m_CurrCtx--;
         partitioner.exitCurrSplit();
+#if JVET_L0266_HMVP
+        bool bUpdate =
+#endif
         xCheckBestMode( tempCS, bestCS, partitioner, encTestMode );
+
+#if JVET_L0266_HMVP
+        if (bUpdate)
+        {
+          std::swap(tempMotCandLUTs, bestMotCandLUTs);
+        }
+#endif
         return;
       }
 
       bool keepResi = KEEP_PRED_AND_RESI_SIGNALS;
       tempCS->useSubStructure( *bestSubCS, partitioner.chType, CS::getArea( *tempCS, subCUArea, partitioner.chType ), KEEP_PRED_AND_RESI_SIGNALS, true, keepResi, keepResi );
+#if JVET_L0266_HMVP
+      tempCS->slice->copyMotionLUTs(bestSubMotCandLUTs, tempMotCandLUTs);
+#endif
 
       if(currDepth < pps.getMaxCuDQPDepth())
       {
@@ -1079,8 +1235,22 @@ void EncCu::xCheckModeSplit(CodingStructure *&tempCS, CodingStructure *&bestCS,
 
 
   // RD check for sub partitioned coding structure.
+#if JVET_L0266_HMVP
+  bool bUpdate =
+#endif
   xCheckBestMode( tempCS, bestCS, partitioner, encTestMode );
 
+#if JVET_L0266_HMVP
+	if (!slice.isIntra())
+	{
+		if (bUpdate)
+		{
+			std::swap(tempMotCandLUTs, bestMotCandLUTs);
+		}
+		tempCS->slice->copyMotionLUTs(m_pSplitTempMotLUTs[wParIdx][hParIdx], tempMotCandLUTs);
+	}
+#endif
+
   tempCS->releaseIntermediateData();
 
   tempCS->prevQP[partitioner.chType] = oldPrevQp;
diff --git a/source/Lib/EncoderLib/EncCu.h b/source/Lib/EncoderLib/EncCu.h
index d8131f4532a7f5b5e278e8284af1d8cf9275b42c..8269b83611d264ea6699558dec19556dd9cd5671 100644
--- a/source/Lib/EncoderLib/EncCu.h
+++ b/source/Lib/EncoderLib/EncCu.h
@@ -95,6 +95,11 @@ private:
 
   CodingStructure    ***m_pTempCS;
   CodingStructure    ***m_pBestCS;
+#if JVET_L0266_HMVP
+  LuTMotionCand      ***m_pTempMotLUTs;
+  LuTMotionCand      ***m_pBestMotLUTs;
+  LuTMotionCand      ***m_pSplitTempMotLUTs;
+#endif
   //  Access channel
   EncCfg*               m_pcEncCfg;
   IntraSearch*          m_pcIntraSearch;
@@ -167,15 +172,31 @@ public:
 
 protected:
 
-  void xCompressCU            ( CodingStructure *&tempCS, CodingStructure *&bestCS, Partitioner &pm );
+  void xCompressCU            ( CodingStructure *&tempCS, CodingStructure *&bestCS, Partitioner &pm
+#if JVET_L0266_HMVP
+    , LuTMotionCand* &tempMotCandLUTs
+    , LuTMotionCand* &bestMotCandLUTs
+#endif
+  );
 #if ENABLE_SPLIT_PARALLELISM
   void xCompressCUParallel    ( CodingStructure *&tempCS, CodingStructure *&bestCS, Partitioner &pm );
   void copyState              ( EncCu* other, Partitioner& pm, const UnitArea& currArea, const bool isDist );
 #endif
 
-  void xCheckBestMode         ( CodingStructure *&tempCS, CodingStructure *&bestCS, Partitioner &pm, const EncTestMode& encTestmode );
+#if JVET_L0266_HMVP
+  bool
+#else
+  void
+#endif
+    xCheckBestMode         ( CodingStructure *&tempCS, CodingStructure *&bestCS, Partitioner &pm, const EncTestMode& encTestmode );
 
-  void xCheckModeSplit        ( CodingStructure *&tempCS, CodingStructure *&bestCS, Partitioner &pm, const EncTestMode& encTestMode );
+  void xCheckModeSplit        ( CodingStructure *&tempCS, CodingStructure *&bestCS, Partitioner &pm, const EncTestMode& encTestMode
+#if JVET_L0266_HMVP
+    , LuTMotionCand* &tempMotCandLUTs
+    , LuTMotionCand* &bestMotCandLUTs
+    , UnitArea  parArea
+#endif
+  );
 
   void xCheckRDCostIntra      ( CodingStructure *&tempCS, CodingStructure *&bestCS, Partitioner &pm, const EncTestMode& encTestMode );
   void xCheckIntraPCM         ( CodingStructure *&tempCS, CodingStructure *&bestCS, Partitioner &pm, const EncTestMode& encTestMode );