Skip to content
Snippets Groups Projects
CABACWriter.cpp 112 KiB
Newer Older
  • Learn to ignore specific revisions
  •           if ( pu.mergeIdx == idx )
              {
                break;
              }
            }
          }
        }
        DTRACE( g_trace_ctx, D_SYNTAX, "aff_merge_idx() aff_merge_idx=%d\n", pu.mergeIdx );
      }
      else
      {
    
          const uint8_t splitDir = pu.geoSplitDir;
          const uint8_t candIdx0 = pu.geoMergeIdx[0];
          uint8_t       candIdx1 = pu.geoMergeIdx[1];
    
          DTRACE( g_trace_ctx, D_SYNTAX, "merge_idx() geo_split_dir=%d\n", splitDir );
          DTRACE( g_trace_ctx, D_SYNTAX, "merge_idx() geo_idx0=%d\n", candIdx0 );
          DTRACE( g_trace_ctx, D_SYNTAX, "merge_idx() geo_idx1=%d\n", candIdx1 );
          xWriteTruncBinCode(splitDir, GEO_NUM_PARTITION_MODE);
          candIdx1 -= candIdx1 < candIdx0 ? 0 : 1;
    
    Ling Li's avatar
    Ling Li committed
          const int maxNumGeoCand = pu.cs->sps->getMaxNumGeoCand();
    
          CHECK(maxNumGeoCand < 2, "Incorrect max number of geo candidates");
          CHECK(candIdx0 >= maxNumGeoCand, "Incorrect candIdx0");
          CHECK(candIdx1 >= maxNumGeoCand, "Incorrect candIdx1");
    
          const int numCandminus2 = maxNumGeoCand - 2;
    
    Frank Bossen's avatar
    Frank Bossen committed
          m_binEncoder.encodeBin(candIdx0 == 0 ? 0 : 1, Ctx::MergeIdx());
    
          if( candIdx0 > 0 )
          {
            unary_max_eqprob(candIdx0 - 1, numCandminus2);
          }
          if (numCandminus2 > 0)
          {
    
    Frank Bossen's avatar
    Frank Bossen committed
            m_binEncoder.encodeBin(candIdx1 == 0 ? 0 : 1, Ctx::MergeIdx());
    
            if (candIdx1 > 0)
            {
              unary_max_eqprob(candIdx1 - 1, numCandminus2 - 1);
            }
          }
          return;
        }
    
    Yan Zhang's avatar
    Yan Zhang committed
        int numCandminus1;
    
    Ling Li's avatar
    Ling Li committed
          numCandminus1 = int(pu.cs->sps->getMaxNumIBCMergeCand()) - 1;
    
    Ling Li's avatar
    Ling Li committed
        else
    
          numCandminus1 = int(pu.cs->sps->getMaxNumMergeCand()) - 1;
    
        CHECK(pu.mergeIdx > numCandminus1, "mergeIdx out of range");
    
        if (numCandminus1 > 0)
    
          if (pu.mergeIdx == 0)
    
    Frank Bossen's avatar
    Frank Bossen committed
            m_binEncoder.encodeBin(0, Ctx::MergeIdx());
    
            DTRACE(g_trace_ctx, D_SYNTAX, "merge_idx() merge_idx=%d\n", pu.mergeIdx);
            return;
          }
          else
          {
    
    Frank Bossen's avatar
    Frank Bossen committed
            m_binEncoder.encodeBin(1, Ctx::MergeIdx());
    
            for (unsigned idx = 1; idx < numCandminus1; idx++)
    
    Frank Bossen's avatar
    Frank Bossen committed
              m_binEncoder.encodeBinEP(pu.mergeIdx == idx ? 0 : 1);
    
              if (pu.mergeIdx == idx)
              {
                break;
              }
    
        DTRACE(g_trace_ctx, D_SYNTAX, "merge_idx() merge_idx=%d\n", pu.mergeIdx);
    
    void CABACWriter::mmvd_merge_idx(const PredictionUnit& pu)
    {
    
    Frank Bossen's avatar
    Frank Bossen committed
      const int mvdBaseIdx  = pu.mmvdMergeIdx.pos.baseIdx;
      const int mvdStep     = pu.mmvdMergeIdx.pos.step;
      const int mvdPosition = pu.mmvdMergeIdx.pos.position;
    
      CHECK(mvdBaseIdx >= std::min<int>(pu.cs->sps->getMaxNumMergeCand(), MmvdIdx::BASE_MV_NUM), "MMVD base index out of range");
    
    
    Ling Li's avatar
    Ling Li committed
      if (pu.cs->sps->getMaxNumMergeCand() > 1)
    
        static_assert(MmvdIdx::BASE_MV_NUM == 2, "");
    
    Frank Bossen's avatar
    Frank Bossen committed
        m_binEncoder.encodeBin(mvdBaseIdx, Ctx::MmvdMergeIdx());
    
    Frank Bossen's avatar
    Frank Bossen committed
      DTRACE(g_trace_ctx, D_SYNTAX, "base_mvp_idx() base_mvp_idx=%d\n", mvdBaseIdx);
    
      int numStepCandMinus1 = MmvdIdx::REFINE_STEP - 1;
      if (numStepCandMinus1 > 0)
    
    Frank Bossen's avatar
    Frank Bossen committed
        if (mvdStep == 0)
    
    Frank Bossen's avatar
    Frank Bossen committed
          m_binEncoder.encodeBin(0, Ctx::MmvdStepMvpIdx());
    
    Frank Bossen's avatar
    Frank Bossen committed
          m_binEncoder.encodeBin(1, Ctx::MmvdStepMvpIdx());
    
          for (unsigned idx = 1; idx < numStepCandMinus1; idx++)
    
    Frank Bossen's avatar
    Frank Bossen committed
            m_binEncoder.encodeBinEP(mvdStep == idx ? 0 : 1);
            if (mvdStep == idx)
    
    Frank Bossen's avatar
    Frank Bossen committed
      DTRACE(g_trace_ctx, D_SYNTAX, "MmvdStepMvpIdx() MmvdStepMvpIdx=%d\n", mvdStep);
    
    Frank Bossen's avatar
    Frank Bossen committed
      m_binEncoder.encodeBinsEP(mvdPosition, 2);
    
    Frank Bossen's avatar
    Frank Bossen committed
      DTRACE(g_trace_ctx, D_SYNTAX, "pos() pos=%d\n", mvdPosition);
    
      DTRACE(g_trace_ctx, D_SYNTAX, "mmvd_merge_idx() mmvd_merge_idx=%d\n", pu.mmvdMergeIdx.val);
    
    void CABACWriter::inter_pred_idc( const PredictionUnit& pu )
    {
      if( !pu.cs->slice->isInterB() )
      {
        return;
      }
    
    Karsten Suehring's avatar
    Karsten Suehring committed
      if( !(PU::isBipredRestriction(pu)) )
    
      {
        unsigned ctxId = DeriveCtx::CtxInterDir(pu);
        if( pu.interDir == 3 )
        {
    
    Frank Bossen's avatar
    Frank Bossen committed
          m_binEncoder.encodeBin(1, Ctx::InterDir(ctxId));
    
          DTRACE( g_trace_ctx, D_SYNTAX, "inter_pred_idc() ctx=%d value=%d pos=(%d,%d)\n", ctxId, pu.interDir, pu.lumaPos().x, pu.lumaPos().y );
          return;
        }
        else
        {
    
    Frank Bossen's avatar
    Frank Bossen committed
          m_binEncoder.encodeBin(0, Ctx::InterDir(ctxId));
    
    Frank Bossen's avatar
    Frank Bossen committed
      m_binEncoder.encodeBin((pu.interDir == 2), Ctx::InterDir(5));
    
      DTRACE( g_trace_ctx, D_SYNTAX, "inter_pred_idc() ctx=5 value=%d pos=(%d,%d)\n", pu.interDir, pu.lumaPos().x, pu.lumaPos().y );
    
    }
    
    void CABACWriter::ref_idx( const PredictionUnit& pu, RefPicList eRefList )
    {
    
      if ( pu.cu->smvdMode )
      {
        CHECK( pu.refIdx[eRefList] != pu.cs->slice->getSymRefIdx( eRefList ), "Invalid reference index!\n" );
        return;
      }
    
    
      int numRef  = pu.cs->slice->getNumRefIdx(eRefList);
    
    Yu Han's avatar
    Yu Han committed
      if (eRefList == REF_PIC_LIST_0 && pu.cs->sps->getIBCFlag())
    
    Yu Han's avatar
    Yu Han committed
      {
        if (CU::isIBC(*pu.cu))
    
    Yu Han's avatar
    Yu Han committed
          return;
    
    Frank Bossen's avatar
    Frank Bossen committed
      m_binEncoder.encodeBin((refIdx > 0), Ctx::RefPic());
    
      if( numRef <= 2 || refIdx == 0 )
      {
        DTRACE( g_trace_ctx, D_SYNTAX, "ref_idx() value=%d pos=(%d,%d)\n", refIdx, pu.lumaPos().x, pu.lumaPos().y );
        return;
      }
    
    Frank Bossen's avatar
    Frank Bossen committed
      m_binEncoder.encodeBin((refIdx > 1), Ctx::RefPic(1));
    
      if( numRef <= 3 || refIdx == 1 )
      {
        DTRACE( g_trace_ctx, D_SYNTAX, "ref_idx() value=%d pos=(%d,%d)\n", refIdx, pu.lumaPos().x, pu.lumaPos().y );
        return;
      }
      for( int idx = 3; idx < numRef; idx++ )
      {
        if( refIdx > idx - 1 )
        {
    
    Frank Bossen's avatar
    Frank Bossen committed
          m_binEncoder.encodeBinEP(1);
    
    Frank Bossen's avatar
    Frank Bossen committed
          m_binEncoder.encodeBinEP(0);
    
          break;
        }
      }
      DTRACE( g_trace_ctx, D_SYNTAX, "ref_idx() value=%d pos=(%d,%d)\n", refIdx, pu.lumaPos().x, pu.lumaPos().y );
    }
    
    void CABACWriter::mvp_flag( const PredictionUnit& pu, RefPicList eRefList )
    {
    
    Frank Bossen's avatar
    Frank Bossen committed
      m_binEncoder.encodeBin(pu.mvpIdx[eRefList], Ctx::MVPIdx());
    
      DTRACE( g_trace_ctx, D_SYNTAX, "mvp_flag() value=%d pos=(%d,%d)\n", pu.mvpIdx[eRefList], pu.lumaPos().x, pu.lumaPos().y );
      DTRACE( g_trace_ctx, D_SYNTAX, "mvpIdx(refList:%d)=%d\n", eRefList, pu.mvpIdx[eRefList] );
    }
    
    
    void CABACWriter::ciip_flag(const PredictionUnit &pu)
    
      if (!pu.cs->sps->getUseCiip())
    
        CHECK(pu.ciipFlag == true, "invalid Ciip SPS");
    
        CHECK(pu.ciipFlag == true, "invalid Ciip and skip");
    
    Frank Bossen's avatar
    Frank Bossen committed
      m_binEncoder.encodeBin(pu.ciipFlag, Ctx::CiipFlag());
    
      DTRACE(g_trace_ctx, D_SYNTAX, "ciip_flag() Ciip=%d pos=(%d,%d) size=%dx%d\n", pu.ciipFlag ? 1 : 0, pu.lumaPos().x,
             pu.lumaPos().y, pu.lumaSize().width, pu.lumaSize().height);
    
    //================================================================================
    //  clause 7.3.8.8
    //--------------------------------------------------------------------------------
    //    void  transform_tree      ( cs, area, cuCtx, chromaCbfs )
    //    bool  split_transform_flag( split, depth )
    //    bool  cbf_comp            ( cbf, area, depth )
    //================================================================================
    
    void CABACWriter::transform_tree( const CodingStructure& cs, Partitioner& partitioner, CUCtx& cuCtx,                         const PartSplit ispType, const int subTuIdx )
    
      const UnitArea&       area = partitioner.currArea();
      int             subTuCounter = subTuIdx;
    
      const TransformUnit  &tu           = *cs.getTU(area.block(partitioner.chType).pos(), partitioner.chType, subTuIdx);
    
      const CodingUnit&     cu = *tu.cu;
      const unsigned        trDepth = partitioner.currTrDepth;
      const bool            split = (tu.depth > trDepth);
    
    Karsten Suehring's avatar
    Karsten Suehring committed
      if( partitioner.canSplit( TU_MAX_TR_SPLIT, cs ) )
    
    Karsten Suehring's avatar
    Karsten Suehring committed
        CHECK( !split, "transform split implied" );
    
      else if( cu.sbtInfo && partitioner.canSplit( PartSplit( cu.getSbtTuSplit() ), cs ) )
      {
        CHECK( !split, "transform split implied - sbt" );
      }
    
    Karsten Suehring's avatar
    Karsten Suehring committed
      else
    
        CHECK(split && cu.ispMode == ISPType::NONE, "transform split not allowed with QTBT");
    
    
      if( split )
      {
        if( partitioner.canSplit( TU_MAX_TR_SPLIT, cs ) )
        {
    #if ENABLE_TRACING
    
          const CompArea &tuArea = partitioner.currArea().block(partitioner.chType);
    
          DTRACE( g_trace_ctx, D_SYNTAX, "transform_tree() maxTrSplit chType=%d pos=(%d,%d) size=%dx%d\n", partitioner.chType, tuArea.x, tuArea.y, tuArea.width, tuArea.height );
    
    #endif
          partitioner.splitCurrArea( TU_MAX_TR_SPLIT, cs );
        }
    
        else if (cu.ispMode != ISPType::NONE)
    
        {
          partitioner.splitCurrArea( ispType, cs );
        }
    
        else if( cu.sbtInfo && partitioner.canSplit( PartSplit( cu.getSbtTuSplit() ), cs ) )
        {
          partitioner.splitCurrArea( PartSplit( cu.getSbtTuSplit() ), cs );
        }
    
          transform_tree( cs, partitioner, cuCtx,                ispType, subTuCounter );
    
          subTuCounter += subTuCounter != -1 ? 1 : 0;
    
        } while( partitioner.nextPart( cs ) );
    
        partitioner.exitCurrSplit();
      }
      else
      {
    
        DTRACE(g_trace_ctx, D_SYNTAX, "transform_unit() pos=(%d,%d) size=%dx%d depth=%d trDepth=%d\n",
               tu.block(tu.chType).x, tu.block(tu.chType).y, tu.block(tu.chType).width, tu.block(tu.chType).height,
               cu.depth, partitioner.currTrDepth);
    
        transform_unit( tu, cuCtx, partitioner, subTuCounter);
    
    void CABACWriter::cbf_comp(bool cbf, const CompArea &area, unsigned depth, const bool prevCbf, const bool useISP,
                               const BdpcmMode bdpcmMode)
    
      unsigned  ctxId = DeriveCtx::CtxQtCbf(area.compID, prevCbf, useISP && isLuma(area.compID));
    
      const CtxSet&   ctxSet  = Ctx::QtCbf[ area.compID ];
    
      if (bdpcmMode != BdpcmMode::NONE)
    
        ctxId = area.compID == COMPONENT_Cr ? 2 : 1;
    
    
      m_binEncoder.encodeBin(cbf ? 1 : 0, ctxSet(ctxId));
    
      DTRACE(g_trace_ctx, D_SYNTAX, "cbf_comp() etype=%d pos=(%d,%d) ctx=%d cbf=%d\n", area.compID, area.x, area.y, ctxId,
             cbf ? 1 : 0);
    
    }
    
    //================================================================================
    //  clause 7.3.8.9
    //--------------------------------------------------------------------------------
    //================================================================================
    
    Xiang Li's avatar
    Xiang Li committed
    void CABACWriter::mvd_coding(const PredictionUnit& pu, Mv mvd, int amvr)
    
    Xiang Li's avatar
    Xiang Li committed
      if (CU::isIBC(*pu.cu))
      {
        mvd.changeIbcPrecInternal2Amvr(amvr);
      }
      else if (pu.cu->affine)
      {
        mvd.changeAffinePrecInternal2Amvr(amvr);
      }
      else
      {
        mvd.changeTransPrecInternal2Amvr(amvr);
    
    Xiang Li's avatar
    Xiang Li committed
      const int horMvd = mvd.getHor();
      const int verMvd = mvd.getVer();
    
    Xiang Li's avatar
    Xiang Li committed
      const unsigned int horAbs = std::abs(horMvd);
      const unsigned int verAbs = std::abs(verMvd);
      
    
    Frank Bossen's avatar
    Frank Bossen committed
      m_binEncoder.encodeBin((horAbs > 0), Ctx::Mvd());
      m_binEncoder.encodeBin((verAbs > 0), Ctx::Mvd());
    
    Frank Bossen's avatar
    Frank Bossen committed
        m_binEncoder.encodeBin((horAbs > 1), Ctx::Mvd(1));
    
    Frank Bossen's avatar
    Frank Bossen committed
        m_binEncoder.encodeBin((verAbs > 1), Ctx::Mvd(1));
    
      }
    
      // abs_mvd_minus2[ 0 | 1 ] and mvd_sign_flag[ 0 | 1 ]
      if( horAbs > 0 )
      {
        if( horAbs > 1 )
        {
    
    Frank Bossen's avatar
    Frank Bossen committed
          m_binEncoder.encodeRemAbsEP(horAbs - 2, 1, 0, MV_BITS - 1);
    
    Frank Bossen's avatar
    Frank Bossen committed
        m_binEncoder.encodeBinEP((horMvd < 0));
    
    Frank Bossen's avatar
    Frank Bossen committed
          m_binEncoder.encodeRemAbsEP(verAbs - 2, 1, 0, MV_BITS - 1);
    
    Frank Bossen's avatar
    Frank Bossen committed
        m_binEncoder.encodeBinEP((verMvd < 0));
    
      }
    }
    
    //================================================================================
    //  clause 7.3.8.10
    //--------------------------------------------------------------------------------
    //    void  transform_unit      ( tu, cuCtx, chromaCbfs )
    //    void  cu_qp_delta         ( cu )
    //    void  cu_chroma_qp_offset ( cu )
    //================================================================================
    
    void CABACWriter::transform_unit( const TransformUnit& tu, CUCtx& cuCtx, Partitioner& partitioner, const int subTuCounter)
    
      const CodingUnit&       cu = *tu.cu;
      const UnitArea&         area = partitioner.currArea();
      const unsigned          trDepth = partitioner.currTrDepth;
      ChromaCbfs              chromaCbfs;
      CHECK(tu.depth != trDepth, " transform unit should be not be futher partitioned");
    
      // cbf_cb & cbf_cr
    
      if (isChromaEnabled(area.chromaFormat))
    
        const bool chromaCbfISP = area.blocks[COMPONENT_Cb].valid() && cu.ispMode != ISPType::NONE;
    
        if (area.blocks[COMPONENT_Cb].valid() && (!cu.isSepTree() || partitioner.chType == ChannelType::CHROMA)
    
            && (cu.ispMode == ISPType::NONE || chromaCbfISP))
    
        {
          unsigned cbfDepth = chromaCbfISP ? trDepth - 1 : trDepth;
    
          chromaCbfs.Cb     = TU::getCbfAtDepth(tu, COMPONENT_Cb, trDepth);
          if (!(cu.sbtInfo && tu.noResidual))
    
            cbf_comp(chromaCbfs.Cb, area.blocks[COMPONENT_Cb], cbfDepth, false, false, cu.getBdpcmMode(COMPONENT_Cb));
    
          chromaCbfs.Cr = TU::getCbfAtDepth(tu, COMPONENT_Cr, trDepth);
          if (!(cu.sbtInfo && tu.noResidual))
    
            cbf_comp(chromaCbfs.Cr, area.blocks[COMPONENT_Cr], cbfDepth, chromaCbfs.Cb, false,
                     cu.getBdpcmMode(COMPONENT_Cr));
    
        else if (cu.isSepTree())
        {
          chromaCbfs = ChromaCbfs(false);
        }
    
      }
      else if (cu.isSepTree())
      {
        chromaCbfs = ChromaCbfs(false);
      }
    
    
      if (!isChroma(partitioner.chType))
      {
        if (!CU::isIntra(cu) && trDepth == 0 && !chromaCbfs.sigChroma(area.chromaFormat))
        {
          CHECK(!TU::getCbfAtDepth(tu, COMPONENT_Y, trDepth), "Luma cbf must be true for inter units with no chroma coeffs");
        }
        else if (cu.sbtInfo && tu.noResidual)
        {
          CHECK(TU::getCbfAtDepth(tu, COMPONENT_Y, trDepth), "Luma cbf must be false for inter sbt no-residual tu");
        }
        else if (cu.sbtInfo && !chromaCbfs.sigChroma(area.chromaFormat))
        {
          assert(!tu.noResidual);
          CHECK(!TU::getCbfAtDepth(tu, COMPONENT_Y, trDepth), "Luma cbf must be true for inter sbt residual tu");
        }
        else
        {
    
          bool lumaCbfIsInferredACT =
            (cu.colorTransform && CU::isIntra(cu) && trDepth == 0 && !chromaCbfs.sigChroma(area.chromaFormat));
    
          CHECK(lumaCbfIsInferredACT && !TU::getCbfAtDepth(tu, COMPONENT_Y, trDepth), "adaptive color transform cannot have all zero coefficients");
          bool lastCbfIsInferred    = lumaCbfIsInferredACT; // ISP and ACT are mutually exclusive
          bool previousCbf          = false;
          bool rootCbfSoFar         = false;
    
          if (cu.ispMode != ISPType::NONE)
    
            uint32_t nTus =
              cu.ispMode == ISPType::HOR ? cu.lheight() >> floorLog2(tu.lheight()) : cu.lwidth() >> floorLog2(tu.lwidth());
    
            if (subTuCounter == nTus - 1)
            {
              TransformUnit* tuPointer = cu.firstTU;
              for (int tuIdx = 0; tuIdx < subTuCounter; tuIdx++)
              {
                rootCbfSoFar |= TU::getCbfAtDepth(*tuPointer, COMPONENT_Y, trDepth);
                tuPointer = tuPointer->next;
              }
              if (!rootCbfSoFar)
              {
                lastCbfIsInferred = true;
              }
            }
            if (!lastCbfIsInferred)
            {
              previousCbf = TU::getPrevTuCbfAtDepth(tu, COMPONENT_Y, partitioner.currTrDepth);
            }
          }
          if (!lastCbfIsInferred)
          {
    
            cbf_comp(TU::getCbfAtDepth(tu, COMPONENT_Y, trDepth), tu.Y(), trDepth, previousCbf, cu.ispMode != ISPType::NONE,
    
                     cu.getBdpcmMode(COMPONENT_Y));
    
      bool        lumaOnly  = !isChromaEnabled(cu.chromaFormat) || !tu.blocks[COMPONENT_Cb].valid();
    
      bool        cbf[3]    = { TU::getCbf( tu, COMPONENT_Y ), chromaCbfs.Cb, chromaCbfs.Cr };
      bool        cbfLuma   = ( cbf[ COMPONENT_Y ] != 0 );
      bool        cbfChroma = false;
    
    
      {
        if( tu.blocks[COMPONENT_Cb].valid() )
        {
          cbf   [ COMPONENT_Cb  ] = TU::getCbf( tu, COMPONENT_Cb );
          cbf   [ COMPONENT_Cr  ] = TU::getCbf( tu, COMPONENT_Cr );
        }
        cbfChroma = ( cbf[ COMPONENT_Cb ] || cbf[ COMPONENT_Cr ] );
      }
    
      if( ( cu.lwidth() > 64 || cu.lheight() > 64 || cbfLuma || cbfChroma ) &&
    
    Yin Zhao's avatar
    Yin Zhao committed
        (!tu.cu->isSepTree() || isLuma(tu.chType)) )
    
      {
        if( cu.cs->pps->getUseDQP() && !cuCtx.isDQPCoded )
        {
    
          cu_qp_delta(cu, cuCtx.qp, cu.qp);
          cuCtx.qp = cu.qp;
          cuCtx.isDQPCoded = true;
    
      if (!cu.isSepTree() || isChroma(tu.chType))   // !DUAL_TREE_LUMA
      {
        SizeType channelWidth = !cu.isSepTree() ? cu.lwidth() : cu.chromaSize().width;
        SizeType channelHeight = !cu.isSepTree() ? cu.lheight() : cu.chromaSize().height;
    
        if (cu.cs->slice->getUseChromaQpAdj() && (channelWidth > 64 || channelHeight > 64 || cbfChroma) && !cuCtx.isChromaQpAdjCoded)
        {
          cu_chroma_qp_offset(cu);
          cuCtx.isChromaQpAdjCoded = true;
        }
      }
    
    
      if( !lumaOnly )
      {
        joint_cb_cr( tu, ( cbf[COMPONENT_Cb] ? 2 : 0 ) + ( cbf[COMPONENT_Cr] ? 1 : 0 ) );
      }
    
    
      if (cbfLuma)
      {
        residual_coding(tu, COMPONENT_Y, &cuCtx);
      }
      if (!lumaOnly)
      {
        for (ComponentID compID = COMPONENT_Cb; compID <= COMPONENT_Cr; compID = ComponentID(compID + 1))
    
          if (cbf[compID])
    
            residual_coding(tu, compID, &cuCtx);
    
      DTRACE_COND((isEncoding()), g_trace_ctx, D_DQP, "x=%d, y=%d, d=%d, qpAdj=%d\n", cu.block(cu.chType).lumaPos().x,
                  cu.block(cu.chType).lumaPos().y, cu.qtDepth, cu.chromaQpAdj);
    
    }
    
    void CABACWriter::cu_qp_delta( const CodingUnit& cu, int predQP, const int8_t qp )
    {
      CHECK(!( predQP != std::numeric_limits<int>::max()), "Unspecified error");
      int       DQp         = qp - predQP;
    
      int       qpBdOffsetY = cu.cs->sps->getQpBDOffset(ChannelType::LUMA);
    
      DQp                   = ( DQp + (MAX_QP + 1) + (MAX_QP + 1) / 2 + qpBdOffsetY + (qpBdOffsetY / 2)) % ((MAX_QP + 1) + qpBdOffsetY) - (MAX_QP + 1) / 2 - (qpBdOffsetY / 2);
      unsigned  absDQP      = unsigned( DQp < 0 ? -DQp : DQp );
      unsigned  unaryDQP    = std::min<unsigned>( absDQP, CU_DQP_TU_CMAX );
    
      unary_max_symbol( unaryDQP, Ctx::DeltaQP(), Ctx::DeltaQP(1), CU_DQP_TU_CMAX );
      if( absDQP >= CU_DQP_TU_CMAX )
      {
        exp_golomb_eqprob( absDQP - CU_DQP_TU_CMAX, CU_DQP_EG_k );
      }
      if( absDQP > 0 )
      {
    
    Frank Bossen's avatar
    Frank Bossen committed
        m_binEncoder.encodeBinEP(DQp < 0);
    
      DTRACE_COND((isEncoding()), g_trace_ctx, D_DQP, "x=%d, y=%d, d=%d, pred_qp=%d, DQp=%d, qp=%d\n",
                  cu.block(cu.chType).lumaPos().x, cu.block(cu.chType).lumaPos().y, cu.qtDepth, predQP, DQp, qp);
    
    }
    
    
    void CABACWriter::cu_chroma_qp_offset( const CodingUnit& cu )
    {
      // cu_chroma_qp_offset_flag
      unsigned qpAdj = cu.chromaQpAdj;
      if( qpAdj == 0 )
      {
    
    Frank Bossen's avatar
    Frank Bossen committed
        m_binEncoder.encodeBin(0, Ctx::ChromaQpAdjFlag());
    
    Frank Bossen's avatar
    Frank Bossen committed
        m_binEncoder.encodeBin(1, Ctx::ChromaQpAdjFlag());
    
        int length = cu.cs->pps->getChromaQpOffsetListLen();
    
        if( length > 1 )
        {
          unary_max_symbol( qpAdj-1, Ctx::ChromaQpAdjIdc(), Ctx::ChromaQpAdjIdc(), length-1 );
        }
      }
    }
    
    //================================================================================
    //  clause 7.3.8.11
    //--------------------------------------------------------------------------------
    //    void        residual_coding         ( tu, compID )
    //    void        transform_skip_flag     ( tu, compID )
    //    void        last_sig_coeff          ( coeffCtx )
    //    void        residual_coding_subblock( coeffCtx )
    //================================================================================
    
    
    void CABACWriter::joint_cb_cr( const TransformUnit& tu, const int cbfMask )
    {
    
      if ( !tu.cu->slice->getSPS()->getJointCbCrEnabledFlag() )
    
      CHECK( tu.jointCbCr && tu.jointCbCr != cbfMask, "wrong value of jointCbCr (" << (int)tu.jointCbCr << " vs " << (int)cbfMask << ")" );
    
      if ((CU::isIntra(*tu.cu) && cbfMask != 0) || cbfMask == CBF_MASK_CBCR)
    
    Frank Bossen's avatar
    Frank Bossen committed
        m_binEncoder.encodeBin(tu.jointCbCr ? 1 : 0, Ctx::JointCbCrFlag(cbfMask - 1));
    
    Mischa Siekmann's avatar
    Mischa Siekmann committed
    void CABACWriter::residual_coding( const TransformUnit& tu, ComponentID compID, CUCtx* cuCtx )
    
    {
      const CodingUnit& cu = *tu.cu;
      DTRACE( g_trace_ctx, D_SYNTAX, "residual_coding() etype=%d pos=(%d,%d) size=%dx%d predMode=%d\n", tu.blocks[compID].compID, tu.blocks[compID].x, tu.blocks[compID].y, tu.blocks[compID].width, tu.blocks[compID].height, cu.predMode );
    
    
      if( compID == COMPONENT_Cr && tu.jointCbCr == 3 )
    
      ts_flag            ( tu, compID );
    
      if (tu.mtsIdx[compID] == MtsType::SKIP && !tu.cs->slice->getTSResidualCodingDisabledFlag())
    
      {
        residual_codingTS( tu, compID );
        return;
      }
    
    
      bool signHiding = cu.cs->slice->getSignDataHidingEnabledFlag();
    
      CoeffCodingContext  cctx(tu, compID, signHiding, BdpcmMode::NONE);
    
      const TCoeff*       coeff   = tu.getCoeffs( compID ).buf;
    
      // determine and set last coeff position and sig group flags
      int                      scanPosLast = -1;
      std::bitset<MLS_GRP_NUM> sigGroupFlags;
      for( int scanPos = 0; scanPos < cctx.maxNumCoeff(); scanPos++)
      {
        unsigned blkPos = cctx.blockPos( scanPos );
        if( coeff[blkPos] )
        {
          scanPosLast = scanPos;
          sigGroupFlags.set( scanPos >> cctx.log2CGSize() );
        }
      }
      CHECK( scanPosLast < 0, "Coefficient coding called for empty TU" );
      cctx.setScanPosLast(scanPosLast);
    
    
      if (cuCtx && tu.mtsIdx[compID] != MtsType::SKIP && tu.blocks[compID].height >= 4 && tu.blocks[compID].width >= 4)
    
    Mischa Siekmann's avatar
    Mischa Siekmann committed
      {
        const int maxLfnstPos = ((tu.blocks[compID].height == 4 && tu.blocks[compID].width == 4) || (tu.blocks[compID].height == 8 && tu.blocks[compID].width == 8)) ? 7 : 15;
    
        cuCtx->violatesLfnstConstrained[toChannelType(compID)] |= cctx.scanPosLast() > maxLfnstPos;
    
      if (cuCtx && tu.mtsIdx[compID] != MtsType::SKIP && tu.blocks[compID].height >= 4 && tu.blocks[compID].width >= 4)
    
    Jason Jung's avatar
    Jason Jung committed
        const int lfnstLastScanPosTh = isLuma( compID ) ? LFNST_LAST_SIG_LUMA : LFNST_LAST_SIG_CHROMA;
        cuCtx->lfnstLastScanPos |= cctx.scanPosLast() >= lfnstLastScanPosTh;
    
      if (cuCtx && isLuma(compID) && tu.mtsIdx[compID] != MtsType::SKIP)
    
      {
        cuCtx->mtsLastScanPos |= cctx.scanPosLast() >= 1;
      }
    
    
    
      last_sig_coeff( cctx, tu, compID );
    
      const int stateTab = ( tu.cs->slice->getDepQuantEnabledFlag() ? 32040 : 0 );
    
      int ctxBinSampleRatio = (compID == COMPONENT_Y) ? MAX_TU_LEVEL_CTX_CODED_BIN_CONSTRAINT_LUMA : MAX_TU_LEVEL_CTX_CODED_BIN_CONSTRAINT_CHROMA;
      cctx.regBinLimit = (tu.getTbAreaAfterCoefZeroOut(compID) * ctxBinSampleRatio) >> 4;
    
    
    Frank Bossen's avatar
    Frank Bossen committed
      int baseLevel = m_binEncoder.getCtx().getBaseLevel();
    
      cctx.setBaseLevel(baseLevel);
      if (tu.cs->slice->getSPS()->getSpsRangeExtension().getPersistentRiceAdaptationEnabledFlag())
      {
        cctx.setUpdateHist(1);
    
    Frank Bossen's avatar
    Frank Bossen committed
        unsigned riceStats    = m_binEncoder.getCtx().getGRAdaptStats((unsigned) compID);
    
        TCoeff historyValue = (TCoeff)1 << riceStats;
        cctx.setHistValue(historyValue);
      }
    
      for( int subSetId = ( cctx.scanPosLast() >> cctx.log2CGSize() ); subSetId >= 0; subSetId--)
      {
        cctx.initSubblock       ( subSetId, sigGroupFlags[subSetId] );
    
        if (tu.cs->sps->getMtsEnabled() && tu.cu->sbtInfo != 0 && tu.blocks[compID].height <= 32
            && tu.blocks[compID].width <= 32 && compID == COMPONENT_Y)
    
        {
          if( ( tu.blocks[ compID ].height == 32 && cctx.cgPosY() >= ( 16 >> cctx.log2CGHeight() ) )
           || ( tu.blocks[ compID ].width  == 32 && cctx.cgPosX() >= ( 16 >> cctx.log2CGWidth()  ) ) )
          {
            continue;
          }
        }
        residual_coding_subblock( cctx, coeff, stateTab, state );
    
        if ( cuCtx && isLuma(compID) && cctx.isSigGroup() && ( cctx.cgPosY() > 3 || cctx.cgPosX() > 3 ) )
        {
          cuCtx->violatesMtsCoeffConstraint = true;
        }
    
    void CABACWriter::ts_flag( const TransformUnit& tu, ComponentID compID )
    {
    
      const int tsFlag = tu.mtsIdx[compID] == MtsType::SKIP ? 1 : 0;
    
      if( TU::isTSAllowed ( tu, compID ) )
      {
    
    Frank Bossen's avatar
    Frank Bossen committed
        m_binEncoder.encodeBin(tsFlag, Ctx::TransformSkipFlag(ctxIdx));
    
      }
      DTRACE( g_trace_ctx, D_SYNTAX, "ts_flag() etype=%d pos=(%d,%d) mtsIdx=%d\n", COMPONENT_Y, tu.cu->lx(), tu.cu->ly(), tsFlag );
    }
    
    
    void CABACWriter::mts_idx( const CodingUnit& cu, CUCtx* cuCtx )
    
    {
      TransformUnit &tu = *cu.firstTU;
    
      MtsType        mtsIdx = tu.mtsIdx[COMPONENT_Y];
    
      if (CU::isMTSAllowed(cu, COMPONENT_Y) && cuCtx && !cuCtx->violatesMtsCoeffConstraint && cuCtx->mtsLastScanPos
          && cu.lfnstIdx == 0 && mtsIdx != MtsType::SKIP)
    
        int symbol = mtsIdx != MtsType::DCT2_DCT2 ? 1 : 0;
    
        int ctxIdx = 0;
    
    Frank Bossen's avatar
    Frank Bossen committed
        m_binEncoder.encodeBin(symbol, Ctx::MTSIdx(ctxIdx));
    
          for( int i = 0; i < 3; i++, ctxIdx++ )
          {
    
            symbol = mtsIdx > MtsType::DST7_DST7 + i ? 1 : 0;
    
    Frank Bossen's avatar
    Frank Bossen committed
            m_binEncoder.encodeBin(symbol, Ctx::MTSIdx(ctxIdx));
    
            if( !symbol )
            {
              break;
            }
          }
        }
      }
      DTRACE( g_trace_ctx, D_SYNTAX, "mts_idx() etype=%d pos=(%d,%d) mtsIdx=%d\n", COMPONENT_Y, tu.cu->lx(), tu.cu->ly(), mtsIdx);
    }
    
    void CABACWriter::isp_mode( const CodingUnit& cu )
    {
    
      if (!CU::isIntra(cu) || !isLuma(cu.chType) || cu.firstPU->multiRefIdx || !cu.cs->sps->getUseISP()
          || cu.bdpcmMode != BdpcmMode::NONE || !CU::canUseISP(cu, getFirstComponentOfChannel(cu.chType))
          || cu.colorTransform)
    
        CHECK(cu.ispMode != ISPType::NONE, "cu.ispMode != 0");
    
      if (cu.ispMode == ISPType::NONE)
    
    Frank Bossen's avatar
    Frank Bossen committed
        m_binEncoder.encodeBin(0, Ctx::ISPMode(0));
    
        m_binEncoder.encodeBin( 1, Ctx::ISPMode( 0 ) );
        m_binEncoder.encodeBin(cu.ispMode == ISPType::HOR ? 0 : 1, Ctx::ISPMode(1));
    
      DTRACE(g_trace_ctx, D_SYNTAX, "intra_subPartitions() etype=%d pos=(%d,%d) ispIdx=%d\n", cu.chType,
             cu.block(cu.chType).x, cu.block(cu.chType).y, (int) cu.ispMode);
    
    void CABACWriter::residual_lfnst_mode( const CodingUnit& cu, CUCtx& cuCtx )
    {
    
      int chIdx = cu.isSepTree() && isChroma(cu.chType) ? 1 : 0;
    
      if ((cu.ispMode != ISPType::NONE && !CU::canUseLfnstWithISP(cu, cu.chType))
          || (cu.cs->sps->getUseLFNST() && CU::isIntra(cu) && cu.mipFlag && !allowLfnstWithMip(cu.firstPU->lumaSize()))
    
          || (cu.isSepTree() && isChroma(cu.chType) && std::min(cu.blocks[1].width, cu.blocks[1].height) < 4)
    
          || (cu.blocks[chIdx].lumaSize().width > cu.cs->sps->getMaxTbSize()
              || cu.blocks[chIdx].lumaSize().height > cu.cs->sps->getMaxTbSize()))
    
      if( cu.cs->sps->getUseLFNST() && CU::isIntra( cu ) )
    
        const bool lumaFlag                   = cu.isSepTree() ? (   isLuma( cu.chType ) ? true : false ) : true;
        const bool chromaFlag                 = cu.isSepTree() ? ( isChroma( cu.chType ) ? true : false ) : true;
    
        bool       nonZeroCoeffNonTsCorner8x8 = (lumaFlag && cuCtx.violatesLfnstConstrained[ChannelType::LUMA])
                                          || (chromaFlag && cuCtx.violatesLfnstConstrained[ChannelType::CHROMA]);
    
        bool isTrSkip = false;
        for (auto &currTU : CU::traverseTUs(cu))
        {
          const uint32_t numValidComp = getNumberValidComponents(cu.chromaFormat);
          for (uint32_t compID = COMPONENT_Y; compID < numValidComp; compID++)
          {
    
            if (currTU.blocks[compID].valid() && TU::getCbf(currTU, (ComponentID) compID)
                && currTU.mtsIdx[compID] == MtsType::SKIP)
    
        if ((!cuCtx.lfnstLastScanPos && cu.ispMode == ISPType::NONE) || nonZeroCoeffNonTsCorner8x8 || isTrSkip)
    
      if (cu.isSepTree())
      {
        cctx++;
      }
    
    
      const uint32_t idxLFNST = cu.lfnstIdx;
      assert( idxLFNST < 3 );
    
    Frank Bossen's avatar
    Frank Bossen committed
      m_binEncoder.encodeBin(idxLFNST ? 1 : 0, Ctx::LFNSTIdx(cctx));
    
    Frank Bossen's avatar
    Frank Bossen committed
        m_binEncoder.encodeBin((idxLFNST - 1) ? 1 : 0, Ctx::LFNSTIdx(2));
    
      }
    
      DTRACE( g_trace_ctx, D_SYNTAX, "residual_lfnst_mode() etype=%d pos=(%d,%d) mode=%d\n", COMPONENT_Y, cu.lx(), cu.ly(), ( int ) cu.lfnstIdx );
    }
    
    void CABACWriter::last_sig_coeff( CoeffCodingContext& cctx, const TransformUnit& tu, ComponentID compID )
    
    {
      unsigned blkPos = cctx.blockPos( cctx.scanPosLast() );
      unsigned posX, posY;
      {
        posY  = blkPos / cctx.width();
        posX  = blkPos - ( posY * cctx.width() );
      }
    
      unsigned CtxLast;
    
    Frank Bossen's avatar
    Frank Bossen committed
      unsigned GroupIdxX = g_groupIdx[posX];
      unsigned GroupIdxY = g_groupIdx[posY];
    
      unsigned maxLastPosX = cctx.maxLastPosX();
      unsigned maxLastPosY = cctx.maxLastPosY();
    
      unsigned zoTbWdith  = getNonzeroTuSize(cctx.width());
      unsigned zoTbHeight = getNonzeroTuSize(cctx.height());
    
      if (tu.cs->sps->getMtsEnabled() && tu.cu->sbtInfo != 0 && tu.blocks[compID].width <= 32
          && tu.blocks[compID].height <= 32 && compID == COMPONENT_Y)
    
    Frank Bossen's avatar
    Frank Bossen committed
        maxLastPosX = (tu.blocks[compID].width == 32) ? g_groupIdx[15] : maxLastPosX;
        maxLastPosY = (tu.blocks[compID].height == 32) ? g_groupIdx[15] : maxLastPosY;
    
        zoTbWdith = (tu.blocks[compID].width == 32) ? 16 : zoTbWdith;
        zoTbHeight = (tu.blocks[compID].height == 32) ? 16 : zoTbHeight;
      }
      if (isEncoding())
      {
        if ((posX + posY) > ((zoTbWdith + zoTbHeight + 2) / 2))
        {
          tu.cu->slice->updateCntRightBottom(1);
        }
        else
        {
          tu.cu->slice->updateCntRightBottom(-1);
        }
    
      if (tu.cu->slice->getReverseLastSigCoeffFlag())
      {
        posX = zoTbWdith - 1 - posX;
        posY = zoTbHeight - 1 - posY;
    
        GroupIdxX = g_groupIdx[posX];
        GroupIdxY = g_groupIdx[posY];
      }
    
      for( CtxLast = 0; CtxLast < GroupIdxX; CtxLast++ )
      {
    
    Frank Bossen's avatar
    Frank Bossen committed
        m_binEncoder.encodeBin(1, cctx.lastXCtxId(CtxLast));
    
      if( GroupIdxX < maxLastPosX )
    
    Frank Bossen's avatar
    Frank Bossen committed
        m_binEncoder.encodeBin(0, cctx.lastXCtxId(CtxLast));
    
      }
      for( CtxLast = 0; CtxLast < GroupIdxY; CtxLast++ )
      {
    
    Frank Bossen's avatar
    Frank Bossen committed
        m_binEncoder.encodeBin(1, cctx.lastYCtxId(CtxLast));
    
      if( GroupIdxY < maxLastPosY )
    
    Frank Bossen's avatar
    Frank Bossen committed
        m_binEncoder.encodeBin(0, cctx.lastYCtxId(CtxLast));
    
    Frank Bossen's avatar
    Frank Bossen committed
        posX -= g_minInGroup[GroupIdxX];
    
        for (int i = ( ( GroupIdxX - 2 ) >> 1 ) - 1 ; i >= 0; i-- )
        {
    
    Frank Bossen's avatar
    Frank Bossen committed
          m_binEncoder.encodeBinEP((posX >> i) & 1);
    
    Frank Bossen's avatar
    Frank Bossen committed
        posY -= g_minInGroup[GroupIdxY];
    
        for ( int i = ( ( GroupIdxY - 2 ) >> 1 ) - 1 ; i >= 0; i-- )
        {
    
    Frank Bossen's avatar
    Frank Bossen committed
          m_binEncoder.encodeBinEP((posY >> i) & 1);
    
        }
      }
    }
    
    void CABACWriter::residual_coding_subblock( CoeffCodingContext& cctx, const TCoeff* coeff, const int stateTransTable, int& state )
    {
      //===== init =====
      const int   minSubPos   = cctx.minSubPos();
      const bool  isLast      = cctx.isLast();
      int         firstSigPos = ( isLast ? cctx.scanPosLast() : cctx.maxSubPos() );
      int         nextSigPos  = firstSigPos;
    
      int baseLevel = cctx.getBaseLevel();
      bool updateHistory = cctx.getUpdateHist();
    
    
      //===== encode significant_coeffgroup_flag =====
      if( !isLast && cctx.isNotFirst() )
      {
        if( cctx.isSigGroup() )
        {
    
    Frank Bossen's avatar
    Frank Bossen committed
          m_binEncoder.encodeBin(1, cctx.sigGroupCtxId());
    
    Frank Bossen's avatar
    Frank Bossen committed
          m_binEncoder.encodeBin(0, cctx.sigGroupCtxId());
    
      const int inferSigPos     = nextSigPos != cctx.scanPosLast() ? (cctx.isNotFirst() ? minSubPos : -1) : nextSigPos;
      int       firstNZPos      = nextSigPos;
      int       lastNZPos       = -1;
      int       numNonZero      = 0;
      unsigned  signPattern     = 0;
      int       remRegBins      = cctx.regBinLimit;
      int       firstPos2ndPass = minSubPos - 1;
    
      for( ; nextSigPos >= minSubPos && remRegBins >= 4; nextSigPos-- )
    
        const TCoeff level   = coeff[cctx.blockPos(nextSigPos)];
        unsigned     sigFlag = (level != 0);
    
        if( numNonZero || nextSigPos != inferSigPos )
        {
          const unsigned sigCtxId = cctx.sigCtxIdAbs( nextSigPos, coeff, state );
    
    Frank Bossen's avatar
    Frank Bossen committed
          m_binEncoder.encodeBin(sigFlag, sigCtxId);
    
          DTRACE( g_trace_ctx, D_SYNTAX_RESI, "sig_bin() bin=%d ctx=%d\n", sigFlag, sigCtxId );
    
        else if( nextSigPos != cctx.scanPosLast() )
        {
          cctx.sigCtxIdAbs( nextSigPos, coeff, state ); // required for setting variables that are needed for gtx/par context selection
        }
    
    
        if( sigFlag )
        {
          uint8_t&  ctxOff  = ctxOffset[ nextSigPos - minSubPos ];
          ctxOff            = cctx.ctxOffsetAbs();
          numNonZero++;
          firstNZPos  = nextSigPos;
          lastNZPos   = std::max<int>( lastNZPos, nextSigPos );
    
          const TCoeff absLevel = abs(level);
    
          if (nextSigPos != cctx.scanPosLast())
          {
            signPattern <<= 1;
          }
    
          {
            signPattern++;
          }
    
          const bool gt1 = absLevel > 1;
          m_binEncoder.encodeBin( gt1, cctx.greater1CtxIdAbs(ctxOff) );
    
          DTRACE( g_trace_ctx, D_SYNTAX_RESI, "gt1_flag() bin=%d ctx=%d\n", gt1, cctx.greater1CtxIdAbs(ctxOff) );
          remRegBins--;
    
          if( gt1 )
          {
    
            m_binEncoder.encodeBin(absLevel & 1, cctx.parityCtxIdAbs(ctxOff));
            DTRACE(g_trace_ctx, D_SYNTAX_RESI, "par_flag() bin=%d ctx=%d\n", absLevel & 1, cctx.parityCtxIdAbs(ctxOff));
    
            const bool gt2 = absLevel > 3;
    
    Frank Bossen's avatar
    Frank Bossen committed
            m_binEncoder.encodeBin(gt2, cctx.greater2CtxIdAbs(ctxOff));
    
            DTRACE(g_trace_ctx, D_SYNTAX_RESI, "gt2_flag() bin=%d ctx=%d\n", gt2, cctx.greater2CtxIdAbs(ctxOff));
            remRegBins--;
    
    
            if (gt2)
            {
              // Start 2nd pass with first coeff that has absLevel > 3
              firstPos2ndPass = std::max(firstPos2ndPass, nextSigPos);
            }
    
        state = (stateTransTable >> ((state << 2) + ((level & 1) << 1))) & 3;
    
      int minPos2ndPass = nextSigPos;
    
      cctx.regBinLimit = remRegBins;
    
      //===== 2nd PASS: Go-rice codes =====
    
      for (int scanPos = firstPos2ndPass; scanPos > minPos2ndPass; scanPos--)
    
        unsigned absLevel = (unsigned) abs( coeff[ cctx.blockPos( scanPos ) ] );
    
          const unsigned ricePar = (cctx.*(cctx.deriveRiceRRC))(scanPos, coeff, baseLevel);
    
    
          unsigned rem      = ( absLevel - 4 ) >> 1;
    
    Frank Bossen's avatar
    Frank Bossen committed
          m_binEncoder.encodeRemAbsEP(rem, ricePar, COEF_REMAIN_BIN_REDUCTION, cctx.maxLog2TrDRange());
    
          DTRACE( g_trace_ctx, D_SYNTAX_RESI, "rem_val() bin=%d ctx=%d\n", rem, ricePar );
    
          if ((updateHistory) && (rem > 0))