diff --git a/source/Lib/CommonLib/ContextModelling.cpp b/source/Lib/CommonLib/ContextModelling.cpp
index 4502c3d655f7ac5e2ee11f7fd618c7bdb7249a0b..aa3f10f470a911649261488912b47816bee58ce0 100644
--- a/source/Lib/CommonLib/ContextModelling.cpp
+++ b/source/Lib/CommonLib/ContextModelling.cpp
@@ -102,6 +102,12 @@ CoeffCodingContext::CoeffCodingContext(const TransformUnit& tu, ComponentID comp
   , m_sigFlagCtxSet             { Ctx::SigFlag[m_chType], Ctx::SigFlag[m_chType+2], Ctx::SigFlag[m_chType+4] }
   , m_parFlagCtxSet             ( Ctx::ParFlag[m_chType] )
   , m_gtxFlagCtxSet             { Ctx::GtxFlag[m_chType], Ctx::GtxFlag[m_chType+2] }
+#if JVET_N0280_RESIDUAL_CODING_TS
+  , m_sigGroupCtxIdTS           (-1)
+  , m_tsSigFlagCtxSet           ( Ctx::TsSigFlag )
+  , m_tsParFlagCtxSet           ( Ctx::TsParFlag )
+  , m_tsGtxFlagCtxSet           ( Ctx::TsGtxFlag )
+#endif
   , m_sigCoeffGroupFlag         ()
 {
   // LOGTODO
@@ -151,6 +157,11 @@ void CoeffCodingContext::initSubblock( int SubsetId, bool sigGroupFlag )
   unsigned  sigRight  = unsigned( ( CGPosX + 1 ) < m_widthInGroups  ? m_sigCoeffGroupFlag[ m_subSetPos + 1               ] : false );
   unsigned  sigLower  = unsigned( ( CGPosY + 1 ) < m_heightInGroups ? m_sigCoeffGroupFlag[ m_subSetPos + m_widthInGroups ] : false );
   m_sigGroupCtxId     = Ctx::SigCoeffGroup[m_chType]( sigRight | sigLower );
+#if JVET_N0280_RESIDUAL_CODING_TS
+  unsigned  sigLeft   = unsigned( int( CGPosX - 1 ) > 0 ? m_sigCoeffGroupFlag[m_subSetPos - 1              ] : false );
+  unsigned  sigAbove  = unsigned( int( CGPosY - 1 ) > 0 ? m_sigCoeffGroupFlag[m_subSetPos - m_widthInGroups] : false );
+  m_sigGroupCtxIdTS   = Ctx::TsSigCoeffGroup( sigLeft  + sigAbove );
+#endif
 }
 
 
diff --git a/source/Lib/CommonLib/ContextModelling.h b/source/Lib/CommonLib/ContextModelling.h
index 7a2b496c3489814bbedca2bce31b4daa81dcb79f..f6e4946bfce367a302e5d74c943536d64e1f4079 100644
--- a/source/Lib/CommonLib/ContextModelling.h
+++ b/source/Lib/CommonLib/ContextModelling.h
@@ -61,6 +61,12 @@ public:
 public:
   void  resetSigGroup   ()                      { m_sigCoeffGroupFlag.reset( m_subSetPos ); }
   void  setSigGroup     ()                      { m_sigCoeffGroupFlag.set( m_subSetPos ); }
+#if JVET_N0280_RESIDUAL_CODING_TS
+  bool  noneSigGroup    ()                      { return m_sigCoeffGroupFlag.none(); }
+  int   lastSubSet      ()                      { return ( maxNumCoeff() - 1 ) >> log2CGSize(); }
+  bool  isLastSubSet    ()                      { return lastSubSet() == m_subSetId; }
+  bool  only1stSigGroup ()                      { return m_sigCoeffGroupFlag.count()-m_sigCoeffGroupFlag[lastSubSet()]==0; }
+#endif
   void  setScanPosLast  ( int       posLast )   { m_scanPosLast = posLast; }
 public:
   ComponentID     compID          ()                        const { return m_compID; }
@@ -96,7 +102,14 @@ public:
   unsigned        maxLastPosY     ()                        const { return m_maxLastPosY; }
   unsigned        lastXCtxId      ( unsigned  posLastX  )   const { return m_CtxSetLastX( m_lastOffsetX + ( posLastX >> m_lastShiftX ) ); }
   unsigned        lastYCtxId      ( unsigned  posLastY  )   const { return m_CtxSetLastY( m_lastOffsetY + ( posLastY >> m_lastShiftY ) ); }
+#if JVET_N0280_RESIDUAL_CODING_TS
+  bool            isContextCoded  ()                              { return --m_remainingContextBins >= 0; }
+  int             numCtxBins      ()                        const { return   m_remainingContextBins;      }
+  void            setNumCtxBins   ( int n )                       {          m_remainingContextBins  = n; }
+  unsigned        sigGroupCtxId   ( bool ts = false     )   const { return ts ? m_sigGroupCtxIdTS : m_sigGroupCtxId; }
+#else
   unsigned        sigGroupCtxId   ()                        const { return m_sigGroupCtxId; }
+#endif
 
   unsigned sigCtxIdAbs( int scanPos, const TCoeff* coeff, const int state )
   {
@@ -189,6 +202,56 @@ public:
 #endif
   }
 
+#if JVET_N0280_RESIDUAL_CODING_TS
+  unsigned sigCtxIdAbsTS( int scanPos, const TCoeff* coeff )
+  {
+    const uint32_t  posY   = m_scan[scanPos].y;
+    const uint32_t  posX   = m_scan[scanPos].x;
+    const TCoeff*   posC   = coeff + posX + posY * m_width;
+    int             numPos = 0;
+#define UPDATE(x) {int a=abs(x);numPos+=!!a;}
+    if( posX > 0 )
+    {
+      UPDATE( posC[-1] );
+    }
+    if( posY > 0 )
+    {
+      UPDATE( posC[-(int)m_width] );
+    }
+#undef UPDATE
+
+    return m_tsSigFlagCtxSet( numPos );
+  }
+
+  unsigned parityCtxIdAbsTS   ()                  const { return m_tsParFlagCtxSet(      0 ); }
+  unsigned greaterXCtxIdAbsTS ( uint8_t offset )  const { return m_tsGtxFlagCtxSet( offset ); }
+
+  unsigned templateAbsSumTS( int scanPos, const TCoeff* coeff )
+  {
+    const uint32_t  posY  = m_scan[scanPos].y;
+    const uint32_t  posX  = m_scan[scanPos].x;
+    const TCoeff*   posC  = coeff + posX + posY * m_width;
+    int             sum   = 0;
+    if (posX > 0)
+    {
+      sum += abs(posC[-1]);
+    }
+    if (posY > 0)
+    {
+      sum += abs(posC[-(int)m_width]);
+    }
+
+    const uint32_t auiGoRicePars[32] =
+    {
+      0, 0, 0, 0,
+      0, 0, 0, 0, 0, 0,
+      0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+      1, 1, 1, 2, 2, 2, 2, 2, 2, 2
+    };
+
+    return auiGoRicePars[ std::min(sum, 31) ];
+  }
+#endif
 
 private:
   // constant
@@ -235,6 +298,13 @@ private:
   CtxSet                    m_sigFlagCtxSet[3];
   CtxSet                    m_parFlagCtxSet;
   CtxSet                    m_gtxFlagCtxSet[2];
+#if JVET_N0280_RESIDUAL_CODING_TS
+  unsigned                  m_sigGroupCtxIdTS;
+  CtxSet                    m_tsSigFlagCtxSet;
+  CtxSet                    m_tsParFlagCtxSet;
+  CtxSet                    m_tsGtxFlagCtxSet;
+  int                       m_remainingContextBins;
+#endif
   std::bitset<MLS_GRP_NUM>  m_sigCoeffGroupFlag;
 };
 
diff --git a/source/Lib/CommonLib/Contexts.cpp b/source/Lib/CommonLib/Contexts.cpp
index 1d1d827602c995550e22c5a85c9b68037c032a45..48c8f7d981f7aef3a5ad5ae016340306f9e08807 100644
--- a/source/Lib/CommonLib/Contexts.cpp
+++ b/source/Lib/CommonLib/Contexts.cpp
@@ -813,6 +813,63 @@ const CtxSet ContextSetCfg::JointCbCrFlag = ContextSetCfg::addCtxSet
 });
 #endif
 
+#if JVET_N0280_RESIDUAL_CODING_TS
+const CtxSet ContextSetCfg::TsSigCoeffGroup =
+{
+  ContextSetCfg::addCtxSet
+  ({
+    {  CNU, CNU, CNU,  },
+    {  CNU, CNU, CNU,  },
+    {  CNU, CNU, CNU,  },
+    {  DWS, DWS, DWS,  },
+  }),
+};
+
+const CtxSet ContextSetCfg::TsSigFlag =
+{
+  ContextSetCfg::addCtxSet
+  ({
+    {  CNU, CNU, CNU, },
+    {  CNU, CNU, CNU, },
+    {  CNU, CNU, CNU, },
+    {  DWS, DWS, DWS, },
+  }),
+};
+
+const CtxSet ContextSetCfg::TsParFlag =
+{
+  ContextSetCfg::addCtxSet
+  ({
+    {  CNU  },
+    {  CNU  },
+    {  CNU  },
+    {  DWS  },
+  }),
+};
+
+const CtxSet ContextSetCfg::TsGtxFlag =
+{
+  ContextSetCfg::addCtxSet
+  ({
+    {  CNU, CNU, CNU, CNU, CNU, CNU, CNU, CNU, CNU, CNU, CNU, CNU, CNU, CNU, CNU,  },
+    {  CNU, CNU, CNU, CNU, CNU, CNU, CNU, CNU, CNU, CNU, CNU, CNU, CNU, CNU, CNU,  },
+    {  CNU, CNU, CNU, CNU, CNU, CNU, CNU, CNU, CNU, CNU, CNU, CNU, CNU, CNU, CNU,  },
+    {  DWS, DWS, DWS, DWS, DWS, DWS, DWS, DWS, DWS, DWS, DWS, DWS, DWS, DWS, DWS,  },
+  }),
+};
+
+const CtxSet ContextSetCfg::TsResidualSign =
+{
+  ContextSetCfg::addCtxSet
+  ({
+    {  CNU,  },
+    {  CNU,  },
+    {  CNU,  },
+    {  DWS,  },
+   }),
+};
+#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 a015dce3933c9727b55a572cd4abafa288e46b1f..9841ca34eb162d610a5a74e6de9fd1c9f8394b86 100644
--- a/source/Lib/CommonLib/Contexts.h
+++ b/source/Lib/CommonLib/Contexts.h
@@ -228,6 +228,13 @@ public:
   static const CtxSet   SigFlag         [6];    // [ ChannelType + State ]
   static const CtxSet   ParFlag         [2];    // [ ChannelType ]
   static const CtxSet   GtxFlag         [4];    // [ ChannelType + x ]
+#if JVET_N0280_RESIDUAL_CODING_TS
+  static const CtxSet   TsSigCoeffGroup;
+  static const CtxSet   TsSigFlag;
+  static const CtxSet   TsParFlag;
+  static const CtxSet   TsGtxFlag;
+  static const CtxSet   TsResidualSign;
+#endif
   static const CtxSet   MVPIdx;
   static const CtxSet   SaoMergeFlag;
   static const CtxSet   SaoTypeIdx;
diff --git a/source/Lib/CommonLib/DepQuant.cpp b/source/Lib/CommonLib/DepQuant.cpp
index 2fc1ab08566713fc491748cdafc1f20135d058fa..5331f35170ed9aee33fca88227beff634b9d0909 100644
--- a/source/Lib/CommonLib/DepQuant.cpp
+++ b/source/Lib/CommonLib/DepQuant.cpp
@@ -1656,7 +1656,11 @@ DepQuant::~DepQuant()
 
 void DepQuant::quant( TransformUnit &tu, const ComponentID &compID, const CCoeffBuf &pSrc, TCoeff &uiAbsSum, const QpParam &cQP, const Ctx& ctx )
 {
+#if JVET_N0280_RESIDUAL_CODING_TS
+  if( tu.cs->slice->getDepQuantEnabledFlag() && tu.mtsIdx != 1 )
+#else
   if( tu.cs->slice->getDepQuantEnabledFlag() )
+#endif
   {
     static_cast<DQIntern::DepQuant*>(p)->quant( tu, pSrc, compID, cQP, Quant::m_dLambda, ctx, uiAbsSum );
   }
@@ -1668,7 +1672,11 @@ void DepQuant::quant( TransformUnit &tu, const ComponentID &compID, const CCoeff
 
 void DepQuant::dequant( const TransformUnit &tu, CoeffBuf &dstCoeff, const ComponentID &compID, const QpParam &cQP )
 {
+#if JVET_N0280_RESIDUAL_CODING_TS
+  if( tu.cs->slice->getDepQuantEnabledFlag() && tu.mtsIdx != 1 )
+#else
   if( tu.cs->slice->getDepQuantEnabledFlag() )
+#endif
   {
     static_cast<DQIntern::DepQuant*>(p)->dequant( tu, dstCoeff, compID, cQP );
   }
diff --git a/source/Lib/CommonLib/QuantRDOQ.cpp b/source/Lib/CommonLib/QuantRDOQ.cpp
index f0c880324ed192c26afa429e0e037ae94bc72a56..cdeea7d257e47cd513192cf38c8a93c2d5fdb963 100644
--- a/source/Lib/CommonLib/QuantRDOQ.cpp
+++ b/source/Lib/CommonLib/QuantRDOQ.cpp
@@ -555,7 +555,18 @@ void QuantRDOQ::quant(TransformUnit &tu, const ComponentID &compID, const CCoeff
     if (!m_useSelectiveRDOQ || xNeedRDOQ(tu, compID, piCoef, cQP))
     {
 #endif
+#if JVET_N0280_RESIDUAL_CODING_TS
+      if( isLuma( compID ) && useTransformSkip )
+      {
+        xRateDistOptQuantTS( tu, compID, pSrc, uiAbsSum, cQP, ctx );
+      }
+      else
+      {
+        xRateDistOptQuant( tu, compID, pSrc, uiAbsSum, cQP, ctx );
+      }
+#else
       xRateDistOptQuant( tu, compID, pSrc, uiAbsSum, cQP, ctx );
+#endif
 #if T0196_SELECTIVE_RDOQ
     }
     else
@@ -1232,4 +1243,341 @@ void QuantRDOQ::xRateDistOptQuant(TransformUnit &tu, const ComponentID &compID,
 #endif
 }
 
+#if JVET_N0280_RESIDUAL_CODING_TS
+void QuantRDOQ::xRateDistOptQuantTS( TransformUnit &tu, const ComponentID &compID, const CCoeffBuf &coeffs, TCoeff &absSum, const QpParam &qp, const Ctx &ctx )
+{
+  const FracBitsAccess& fracBits = ctx.getFracBitsAcess();
+
+  const SPS &sps            = *tu.cs->sps;
+  const CompArea &rect      = tu.blocks[compID];
+  const uint32_t width      = rect.width;
+  const uint32_t height     = rect.height;
+  const ChannelType chType  = toChannelType(compID);
+  const int channelBitDepth = sps.getBitDepth( chType );
+
+  const bool extendedPrecision     = sps.getSpsRangeExtension().getExtendedPrecisionProcessingFlag();
+  const int  maxLog2TrDynamicRange = sps.getMaxLog2TrDynamicRange(chType);
+
+  int transformShift = getTransformShift( channelBitDepth, rect.size(), maxLog2TrDynamicRange );
+
+  if( extendedPrecision )
+  {
+    transformShift = std::max<int>( 0, transformShift );
+  }
+
+        double   blockUncodedCost                   = 0;
+#if HEVC_USE_SCALING_LISTS
+  const uint32_t log2BlockHeight                    = g_aucLog2[height];
+#endif
+  const uint32_t maxNumCoeff                        = rect.area();
+
+  CHECK( compID >= MAX_NUM_TBLOCKS, "Invalid component ID" );
+
+#if HEVC_USE_SCALING_LISTS
+  int scalingListType = getScalingListType( tu.cu->predMode, compID );
+  CHECK( scalingListType >= SCALING_LIST_NUM, "Invalid scaling list" );
+#endif
+
+  const TCoeff *srcCoeff = coeffs.buf;
+        TCoeff *dstCoeff = tu.getCoeffs( compID ).buf;
+
+  double *costCoeff  = m_pdCostCoeff;
+  double *costSig    = m_pdCostSig;
+  double *costCoeff0 = m_pdCostCoeff0;
+
+  memset( m_pdCostCoeff,  0, sizeof( double ) *  maxNumCoeff );
+  memset( m_pdCostSig,    0, sizeof( double ) *  maxNumCoeff );
+
+  const int qBits = QUANT_SHIFT + qp.per + transformShift;                   // Right shift of non-RDOQ quantizer;  level = (coeff*uiQ + offset)>>q_bits
+
+#if HM_QTBT_AS_IN_JEM_QUANT
+  const int    quantisationCoefficient = ( TU::needsSqrt2Scale( tu, compID ) ? ( g_quantScales[qp.rem] * 181 ) >> 7 : g_quantScales[qp.rem] );
+  const double errorScale              = xGetErrScaleCoeff( TU::needsSqrt2Scale( tu, compID ), width, height, qp.rem, maxLog2TrDynamicRange, channelBitDepth );
+#else
+  const double blkErrScale             = ( TU::needsQP3Offset( tu, compID ) ? 2.0 : 1.0 );
+  const int    quantisationCoefficient = g_quantScales[qp.rem];
+  const double errorScale              = blkErrScale * xGetErrScaleCoeff( width, height, qp.rem, maxLog2TrDynamicRange, channelBitDepth );
+#endif
+
+  const TCoeff entropyCodingMaximum = ( 1 << maxLog2TrDynamicRange ) - 1;
+
+#if HEVC_USE_SIGN_HIDING
+  CoeffCodingContext cctx( tu, compID, tu.cs->slice->getSignDataHidingEnabledFlag() );
+#else
+  CoeffCodingContext cctx( tu, compID );
+#endif
+  const int sbSizeM1    = ( 1 << cctx.log2CGSize() ) - 1;
+  double    baseCost    = 0;
+  uint32_t  goRiceParam = 0;
+
+  double *costSigSubBlock = m_pdCostCoeffGroupSig;
+  memset( costSigSubBlock, 0, ( maxNumCoeff >> cctx.log2CGSize() ) * sizeof( double ) );
+
+  const int sbNum = width * height >> cctx.log2CGSize();
+  int scanPos;
+  coeffGroupRDStats rdStats;
+
+  bool anySigCG = false;
+
+  for( int sbId = 0; sbId < sbNum; sbId++ )
+  {
+    cctx.initSubblock( sbId );
+
+    memset( &rdStats, 0, sizeof (coeffGroupRDStats));
+
+    for( int scanPosInSB = 0; scanPosInSB <= sbSizeM1; scanPosInSB++ )
+    {
+      scanPos = cctx.minSubPos() + scanPosInSB;
+      //===== quantization =====
+      uint32_t blkPos = cctx.blockPos( scanPos );
+
+      // set coeff
+      const int64_t          tmpLevel    = int64_t( abs( srcCoeff[blkPos] ) ) * quantisationCoefficient;
+      const Intermediate_Int levelDouble = (Intermediate_Int)std::min<int64_t>( tmpLevel, std::numeric_limits<Intermediate_Int>::max() - ( Intermediate_Int( 1 ) << ( qBits - 1 ) ) );
+            uint32_t         maxAbsLevel = std::min<uint32_t>( uint32_t( entropyCodingMaximum ), uint32_t( ( levelDouble + ( Intermediate_Int( 1 ) << ( qBits - 1 ) ) ) >> qBits ) );
+
+      const double err       = double( levelDouble );
+      costCoeff0[ scanPos ]  = err * err * errorScale;
+      blockUncodedCost      += costCoeff0[ scanPos ];
+      dstCoeff[ blkPos ]     = maxAbsLevel;
+
+      //===== coefficient level estimation =====
+            unsigned    ctxIdSig = cctx.sigCtxIdAbsTS( scanPos, dstCoeff );
+            uint32_t    cLevel;
+      const BinFracBits fracBitsPar = fracBits.getFracBitsArray( cctx.parityCtxIdAbsTS() );
+
+      goRiceParam = cctx.templateAbsSumTS( scanPos, dstCoeff );
+      const BinFracBits fracBitsSign = fracBits.getFracBitsArray( Ctx::TsResidualSign( toChannelType(compID) ) );
+      const uint8_t     sign         = srcCoeff[ blkPos ] < 0 ? 1 : 0;
+
+      DTRACE_COND( ( maxAbsLevel != 0 ), g_trace_ctx, D_RDOQ_MORE, " uiCtxSig=%d", ctxIdSig );
+
+      const BinFracBits fracBitsSig = fracBits.getFracBitsArray( ctxIdSig );
+      cLevel = xGetCodedLevelTS( costCoeff[ scanPos ], costCoeff0[ scanPos ], costSig[ scanPos ],
+                                 levelDouble, maxAbsLevel, &fracBitsSig, fracBitsPar, cctx, fracBits, fracBitsSign, sign, goRiceParam, qBits, errorScale, 0, extendedPrecision, maxLog2TrDynamicRange );
+      dstCoeff[ blkPos ]  = cLevel;
+      baseCost           += costCoeff[ scanPos ];
+      rdStats.d64SigCost += costSig[ scanPos ];
+
+      if( scanPosInSB == 0 )
+      {
+        rdStats.d64SigCost_0 = costSig[ scanPos ];
+      }
+      if( dstCoeff[ blkPos ] )
+      {
+        cctx.setSigGroup();
+        rdStats.d64CodedLevelandDist += costCoeff [ scanPos ] - costSig[ scanPos ];
+        rdStats.d64UncodedDist       += costCoeff0[ scanPos ];
+        if( scanPosInSB != 0 )
+        {
+          rdStats.iNNZbeforePos0++;
+        }
+      }
+    } //end for (iScanPosinCG)
+
+    if( !cctx.isSigGroup() )
+    {
+      const BinFracBits fracBitsSigGroup = fracBits.getFracBitsArray( cctx.sigGroupCtxId( true ) );
+      baseCost += xGetRateSigCoeffGroup( fracBitsSigGroup, 0 ) - rdStats.d64SigCost;
+      costSigSubBlock[cctx.subSetId()] = xGetRateSigCoeffGroup( fracBitsSigGroup, 0 );
+    }
+    else if( sbId != sbSizeM1 || anySigCG )
+    {
+      if( rdStats.iNNZbeforePos0 == 0 )
+      {
+        baseCost -= rdStats.d64SigCost_0;
+        rdStats.d64SigCost -= rdStats.d64SigCost_0;
+      }
+      // rd-cost if SigCoeffGroupFlag = 0, initialization
+      double costZeroSB = baseCost;
+
+      const BinFracBits fracBitsSigGroup = fracBits.getFracBitsArray( cctx.sigGroupCtxId( true ) );
+
+      baseCost   += xGetRateSigCoeffGroup( fracBitsSigGroup, 1 );
+      costZeroSB += xGetRateSigCoeffGroup( fracBitsSigGroup, 0 );
+      costSigSubBlock[ cctx.subSetId() ] = xGetRateSigCoeffGroup( fracBitsSigGroup, 1 );
+
+      costZeroSB += rdStats.d64UncodedDist;         // distortion for resetting non-zero levels to zero levels
+      costZeroSB -= rdStats.d64CodedLevelandDist;   // distortion and level cost for keeping all non-zero levels
+      costZeroSB -= rdStats.d64SigCost;             // sig cost for all coeffs, including zero levels and non-zerl levels
+
+      if( costZeroSB < baseCost )
+      {
+        cctx.resetSigGroup();
+        baseCost = costZeroSB;
+        costSigSubBlock[ cctx.subSetId() ] = xGetRateSigCoeffGroup( fracBitsSigGroup, 0 );
+
+        for( int scanPosInSB = 0; scanPosInSB < sbSizeM1; scanPosInSB++ )
+        {
+          scanPos = cctx.minSubPos() + scanPosInSB;
+          uint32_t blkPos = cctx.blockPos( scanPos );
+
+          if( dstCoeff[ blkPos ] )
+          {
+            dstCoeff[ blkPos ] = 0;
+            costCoeff[ scanPos ] = costCoeff0[ scanPos ];
+            costSig[ scanPos] = 0;
+          }
+        }
+      }
+      else
+      {
+        anySigCG = true;
+      }
+    }
+  }
+
+  //===== estimate last position =====
+  for( int scanPos = 0; scanPos < maxNumCoeff; scanPos++ )
+  {
+    int blkPos = cctx.blockPos( scanPos );
+    TCoeff level = dstCoeff[ blkPos ];
+    absSum += level;
+    dstCoeff[ blkPos ] = ( level != 0 && srcCoeff[ blkPos ] < 0 ) ? -level : level;
+  }
+}
+
+inline uint32_t QuantRDOQ::xGetCodedLevelTS(       double&             codedCost,
+                                                   double&             codedCost0,
+                                                   double&             codedCostSig,
+                                                   Intermediate_Int    levelDouble,
+                                                   uint32_t            maxAbsLevel,
+                                             const BinFracBits*        fracBitsSig,
+                                             const BinFracBits&        fracBitsPar,
+                                             const CoeffCodingContext& cctx,
+                                             const FracBitsAccess&     fracBitsAccess,
+                                             const BinFracBits&        fracBitsSign,
+                                             const uint8_t             sign,
+                                                   uint16_t            ricePar,
+                                                   int                 qBits,
+                                                   double              errorScale,
+                                                   bool                isLast,
+                                                   bool                useLimitedPrefixLength,
+                                                   const int           maxLog2TrDynamicRange
+                                           ) const
+{
+  double   currCostSig  = 0;
+  uint32_t bestAbsLevel = 0;
+
+  if( !isLast && maxAbsLevel < 3 )
+  {
+    codedCostSig = xGetRateSigCoef( *fracBitsSig, 0 );
+    codedCost    = codedCost0 + codedCostSig;
+    if( maxAbsLevel == 0 )
+    {
+      return bestAbsLevel;
+    }
+  }
+  else
+  {
+    codedCost = MAX_DOUBLE;
+  }
+
+  if( !isLast )
+  {
+    currCostSig = xGetRateSigCoef( *fracBitsSig, 1 );
+  }
+
+  uint32_t minAbsLevel = ( maxAbsLevel > 1 ? maxAbsLevel - 1 : 1 );
+  for( int absLevel = maxAbsLevel; absLevel >= minAbsLevel ; absLevel-- )
+  {
+    double err       = double( levelDouble  - ( Intermediate_Int( absLevel ) << qBits ) );
+    double currCost  = err * err * errorScale + xGetICost( xGetICRateTS( absLevel, fracBitsPar, cctx, fracBitsAccess, fracBitsSign, sign, ricePar, useLimitedPrefixLength, maxLog2TrDynamicRange ) );
+           currCost += currCostSig;
+
+    if( currCost < codedCost )
+    {
+      bestAbsLevel = absLevel;
+      codedCost    = currCost;
+      codedCostSig = currCostSig;
+    }
+  }
+
+  return bestAbsLevel;
+}
+
+inline int QuantRDOQ::xGetICRateTS( const uint32_t            absLevel,
+                                    const BinFracBits&        fracBitsPar,
+                                    const CoeffCodingContext& cctx,
+                                    const FracBitsAccess&     fracBitsAccess,
+                                    const BinFracBits&        fracBitsSign,
+                                    const uint8_t             sign,
+                                    const uint16_t            ricePar,
+                                    const bool                useLimitedPrefixLength,
+                                    const int                 maxLog2TrDynamicRange  ) const
+{
+  int rate = fracBitsSign.intBits[sign];
+
+  const uint16_t     ctxGt1      = cctx.greaterXCtxIdAbsTS( 0 );
+  const BinFracBits &fracBitsGt1 = fracBitsAccess.getFracBitsArray( ctxGt1 );
+
+  if( absLevel > 1 )
+  {
+    rate += fracBitsGt1.intBits[1];
+    rate += fracBitsPar.intBits[( absLevel - 2 ) & 1];
+
+          int cutoffVal = 2;
+    const int numGtBins = 4;
+    for( int i = 0; i < numGtBins; i++ )
+    {
+      if( absLevel >= cutoffVal )
+      {
+        const uint16_t ctxGtX = cctx.greaterXCtxIdAbsTS( cutoffVal>>1 );
+        const BinFracBits &fracBitsGtX = fracBitsAccess.getFracBitsArray( ctxGtX );
+        unsigned gtX = ( absLevel >= ( cutoffVal + 2 ) );
+        rate += fracBitsGtX.intBits[gtX];
+      }
+      cutoffVal += 2;
+    }
+
+    if( absLevel >= cutoffVal )
+    {
+      uint32_t symbol = ( absLevel - cutoffVal ) >> 1;
+      uint32_t length;
+      const int threshold = COEF_REMAIN_BIN_REDUCTION;
+      if( symbol < ( threshold << ricePar ) )
+      {
+        length = symbol >> ricePar;
+        rate  += ( length + 1 + ricePar ) << SCALE_BITS;
+      }
+      else if( useLimitedPrefixLength )
+      {
+        const uint32_t maximumPrefixLength = ( 32 - ( COEF_REMAIN_BIN_REDUCTION + maxLog2TrDynamicRange ) );
+
+        uint32_t prefixLength = 0;
+        uint32_t suffix = ( symbol >> ricePar ) - COEF_REMAIN_BIN_REDUCTION;
+
+        while( ( prefixLength < maximumPrefixLength ) && ( suffix > ( ( 2 << prefixLength ) - 2 ) ) )
+        {
+          prefixLength++;
+        }
+
+        const uint32_t suffixLength = ( prefixLength == maximumPrefixLength ) ? ( maxLog2TrDynamicRange - ricePar ) : ( prefixLength + 1/*separator*/ );
+
+        rate += ( COEF_REMAIN_BIN_REDUCTION + prefixLength + suffixLength + ricePar ) << SCALE_BITS;
+      }
+      else
+      {
+        length = ricePar;
+        symbol = symbol - ( threshold << ricePar );
+        while( symbol >= ( 1 << length ) )
+        {
+          symbol -= ( 1 << ( length++ ) );
+        }
+        rate += ( threshold + length + 1 - ricePar + length ) << SCALE_BITS;
+      }
+    }
+  }
+  else if( absLevel == 1 )
+  {
+    rate += fracBitsGt1.intBits[0];
+  }
+  else
+  {
+    rate = 0;
+  }
+  return rate;
+}
+#endif
+
 //! \}
diff --git a/source/Lib/CommonLib/QuantRDOQ.h b/source/Lib/CommonLib/QuantRDOQ.h
index a51bdca698c3c03bada83ab39ef21562d4a4609c..7077e6ef426a37f3001aad689e4a2ca1fa615105 100644
--- a/source/Lib/CommonLib/QuantRDOQ.h
+++ b/source/Lib/CommonLib/QuantRDOQ.h
@@ -120,6 +120,37 @@ private:
   inline double xGetICost            ( double dRate                                                      ) const;
   inline double xGetIEPRate          (                                                                   ) const;
 
+#if JVET_N0280_RESIDUAL_CODING_TS
+  void xRateDistOptQuantTS( TransformUnit &tu, const ComponentID &compID, const CCoeffBuf &coeffs, TCoeff &absSum, const QpParam &qp, const Ctx &ctx );
+
+  inline uint32_t xGetCodedLevelTS(       double&             codedCost,
+                                          double&             codedCost0,
+                                          double&             codedCostSig,
+                                          Intermediate_Int    levelDouble,
+                                          uint32_t            maxAbsLevel,
+                                    const BinFracBits*        fracBitsSig,
+                                    const BinFracBits&        fracBitsPar,
+                                    const CoeffCodingContext& cctx,
+                                    const FracBitsAccess&     fracBitsAccess,
+                                    const BinFracBits&        fracBitsSign,
+                                    const uint8_t             sign,
+                                          uint16_t            ricePar,
+                                          int                 qBits,
+                                          double              errorScale,
+                                          bool                isLast,
+                                          bool                useLimitedPrefixLength,
+                                    const int                 maxLog2TrDynamicRange ) const;
+
+  inline int xGetICRateTS   ( const uint32_t            absLevel,
+                              const BinFracBits&        fracBitsPar,
+                              const CoeffCodingContext& cctx,
+                              const FracBitsAccess&     fracBitsAccess,
+                              const BinFracBits&        fracBitsSign,
+                              const uint8_t             sign,
+                              const uint16_t            ricePar,
+                              const bool                useLimitedPrefixLength,
+                              const int                 maxLog2TrDynamicRange  ) const;
+#endif
 private:
 #if HEVC_USE_SCALING_LISTS
   bool    m_isErrScaleListOwner;
diff --git a/source/Lib/CommonLib/TypeDef.h b/source/Lib/CommonLib/TypeDef.h
index 67b78ab04afc85b4022751f9a8c292b9b54b429d..c93e20f3dcd158805d14135c887c0c91e7fe8413 100644
--- a/source/Lib/CommonLib/TypeDef.h
+++ b/source/Lib/CommonLib/TypeDef.h
@@ -50,6 +50,8 @@
 #include <assert.h>
 #include <cassert>
 
+#define JVET_N0280_RESIDUAL_CODING_TS                     1
+
 #define JVET_N0103_CGSIZE_HARMONIZATION                   1 // Chroma CG sizes aligned to luma CG sizes
 
 #define JVET_N0146_DMVR_BDOF_CONDITION                    1 // JVET-N146/N0162/N0442/N0153/N0262/N0440/N0086 applicable condition of DMVR and BDOF
@@ -61,7 +63,9 @@
 #define JVET_N0843_BVP_SIMPLIFICATION                     1
 
 #define JVET_N0448_N0380                                  1 // When MaxNumMergeCand is 1, MMVD_BASE_MV_NUM is inferred to be 1.
+
 #define JVET_N0266_SMALL_BLOCKS                           1 // remove 4x4 uni-pred, 4x8/8x4 bi-pred from regular inter modes
+
 #define JVET_N0054_JOINT_CHROMA                           1 // Joint chroma residual coding mode
 
 #define JVET_N0317_ADD_ZERO_BV                            1
diff --git a/source/Lib/DecoderLib/CABACReader.cpp b/source/Lib/DecoderLib/CABACReader.cpp
index e4e9b6c9f44c8283add341a466c1f4a1080afeda..b06830023aa24aedb7733c3ab8366bf55a0f581e 100644
--- a/source/Lib/DecoderLib/CABACReader.cpp
+++ b/source/Lib/DecoderLib/CABACReader.cpp
@@ -2460,6 +2460,13 @@ void CABACReader::residual_coding( TransformUnit& tu, ComponentID compID )
   mts_coding         ( tu, compID );
   explicit_rdpcm_mode( tu, compID );
 
+#if JVET_N0280_RESIDUAL_CODING_TS
+  if( isLuma( compID ) && tu.mtsIdx == 1 )
+  {
+    residual_codingTS( tu, compID );
+    return;
+  }
+#endif
 
 #if HEVC_USE_SIGN_HIDING
   // determine sign hiding
@@ -2871,6 +2878,197 @@ void CABACReader::residual_coding_subblock( CoeffCodingContext& cctx, TCoeff* co
 #endif
 }
 
+#if JVET_N0280_RESIDUAL_CODING_TS
+void CABACReader::residual_codingTS( TransformUnit& tu, ComponentID compID )
+{
+  DTRACE( g_trace_ctx, D_SYNTAX, "residual_codingTS() etype=%d pos=(%d,%d) size=%dx%d\n", tu.blocks[compID].compID, tu.blocks[compID].x, tu.blocks[compID].y, tu.blocks[compID].width, tu.blocks[compID].height );
+
+  // init coeff coding context
+  CoeffCodingContext  cctx    ( tu, compID, false );
+  TCoeff*             coeff   = tu.getCoeffs( compID ).buf;
+
+  cctx.setNumCtxBins( 2 * tu.lwidth()*tu.lheight() );
+
+  for( int subSetId = 0; subSetId <= ( cctx.maxNumCoeff() - 1 ) >> cctx.log2CGSize(); subSetId++ )
+  {
+    cctx.initSubblock         ( subSetId );
+    residual_coding_subblockTS( cctx, coeff );
+  }
+}
+
+void CABACReader::residual_coding_subblockTS( CoeffCodingContext& cctx, TCoeff* coeff )
+{
+  // NOTE: All coefficients of the subblock must be set to zero before calling this function
+#if RExt__DECODER_DEBUG_BIT_STATISTICS
+  CodingStatisticsClassType ctype_group ( STATS__CABAC_BITS__SIG_COEFF_GROUP_FLAG,  cctx.width(), cctx.height(), cctx.compID() );
+  CodingStatisticsClassType ctype_map   ( STATS__CABAC_BITS__SIG_COEFF_MAP_FLAG,    cctx.width(), cctx.height(), cctx.compID() );
+  CodingStatisticsClassType ctype_par   ( STATS__CABAC_BITS__PAR_FLAG,              cctx.width(), cctx.height(), cctx.compID() );
+  CodingStatisticsClassType ctype_gt1   ( STATS__CABAC_BITS__GT1_FLAG,              cctx.width(), cctx.height(), cctx.compID() );
+  CodingStatisticsClassType ctype_gt2   ( STATS__CABAC_BITS__GT2_FLAG,              cctx.width(), cctx.height(), cctx.compID() );
+  CodingStatisticsClassType ctype_escs  ( STATS__CABAC_BITS__ESCAPE_BITS,           cctx.width(), cctx.height(), cctx.compID() );
+#endif
+
+  //===== init =====
+  const int   minSubPos   = cctx.maxSubPos();
+  int         firstSigPos = cctx.minSubPos();
+  int         nextSigPos  = firstSigPos;
+  unsigned    signPattern = 0;
+
+  //===== decode significant_coeffgroup_flag =====
+  RExt__DECODER_DEBUG_BIT_STATISTICS_SET( ctype_group );
+  bool sigGroup = cctx.isLastSubSet() && cctx.noneSigGroup();
+  if( !sigGroup )
+  {
+    if( cctx.isContextCoded() )
+    {
+      sigGroup = m_BinDecoder.decodeBin( cctx.sigGroupCtxId( true ) );
+      DTRACE( g_trace_ctx, D_SYNTAX_RESI, "ts_sigGroup() bin=%d ctx=%d\n", sigGroup, cctx.sigGroupCtxId() );
+    }
+    else
+    {
+      sigGroup = m_BinDecoder.decodeBinEP( );
+      DTRACE( g_trace_ctx, D_SYNTAX_RESI, "ts_sigGroup() EPbin=%d\n", sigGroup );
+    }
+  }
+  if( sigGroup )
+  {
+    cctx.setSigGroup();
+  }
+  else
+  {
+    return;
+  }
+
+  //===== decode absolute values =====
+  const int inferSigPos   = minSubPos;
+  int       numNonZero    =  0;
+  int       sigBlkPos[ 1 << MLS_CG_SIZE ];
+
+  for( ; nextSigPos <= minSubPos; nextSigPos++ )
+  {
+    int      blkPos     = cctx.blockPos( nextSigPos );
+    unsigned sigFlag    = ( !numNonZero && nextSigPos == inferSigPos );
+    if( !sigFlag )
+    {
+      RExt__DECODER_DEBUG_BIT_STATISTICS_SET( ctype_map );
+      if( cctx.isContextCoded() )
+      {
+        const unsigned sigCtxId = cctx.sigCtxIdAbsTS( nextSigPos, coeff );
+        sigFlag = m_BinDecoder.decodeBin( sigCtxId );
+        DTRACE( g_trace_ctx, D_SYNTAX_RESI, "ts_sig_bin() bin=%d ctx=%d\n", sigFlag, sigCtxId );
+      }
+      else
+      {
+        sigFlag = m_BinDecoder.decodeBinEP( );
+        DTRACE( g_trace_ctx, D_SYNTAX_RESI, "ts_sig_bin() EPbin=%d\n", sigFlag );
+      }
+    }
+
+    if( sigFlag )
+    {
+      //===== decode sign's =====
+      RExt__DECODER_DEBUG_BIT_STATISTICS_CREATE_SET_SIZE2( STATS__CABAC_BITS__SIGN_BIT, Size( cctx.width(), cctx.height() ), cctx.compID() );
+      int sign;
+      if( cctx.isContextCoded() )
+      {
+        sign = m_BinDecoder.decodeBin( Ctx::TsResidualSign( toChannelType( cctx.compID() ) ) );
+      }
+      else
+      {
+        sign = m_BinDecoder.decodeBinEP( );
+      }
+
+      signPattern += ( sign << numNonZero );
+
+      sigBlkPos[numNonZero++] = blkPos;
+
+      RExt__DECODER_DEBUG_BIT_STATISTICS_SET( ctype_gt1 );
+      unsigned gt1Flag;
+      if( cctx.isContextCoded() )
+      {
+        gt1Flag = m_BinDecoder.decodeBin( cctx.greaterXCtxIdAbsTS(0) );
+        DTRACE( g_trace_ctx, D_SYNTAX_RESI, "ts_gt1_flag() bin=%d ctx=%d\n", gt1Flag, cctx.greaterXCtxIdAbsTS(0) );
+      }
+      else
+      {
+        gt1Flag = m_BinDecoder.decodeBinEP( );
+        DTRACE( g_trace_ctx, D_SYNTAX_RESI, "ts_gt1_flag() EPbin=%d\n", gt1Flag );
+      }
+
+      unsigned parFlag = 0;
+      if( gt1Flag )
+      {
+        RExt__DECODER_DEBUG_BIT_STATISTICS_SET( ctype_par );
+        if( cctx.isContextCoded() )
+        {
+          parFlag = m_BinDecoder.decodeBin( cctx.parityCtxIdAbsTS() );
+          DTRACE( g_trace_ctx, D_SYNTAX_RESI, "ts_par_flag() bin=%d ctx=%d\n", parFlag, cctx.parityCtxIdAbsTS() );
+        }
+        else
+        {
+          parFlag = m_BinDecoder.decodeBinEP( );
+          DTRACE( g_trace_ctx, D_SYNTAX_RESI, "ts_par_flag() EPbin=%d\n", parFlag );
+        }
+      }
+      coeff[ blkPos ] += 1 + parFlag + gt1Flag;
+    }
+  }
+
+  int cutoffVal = 2;
+  int numGtBins = 4;
+
+  //===== 2nd PASS: gt2 =====
+  for( int i = 0; i < numGtBins; i++ )
+  {
+    for( int scanPos = firstSigPos; scanPos <= minSubPos; scanPos++ )
+    {
+      TCoeff& tcoeff = coeff[cctx.blockPos( scanPos )];
+      if( tcoeff >= cutoffVal )
+      {
+        RExt__DECODER_DEBUG_BIT_STATISTICS_SET( ctype_gt2 );
+        unsigned gt2Flag;
+        if( cctx.isContextCoded() )
+        {
+          gt2Flag = m_BinDecoder.decodeBin( cctx.greaterXCtxIdAbsTS(cutoffVal>>1) );
+          tcoeff += ( gt2Flag << 1 );
+          DTRACE( g_trace_ctx, D_SYNTAX_RESI, "ts_gt%d_flag() bin=%d ctx=%d sp=%d coeff=%d\n", i, gt2Flag, cctx.greaterXCtxIdAbsTS(cutoffVal>>1), scanPos, tcoeff );
+        }
+        else
+        {
+          gt2Flag = m_BinDecoder.decodeBinEP( );
+          tcoeff += ( gt2Flag << 1 );
+          DTRACE( g_trace_ctx, D_SYNTAX_RESI, "ts_gt%d_flag() EPbin=%d sp=%d coeff=%d\n", i, gt2Flag, scanPos, tcoeff );
+        }
+      }
+    }
+    cutoffVal += 2;
+  }
+
+  //===== 3rd PASS: Go-rice codes =====
+  for( int scanPos = firstSigPos; scanPos <= minSubPos; scanPos++ )
+  {
+    TCoeff& tcoeff = coeff[ cctx.blockPos( scanPos ) ];
+    RExt__DECODER_DEBUG_BIT_STATISTICS_SET( ctype_escs );
+    if( tcoeff >= cutoffVal )
+    {
+      int       rice = cctx.templateAbsSumTS( scanPos, coeff );
+      int       rem  = m_BinDecoder.decodeRemAbsEP( rice, cctx.extPrec(), cctx.maxLog2TrDRange() );
+      DTRACE( g_trace_ctx, D_SYNTAX_RESI, "ts_rem_val() bin=%d ctx=%d sp=%d\n", rem, rice, scanPos );
+      tcoeff += ( rem << 1 );
+    }
+  }
+
+  //===== set final coefficents =====
+  for( unsigned k = 0; k < numNonZero; k++ )
+  {
+    int AbsCoeff          = coeff[ sigBlkPos[ k ] ];
+    coeff[ sigBlkPos[k] ] = ( signPattern & 1 ? -AbsCoeff : AbsCoeff );
+    signPattern         >>= 1;
+  }
+}
+#endif
+
+
 //================================================================================
 //  clause 7.3.8.12
 //--------------------------------------------------------------------------------
diff --git a/source/Lib/DecoderLib/CABACReader.h b/source/Lib/DecoderLib/CABACReader.h
index 2efdb3be744a3fd54c6c6d15b653a0beda931637..c8dd4878140edec5ea0e524119281a6cbf20d44b 100644
--- a/source/Lib/DecoderLib/CABACReader.h
+++ b/source/Lib/DecoderLib/CABACReader.h
@@ -130,6 +130,10 @@ public:
   void        explicit_rdpcm_mode       ( TransformUnit&                tu,     ComponentID     compID );
   int         last_sig_coeff            ( CoeffCodingContext&           cctx,   TransformUnit& tu, ComponentID   compID );
   void        residual_coding_subblock  ( CoeffCodingContext&           cctx,   TCoeff*         coeff, const int stateTransTable, int& state );
+#if JVET_N0280_RESIDUAL_CODING_TS
+  void        residual_codingTS         ( TransformUnit&                tu,     ComponentID     compID );
+  void        residual_coding_subblockTS( CoeffCodingContext&           cctx,   TCoeff*         coeff  );
+#endif
 #if JVET_N0054_JOINT_CHROMA
   void        joint_cb_cr               ( TransformUnit&                tu );
 #endif
diff --git a/source/Lib/EncoderLib/CABACWriter.cpp b/source/Lib/EncoderLib/CABACWriter.cpp
index f45f3189b12f8d1766e7a5f2d930e1fc6e6d860b..ce9c579f52f8d7d3e3c83c8fa61c924a1b32dbd1 100644
--- a/source/Lib/EncoderLib/CABACWriter.cpp
+++ b/source/Lib/EncoderLib/CABACWriter.cpp
@@ -2349,6 +2349,14 @@ void CABACWriter::residual_coding( const TransformUnit& tu, ComponentID compID )
   mts_coding         ( tu, compID );
   explicit_rdpcm_mode( tu, compID );
 
+#if JVET_N0280_RESIDUAL_CODING_TS
+  if( isLuma( compID ) && tu.mtsIdx==1 )
+  {
+    residual_codingTS( tu, compID );
+    return;
+  }
+#endif
+
 #if HEVC_USE_SIGN_HIDING
   // determine sign hiding
   bool signHiding  = ( cu.cs->slice->getSignDataHidingEnabledFlag() && !cu.transQuantBypass && tu.rdpcm[compID] == RDPCM_OFF );
@@ -2725,6 +2733,184 @@ void CABACWriter::residual_coding_subblock( CoeffCodingContext& cctx, const TCoe
 #endif
 }
 
+#if JVET_N0280_RESIDUAL_CODING_TS
+void CABACWriter::residual_codingTS( const TransformUnit& tu, ComponentID compID )
+{
+  DTRACE( g_trace_ctx, D_SYNTAX, "residual_codingTS() etype=%d pos=(%d,%d) size=%dx%d\n", tu.blocks[compID].compID, tu.blocks[compID].x, tu.blocks[compID].y, tu.blocks[compID].width, tu.blocks[compID].height );
+
+  // init coeff coding context
+  CoeffCodingContext  cctx    ( tu, compID, false );
+  const TCoeff*       coeff   = tu.getCoeffs( compID ).buf;
+
+  cctx.setNumCtxBins( 2 * tu.lwidth()*tu.lheight() );
+
+  // determine and set last coeff position and sig group flags
+  std::bitset<MLS_GRP_NUM> sigGroupFlags;
+  for( int scanPos = 0; scanPos < cctx.maxNumCoeff(); scanPos++)
+  {
+    unsigned blkPos = cctx.blockPos( scanPos );
+    if( coeff[blkPos] )
+    {
+      sigGroupFlags.set( scanPos >> cctx.log2CGSize() );
+    }
+  }
+
+  // code subblocks
+  for( int subSetId = 0; subSetId <= ( cctx.maxNumCoeff() - 1 ) >> cctx.log2CGSize(); subSetId++ )
+  {
+    cctx.initSubblock         ( subSetId, sigGroupFlags[subSetId] );
+    residual_coding_subblockTS( cctx, coeff );
+  }
+}
+
+void CABACWriter::residual_coding_subblockTS( CoeffCodingContext& cctx, const TCoeff* coeff )
+{
+  //===== init =====
+  const int   minSubPos   = cctx.maxSubPos();
+  int         firstSigPos = cctx.minSubPos();
+  int         nextSigPos  = firstSigPos;
+
+  //===== encode significant_coeffgroup_flag =====
+  if( !cctx.isLastSubSet() || !cctx.only1stSigGroup() )
+  {
+    if( cctx.isSigGroup() )
+    {
+      if( cctx.isContextCoded() )
+      {
+        m_BinEncoder.encodeBin( 1, cctx.sigGroupCtxId( true ) );
+        DTRACE( g_trace_ctx, D_SYNTAX_RESI, "ts_sigGroup() bin=%d ctx=%d\n", 1, cctx.sigGroupCtxId() );
+      }
+      else
+      {
+        m_BinEncoder.encodeBinEP( 1 );
+        DTRACE( g_trace_ctx, D_SYNTAX_RESI, "ts_sigGroup() EPbin=%d\n", 1 );
+      }
+    }
+    else
+    {
+      if( cctx.isContextCoded() )
+      {
+        m_BinEncoder.encodeBin( 0, cctx.sigGroupCtxId( true ) );
+        DTRACE( g_trace_ctx, D_SYNTAX_RESI, "ts_sigGroup() bin=%d ctx=%d\n", 0, cctx.sigGroupCtxId() );
+      }
+      else
+      {
+        m_BinEncoder.encodeBinEP( 0 );
+        DTRACE( g_trace_ctx, D_SYNTAX_RESI, "ts_sigGroup() EPbin=%d\n", 0 );
+      }
+      return;
+    }
+  }
+
+  //===== encode absolute values =====
+  const int inferSigPos   = minSubPos;
+  int       remAbsLevel   = -1;
+  int       numNonZero    =  0;
+
+  for( ; nextSigPos <= minSubPos; nextSigPos++ )
+  {
+    TCoeff    Coeff      = coeff[ cctx.blockPos( nextSigPos ) ];
+    unsigned  sigFlag    = ( Coeff != 0 );
+    if( numNonZero || nextSigPos != inferSigPos )
+    {
+      if( cctx.isContextCoded() )
+      {
+        const unsigned sigCtxId = cctx.sigCtxIdAbsTS( nextSigPos, coeff );
+        m_BinEncoder.encodeBin( sigFlag, sigCtxId );
+        DTRACE( g_trace_ctx, D_SYNTAX_RESI, "ts_sig_bin() bin=%d ctx=%d\n", sigFlag, sigCtxId );
+      }
+      else
+      {
+        m_BinEncoder.encodeBinEP( sigFlag );
+        DTRACE( g_trace_ctx, D_SYNTAX_RESI, "ts_sig_bin() EPbin=%d\n", sigFlag );
+      }
+    }
+
+    if( sigFlag )
+    {
+      //===== encode sign's =====
+      int sign = Coeff < 0;
+      if( cctx.isContextCoded() )
+      {
+        m_BinEncoder.encodeBin( sign, Ctx::TsResidualSign( toChannelType( cctx.compID() ) ) );
+      }
+      else
+      {
+        m_BinEncoder.encodeBinEP( sign );
+      }
+      numNonZero++;
+      remAbsLevel = abs( Coeff ) - 1;
+
+      unsigned gt1 = !!remAbsLevel;
+      if( cctx.isContextCoded() )
+      {
+        m_BinEncoder.encodeBin( gt1, cctx.greaterXCtxIdAbsTS(0) );
+        DTRACE( g_trace_ctx, D_SYNTAX_RESI, "ts_gt1_flag() bin=%d ctx=%d\n", gt1, cctx.greaterXCtxIdAbsTS(0) );
+      }
+      else
+      {
+        m_BinEncoder.encodeBinEP( gt1 );
+        DTRACE( g_trace_ctx, D_SYNTAX_RESI, "ts_gt1_flag() EPbin=%d\n", gt1 );
+      }
+
+      if( gt1 )
+      {
+        remAbsLevel  -= 1;
+        if( cctx.isContextCoded() )
+        {
+          m_BinEncoder.encodeBin( remAbsLevel&1, cctx.parityCtxIdAbsTS() );
+          DTRACE( g_trace_ctx, D_SYNTAX_RESI, "ts_par_flag() bin=%d ctx=%d\n", remAbsLevel&1, cctx.parityCtxIdAbsTS() );
+        }
+        else
+        {
+          m_BinEncoder.encodeBinEP( remAbsLevel&1 );
+          DTRACE( g_trace_ctx, D_SYNTAX_RESI, "ts_par_flag() EPbin=%d\n", remAbsLevel&1 );
+        }
+      }
+    }
+  }
+
+  int cutoffVal = 2;
+  int numGtBins = 4;
+
+  for( int i = 0; i < numGtBins; i++ )
+  {
+    for( int scanPos = firstSigPos; scanPos <= minSubPos; scanPos++ )
+    {
+      unsigned absLevel = abs( coeff[cctx.blockPos( scanPos )] );
+      if( absLevel >= cutoffVal )
+      {
+        unsigned gt2 = ( absLevel >= ( cutoffVal + 2 ) );
+        if( cctx.isContextCoded() )
+        {
+          m_BinEncoder.encodeBin( gt2, cctx.greaterXCtxIdAbsTS( cutoffVal>>1 ) );
+          DTRACE( g_trace_ctx, D_SYNTAX_RESI, "ts_gt%d_flag() bin=%d ctx=%d sp=%d coeff=%d\n", i, gt2, cctx.greaterXCtxIdAbsTS( cutoffVal>>1 ), scanPos, min<int>( absLevel, cutoffVal+2 ) );
+        }
+        else
+        {
+          m_BinEncoder.encodeBinEP( gt2 );
+          DTRACE( g_trace_ctx, D_SYNTAX_RESI, "ts_gt%d_flag() EPbin=%d sp=%d coeff=%d\n", i, gt2, scanPos, min<int>( absLevel, cutoffVal+2 ) );
+        }
+      }
+    }
+    cutoffVal += 2;
+  }
+
+  //===== coeff bypass ====
+  for( int scanPos = firstSigPos; scanPos <= minSubPos; scanPos++ )
+  {
+    TCoeff    Coeff     = coeff[ cctx.blockPos( scanPos ) ];
+    unsigned  absLevel  = abs( Coeff );
+    if( absLevel >= cutoffVal )
+    {
+      int       rice = cctx.templateAbsSumTS( scanPos, coeff );
+      unsigned  rem  = ( absLevel - cutoffVal ) >> 1;
+      m_BinEncoder.encodeRemAbsEP( rem, rice, cctx.extPrec(), cctx.maxLog2TrDRange() );
+      DTRACE( g_trace_ctx, D_SYNTAX_RESI, "ts_rem_val() bin=%d ctx=%d sp=%d\n", rem, rice, scanPos );
+    }
+  }
+}
+#endif
 
 
 
diff --git a/source/Lib/EncoderLib/CABACWriter.h b/source/Lib/EncoderLib/CABACWriter.h
index 49c2f0a9cad7ce3f21279bde95189af281a7e409..59b513a48cfb17113a4a36396419fbf8c26f7731 100644
--- a/source/Lib/EncoderLib/CABACWriter.h
+++ b/source/Lib/EncoderLib/CABACWriter.h
@@ -144,6 +144,10 @@ public:
   void        explicit_rdpcm_mode       ( const TransformUnit&          tu,       ComponentID       compID );
   void        last_sig_coeff            ( CoeffCodingContext&           cctx,     const TransformUnit& tu, ComponentID       compID );
   void        residual_coding_subblock  ( CoeffCodingContext&           cctx,     const TCoeff*     coeff, const int stateTransTable, int& state );
+#if JVET_N0280_RESIDUAL_CODING_TS
+  void        residual_codingTS         ( const TransformUnit&          tu,       ComponentID       compID );
+  void        residual_coding_subblockTS( CoeffCodingContext&           cctx,     const TCoeff*     coeff  );
+#endif
 #if JVET_N0054_JOINT_CHROMA
   void        joint_cb_cr               ( const TransformUnit&          tu );
 #endif