# Global Metrics

path: .metrics.nargs.sum
old: 77.0
new: 12.0
path: .metrics.nargs.average
old: 2.2
new: 0.8571428571428571
path: .metrics.halstead.estimated_program_length
old: 2745.5915000448863
new: 1173.1417466270589
path: .metrics.halstead.level
old: 0.011320199941193768
new: 0.015452901528850895
path: .metrics.halstead.vocabulary
old: 346.0
new: 174.0
path: .metrics.halstead.length
old: 3485.0
new: 1334.0
path: .metrics.halstead.volume
old: 29394.67937331399
new: 9928.886623462204
path: .metrics.halstead.time
old: 144258.7367224255
new: 35695.87312678521
path: .metrics.halstead.difficulty
old: 88.33766233766234
new: 64.7127659574468
path: .metrics.halstead.n2
old: 308.0
new: 141.0
path: .metrics.halstead.N1
old: 2053.0
new: 781.0
path: .metrics.halstead.N2
old: 1432.0
new: 553.0
path: .metrics.halstead.bugs
old: 6.297311336541657
new: 2.4820227329793325
path: .metrics.halstead.effort
old: 2596657.261003659
new: 642525.7162821338
path: .metrics.halstead.purity_ratio
old: 0.7878311334418612
new: 0.8794166016694595
path: .metrics.halstead.n1
old: 38.0
new: 33.0
path: .metrics.loc.lloc
old: 244.0
new: 106.0
path: .metrics.loc.cloc
old: 133.0
new: 86.0
path: .metrics.loc.ploc
old: 618.0
new: 250.0
path: .metrics.loc.sloc
old: 887.0
new: 381.0
path: .metrics.loc.blank
old: 136.0
new: 45.0
path: .metrics.nexits.sum
old: 32.0
new: 11.0
path: .metrics.nexits.average
old: 0.9142857142857144
new: 0.7857142857142857
path: .metrics.cognitive.average
old: 4.6
new: 2.642857142857143
path: .metrics.cognitive.sum
old: 161.0
new: 37.0
path: .metrics.cyclomatic.sum
old: 152.0
new: 52.0
path: .metrics.cyclomatic.average
old: 3.619047619047619
new: 4.0
path: .metrics.mi.mi_sei
old: -71.56072270358925
new: -15.32830288907909
path: .metrics.mi.mi_original
old: -27.4236473223269
new: 14.909991256090803
path: .metrics.mi.mi_visual_studio
old: 0.0
new: 8.719293132216844
path: .metrics.nom.total
old: 35.0
new: 14.0
path: .metrics.nom.functions
old: 35.0
new: 10.0
path: .metrics.nom.closures
old: 0.0
new: 4.0

# Spaces Data

## Minimal test - lines (16, 380)

path: .spaces[0].spaces[0].metrics.mi.mi_original
old: -26.473616939307604
new: 16.098179018225906
path: .spaces[0].spaces[0].metrics.mi.mi_sei
old: -70.63924346987328
new: -14.910614678451132
path: .spaces[0].spaces[0].metrics.mi.mi_visual_studio
old: 0.0
new: 9.414139776740296
path: .spaces[0].spaces[0].metrics.nargs.average
old: 2.2
new: 0.8571428571428571
path: .spaces[0].spaces[0].metrics.nargs.sum
old: 77.0
new: 12.0
path: .spaces[0].spaces[0].metrics.nexits.average
old: 0.9142857142857144
new: 0.7857142857142857
path: .spaces[0].spaces[0].metrics.nexits.sum
old: 32.0
new: 11.0
path: .spaces[0].spaces[0].metrics.cyclomatic.sum
old: 150.0
new: 50.0
path: .spaces[0].spaces[0].metrics.cyclomatic.average
old: 3.75
new: 4.545454545454546
path: .spaces[0].spaces[0].metrics.loc.lloc
old: 244.0
new: 106.0
path: .spaces[0].spaces[0].metrics.loc.ploc
old: 603.0
new: 246.0
path: .spaces[0].spaces[0].metrics.loc.blank
old: 134.0
new: 43.0
path: .spaces[0].spaces[0].metrics.loc.sloc
old: 864.0
new: 365.0
path: .spaces[0].spaces[0].metrics.loc.cloc
old: 127.0
new: 76.0
path: .spaces[0].spaces[0].metrics.cognitive.sum
old: 161.0
new: 37.0
path: .spaces[0].spaces[0].metrics.cognitive.average
old: 4.6
new: 2.642857142857143
path: .spaces[0].spaces[0].metrics.nom.closures
old: 0.0
new: 4.0
path: .spaces[0].spaces[0].metrics.nom.total
old: 35.0
new: 14.0
path: .spaces[0].spaces[0].metrics.nom.functions
old: 35.0
new: 10.0
path: .spaces[0].spaces[0].metrics.halstead.time
old: 146602.4003990225
new: 36043.32745231243
path: .spaces[0].spaces[0].metrics.halstead.vocabulary
old: 334.0
new: 171.0
path: .spaces[0].spaces[0].metrics.halstead.length
old: 3463.0
new: 1330.0
path: .spaces[0].spaces[0].metrics.halstead.volume
old: 29032.767964837643
new: 9865.743844798244
path: .spaces[0].spaces[0].metrics.halstead.effort
old: 2638843.207182405
new: 648779.8941416236
path: .spaces[0].spaces[0].metrics.halstead.N2
old: 1416.0
new: 550.0
path: .spaces[0].spaces[0].metrics.halstead.level
old: 0.011002081474873623
new: 0.015206611570247934
path: .spaces[0].spaces[0].metrics.halstead.n1
old: 38.0
new: 33.0
path: .spaces[0].spaces[0].metrics.halstead.purity_ratio
old: 0.7592894720580495
new: 0.8627378804317416
path: .spaces[0].spaces[0].metrics.halstead.bugs
old: 6.365333112534401
new: 2.498102957977555
path: .spaces[0].spaces[0].metrics.halstead.estimated_program_length
old: 2629.4194417370254
new: 1147.4413809742164
path: .spaces[0].spaces[0].metrics.halstead.n2
old: 296.0
new: 138.0
path: .spaces[0].spaces[0].metrics.halstead.N1
old: 2047.0
new: 780.0
path: .spaces[0].spaces[0].metrics.halstead.difficulty
old: 90.89189189189187
new: 65.76086956521739

### Code

    namespace widget {

    // The values below have been tested and found to be acceptable on a device
    // with a display refresh rate of 60Hz and touch sampling rate of 100Hz.
    // While their "ideal" values are dependent on the exact rates of each device,
    // the values we've picked below should be somewhat robust across a variation of
    // different rates. They mostly aim to avoid making predictions that are too far
    // away (in terms of distance) from the finger, and to detect pauses in the
    // finger motion without too much delay.

    // Maximum time between two consecutive data points to consider resampling
    // between them.
    // Values between 1x and 5x of the touch sampling interval are reasonable.
    static const double kTouchResampleWindowSize = 40.0;

    // These next two values constrain the sampling timestamp.
    // Our caller will usually adjust frame timestamps to be slightly in the past,
    // for example by 5ms. This means that, during normal operation, we will
    // maximally need to predict by [touch sampling rate] minus 5ms.
    // So we would like kTouchResampleMaxPredictMs to satisfy the following:
    // kTouchResampleMaxPredictMs + [frame time adjust] > [touch sampling rate]
    static const double kTouchResampleMaxPredictMs = 8.0;
    // This one is a protection against very outdated frame timestamps.
    // Values larger than the touch sampling interval and less than 3x of the vsync
    // interval are reasonable.
    static const double kTouchResampleMaxBacksampleMs = 20.0;

    // The maximum age of the most recent data point to consider resampling.
    // Should be between 1x and 3x of the touch sampling interval.
    static const double kTouchResampleOldTouchThresholdMs = 17.0;

    uint64_t TouchResampler::ProcessEvent(MultiTouchInput&& aInput) {
      mCurrentTouches.UpdateFromEvent(aInput);

      uint64_t eventId = mNextEventId;
      mNextEventId++;

      if (aInput.mType == MultiTouchInput::MULTITOUCH_MOVE) {
        // Touch move events are deferred until NotifyFrame.
        mDeferredTouchMoveEvents.push({std::move(aInput), eventId});
      } else {
        // Non-move events are transferred to the outgoing queue unmodified.
        // If there are pending touch move events, flush those out first, so that
        // events are emitted in the right order.
        FlushDeferredTouchMoveEventsUnresampled();
        if (mInResampledState) {
          // Return to a non-resampled state before emitting a non-move event.
          ReturnToNonResampledState();
        }
        EmitEvent(std::move(aInput), eventId);
      }

      return eventId;
    }

    void TouchResampler::NotifyFrame(const TimeStamp& aTimeStamp) {
      TimeStamp lastTouchTime = mCurrentTouches.LatestDataPointTime();
      if (mDeferredTouchMoveEvents.empty() ||
          (lastTouchTime &&
           lastTouchTime < aTimeStamp - TimeDuration::FromMilliseconds(
                                            kTouchResampleOldTouchThresholdMs))) {
        // We haven't received a touch move event in a while, so the fingers must
        // have stopped moving. Flush any old touch move events.
        FlushDeferredTouchMoveEventsUnresampled();

        if (mInResampledState) {
          // Make sure we pause at the resting position that we actually observed,
          // and not at a resampled position.
          ReturnToNonResampledState();
        }

        // Clear touch location history so that we don't resample across a pause.
        mCurrentTouches.ClearDataPoints();
        return;
      }

      MOZ_RELEASE_ASSERT(lastTouchTime);
      TimeStamp lowerBound = lastTouchTime - TimeDuration::FromMilliseconds(
                                                 kTouchResampleMaxBacksampleMs);
      TimeStamp upperBound = lastTouchTime + TimeDuration::FromMilliseconds(
                                                 kTouchResampleMaxPredictMs);
      TimeStamp sampleTime = clamped(aTimeStamp, lowerBound, upperBound);

      if (mLastEmittedEventTime && sampleTime < mLastEmittedEventTime) {
        // Keep emitted timestamps in order.
        sampleTime = mLastEmittedEventTime;
      }

      // We have at least one pending touch move event. Pick one of the events from
      // mDeferredTouchMoveEvents as the base event for the resampling adjustment.
      // We want to produce an event stream whose timestamps are in the right order.
      // As the base event, use the first event that's at or after sampleTime,
      // unless there is no such event, in that case use the last one we have. We
      // will set the timestamp on the resampled event to sampleTime later.
      // Flush out any older events so that everything remains in the right order.
      MultiTouchInput input;
      uint64_t eventId;
      while (true) {
        MOZ_RELEASE_ASSERT(!mDeferredTouchMoveEvents.empty());

        std::tie(input, eventId) = std::move(mDeferredTouchMoveEvents.front());
        mDeferredTouchMoveEvents.pop();
        if (mDeferredTouchMoveEvents.empty() || input.mTimeStamp >= sampleTime) {
          break;
        }

        // Flush this event to the outgoing queue without resampling. What ends up
        // on the screen will still be smooth because we will proceed to emit a
        // resampled event before the paint for this frame starts.
        PrependLeftoverHistoricalData(&input);
        MOZ_RELEASE_ASSERT(input.mTimeStamp < sampleTime);
        EmitEvent(std::move(input), eventId);
      }

      mOriginalOfResampledTouchMove = Nothing();

      // Compute the resampled touch positions.
      nsTArray resampledPositions;
      bool anyPositionDifferentFromOriginal = false;
      for (const auto& touch : input.mTouches) {
        ScreenIntPoint resampledPosition =
            mCurrentTouches.ResampleTouchPositionAtTime(
                touch.mIdentifier, touch.mScreenPoint, sampleTime);
        if (resampledPosition != touch.mScreenPoint) {
          anyPositionDifferentFromOriginal = true;
        }
        resampledPositions.AppendElement(resampledPosition);
      }

      if (anyPositionDifferentFromOriginal) {
        // Store a copy of the original event, so that we can return to an
        // non-resampled position later, if necessary.
        mOriginalOfResampledTouchMove = Some(input);

        // Add the original observed position to the historical data, as well as any
        // leftover historical positions from the previous touch move event, and
        // store the resampled values in the "final" position of the event.
        PrependLeftoverHistoricalData(&input);
        for (size_t i = 0; i < input.mTouches.Length(); i++) {
          auto& touch = input.mTouches[i];
          touch.mHistoricalData.AppendElement(SingleTouchData::HistoricalTouchData{
              input.mTimeStamp,
              touch.mScreenPoint,
              touch.mLocalScreenPoint,
              touch.mRadius,
              touch.mRotationAngle,
              touch.mForce,
          });

          // Remove any historical touch data that's in the future, compared to
          // sampleTime. This data will be included by upcoming touch move
          // events. This only happens if the frame timestamp can be older than the
          // event timestamp, i.e. if interpolation occurs (rather than
          // extrapolation).
          auto futureDataStart = std::find_if(
              touch.mHistoricalData.begin(), touch.mHistoricalData.end(),
              [sampleTime](
                  const SingleTouchData::HistoricalTouchData& aHistoricalData) {
                return aHistoricalData.mTimeStamp > sampleTime;
              });
          if (futureDataStart != touch.mHistoricalData.end()) {
            nsTArray futureData(
                Span(touch.mHistoricalData)
                    .From(futureDataStart.GetIndex()));
            touch.mHistoricalData.TruncateLength(futureDataStart.GetIndex());
            mRemainingTouchData.insert({touch.mIdentifier, std::move(futureData)});
          }

          touch.mScreenPoint = resampledPositions[i];
        }
        input.mTimeStamp = sampleTime;
      }

      EmitEvent(std::move(input), eventId);
      mInResampledState = anyPositionDifferentFromOriginal;
    }

    void TouchResampler::PrependLeftoverHistoricalData(MultiTouchInput* aInput) {
      for (auto& touch : aInput->mTouches) {
        auto leftoverData = mRemainingTouchData.find(touch.mIdentifier);
        if (leftoverData != mRemainingTouchData.end()) {
          nsTArray data =
              std::move(leftoverData->second);
          mRemainingTouchData.erase(leftoverData);
          touch.mHistoricalData.InsertElementsAt(0, data);
        }

        if (TimeStamp cutoffTime = mLastEmittedEventTime) {
          // If we received historical touch data that was further in the past than
          // the last resampled event, discard that data so that the touch data
          // points are emitted in order.
          touch.mHistoricalData.RemoveElementsBy(
              [cutoffTime](const SingleTouchData::HistoricalTouchData& aTouchData) {
                return aTouchData.mTimeStamp < cutoffTime;
              });
        }
      }
      mRemainingTouchData.clear();
    }

    void TouchResampler::FlushDeferredTouchMoveEventsUnresampled() {
      while (!mDeferredTouchMoveEvents.empty()) {
        MultiTouchInput input;
        uint64_t eventId;
        std::tie(input, eventId) = std::move(mDeferredTouchMoveEvents.front());
        mDeferredTouchMoveEvents.pop();
        PrependLeftoverHistoricalData(&input);
        EmitEvent(std::move(input), eventId);
        mInResampledState = false;
        mOriginalOfResampledTouchMove = Nothing();
      }
    }

    void TouchResampler::ReturnToNonResampledState() {
      MOZ_RELEASE_ASSERT(mInResampledState);
      MOZ_RELEASE_ASSERT(mDeferredTouchMoveEvents.empty(),
                         "Don't call this if there is a deferred touch move event. "
                         "We can return to the non-resampled state by sending that "
                         "event, rather than a copy of a previous event.");

      // The last outgoing event was a resampled touch move event.
      // Return to the non-resampled state, by sending a touch move event to
      // "overwrite" any resampled positions with the original observed positions.
      MultiTouchInput input = std::move(*mOriginalOfResampledTouchMove);
      mOriginalOfResampledTouchMove = Nothing();

      // For the event's timestamp, we want to backdate the correction as far as we
      // can, while still preserving timestamp ordering. But we also don't want to
      // backdate it to be older than it was originally.
      if (mLastEmittedEventTime > input.mTimeStamp) {
        input.mTimeStamp = mLastEmittedEventTime;
      }

      // Assemble the correct historical touch data for this event.
      // We don't want to include data points that we've already sent out with the
      // resampled event. And from the leftover data points, we only want those that
      // don't duplicate the final time + position of this event.
      for (auto& touch : input.mTouches) {
        touch.mHistoricalData.Clear();
      }
      PrependLeftoverHistoricalData(&input);
      for (auto& touch : input.mTouches) {
        touch.mHistoricalData.RemoveElementsBy([&](const auto& histData) {
          return histData.mTimeStamp >= input.mTimeStamp;
        });
      }

      EmitExtraEvent(std::move(input));
      mInResampledState = false;
    }

    void TouchResampler::TouchInfo::Update(const SingleTouchData& aTouch,
                                           const TimeStamp& aEventTime) {
      for (const auto& historicalData : aTouch.mHistoricalData) {
        mBaseDataPoint = mLatestDataPoint;
        mLatestDataPoint =
            Some(DataPoint{historicalData.mTimeStamp, historicalData.mScreenPoint});
      }
      mBaseDataPoint = mLatestDataPoint;
      mLatestDataPoint = Some(DataPoint{aEventTime, aTouch.mScreenPoint});
    }

    ScreenIntPoint TouchResampler::TouchInfo::ResampleAtTime(
        const ScreenIntPoint& aLastObservedPosition, const TimeStamp& aTimeStamp) {
      TimeStamp cutoff =
          aTimeStamp - TimeDuration::FromMilliseconds(kTouchResampleWindowSize);
      if (!mBaseDataPoint || !mLatestDataPoint ||
          !(mBaseDataPoint->mTimeStamp < mLatestDataPoint->mTimeStamp) ||
          mBaseDataPoint->mTimeStamp < cutoff) {
        return aLastObservedPosition;
      }

      // For the actual resampling, connect the last two data points with a line and
      // sample along that line.
      TimeStamp t1 = mBaseDataPoint->mTimeStamp;
      TimeStamp t2 = mLatestDataPoint->mTimeStamp;
      double t = (aTimeStamp - t1) / (t2 - t1);

      double x1 = mBaseDataPoint->mPosition.x;
      double x2 = mLatestDataPoint->mPosition.x;
      double y1 = mBaseDataPoint->mPosition.y;
      double y2 = mLatestDataPoint->mPosition.y;

      int32_t resampledX = round(x1 + t * (x2 - x1));
      int32_t resampledY = round(y1 + t * (y2 - y1));
      return ScreenIntPoint(resampledX, resampledY);
    }

    void TouchResampler::CurrentTouches::UpdateFromEvent(
        const MultiTouchInput& aInput) {
      switch (aInput.mType) {
        case MultiTouchInput::MULTITOUCH_START: {
          // A new touch has been added; make sure mTouches reflects the current
          // touches in the event.
          nsTArray newTouches;
          for (const auto& touch : aInput.mTouches) {
            const auto touchInfo = TouchByIdentifier(touch.mIdentifier);
            if (touchInfo != mTouches.end()) {
              // This is one of the existing touches.
              newTouches.AppendElement(std::move(*touchInfo));
              mTouches.RemoveElementAt(touchInfo);
            } else {
              // This is the new touch.
              newTouches.AppendElement(TouchInfo{
                  touch.mIdentifier, Nothing(),
                  Some(DataPoint{aInput.mTimeStamp, touch.mScreenPoint})});
            }
          }
          MOZ_ASSERT(mTouches.IsEmpty(), "Missing touch end before touch start?");
          mTouches = std::move(newTouches);
          break;
        }

        case MultiTouchInput::MULTITOUCH_MOVE: {
          // The touches have moved.
          // Add position information to the history data points.
          for (const auto& touch : aInput.mTouches) {
            const auto touchInfo = TouchByIdentifier(touch.mIdentifier);
            MOZ_ASSERT(touchInfo != mTouches.end());
            if (touchInfo != mTouches.end()) {
              touchInfo->Update(touch, aInput.mTimeStamp);
            }
          }
          mLatestDataPointTime = aInput.mTimeStamp;
          break;
        }

        case MultiTouchInput::MULTITOUCH_END: {
          // A touch has been removed.
          MOZ_RELEASE_ASSERT(aInput.mTouches.Length() == 1);
          const auto touchInfo = TouchByIdentifier(aInput.mTouches[0].mIdentifier);
          MOZ_ASSERT(touchInfo != mTouches.end());
          if (touchInfo != mTouches.end()) {
            mTouches.RemoveElementAt(touchInfo);
          }
          break;
        }

        case MultiTouchInput::MULTITOUCH_CANCEL:
          // All touches are canceled.
          mTouches.Clear();
          break;
      }
    }

    nsTArray::iterator
    TouchResampler::CurrentTouches::TouchByIdentifier(int32_t aIdentifier) {
      return std::find_if(mTouches.begin(), mTouches.end(),
                          [aIdentifier](const TouchInfo& info) {
                            return info.mIdentifier == aIdentifier;
                          });
    }

    ScreenIntPoint TouchResampler::CurrentTouches::ResampleTouchPositionAtTime(
        int32_t aIdentifier, const ScreenIntPoint& aLastObservedPosition,
        const TimeStamp& aTimeStamp) {
      const auto touchInfo = TouchByIdentifier(aIdentifier);
      MOZ_ASSERT(touchInfo != mTouches.end());
      if (touchInfo != mTouches.end()) {
        return touchInfo->ResampleAtTime(aLastObservedPosition, aTimeStamp);
      }
      return aLastObservedPosition;
    }

    }  // namespace widget

## Minimal test - lines (193, 214)

path: .spaces[0].spaces[0].spaces[2].metrics.nargs.sum
old: 0.0
new: 1.0
path: .spaces[0].spaces[0].spaces[2].metrics.nargs.average
old: 0.0
new: 0.5
path: .spaces[0].spaces[0].spaces[2].metrics.cyclomatic.sum
old: 1.0
new: 4.0
path: .spaces[0].spaces[0].spaces[2].metrics.cyclomatic.average
old: 1.0
new: 4.0
path: .spaces[0].spaces[0].spaces[2].metrics.loc.cloc
old: 0.0
new: 3.0
path: .spaces[0].spaces[0].spaces[2].metrics.loc.sloc
old: 7.0
new: 22.0
path: .spaces[0].spaces[0].spaces[2].metrics.loc.ploc
old: 7.0
new: 18.0
path: .spaces[0].spaces[0].spaces[2].metrics.loc.lloc
old: 0.0
new: 7.0
path: .spaces[0].spaces[0].spaces[2].metrics.loc.blank
old: 0.0
new: 1.0
path: .spaces[0].spaces[0].spaces[2].metrics.nexits.sum
old: 0.0
new: 1.0
path: .spaces[0].spaces[0].spaces[2].metrics.nexits.average
old: 0.0
new: 0.5
path: .spaces[0].spaces[0].spaces[2].metrics.nom.closures
old: 0.0
new: 1.0
path: .spaces[0].spaces[0].spaces[2].metrics.nom.total
old: 1.0
new: 2.0
path: .spaces[0].spaces[0].spaces[2].metrics.halstead.estimated_program_length
old: 59.71535681027101
new: 202.9220554270965
path: .spaces[0].spaces[0].spaces[2].metrics.halstead.effort
old: 291.89475010096186
new: 8427.882251675155
path: .spaces[0].spaces[0].spaces[2].metrics.halstead.n2
old: 13.0
new: 26.0
path: .spaces[0].spaces[0].spaces[2].metrics.halstead.purity_ratio
old: 2.132691314652536
new: 2.029220554270965
path: .spaces[0].spaces[0].spaces[2].metrics.halstead.time
old: 16.21637500560899
new: 468.21568064861975
path: .spaces[0].spaces[0].spaces[2].metrics.halstead.N1
old: 15.0
new: 58.0
path: .spaces[0].spaces[0].spaces[2].metrics.halstead.level
old: 0.4
new: 0.06516290726817042
path: .spaces[0].spaces[0].spaces[2].metrics.halstead.bugs
old: 0.01466773093381915
new: 0.1380461772802505
path: .spaces[0].spaces[0].spaces[2].metrics.halstead.volume
old: 116.75790004038474
new: 549.1853096329675
path: .spaces[0].spaces[0].spaces[2].metrics.halstead.length
old: 28.0
new: 100.0
path: .spaces[0].spaces[0].spaces[2].metrics.halstead.difficulty
old: 2.5
new: 15.346153846153848
path: .spaces[0].spaces[0].spaces[2].metrics.halstead.vocabulary
old: 18.0
new: 45.0
path: .spaces[0].spaces[0].spaces[2].metrics.halstead.n1
old: 5.0
new: 19.0
path: .spaces[0].spaces[0].spaces[2].metrics.halstead.N2
old: 13.0
new: 42.0
path: .spaces[0].spaces[0].spaces[2].metrics.mi.mi_visual_studio
old: 66.9553931405845
new: 50.99488037793438
path: .spaces[0].spaces[0].spaces[2].metrics.mi.mi_sei
old: 89.58049319981535
new: 77.58023067257346
path: .spaces[0].spaces[0].spaces[2].metrics.mi.mi_original
old: 114.4937222703995
new: 87.20124544626779
path: .spaces[0].spaces[0].spaces[2].metrics.cognitive.average
old: 0.0
new: 1.0
path: .spaces[0].spaces[0].spaces[2].metrics.cognitive.sum
old: 0.0
new: 2.0

### Code

    void TouchResampler::PrependLeftoverHistoricalData(MultiTouchInput* aInput) {
      for (auto& touch : aInput->mTouches) {
        auto leftoverData = mRemainingTouchData.find(touch.mIdentifier);
        if (leftoverData != mRemainingTouchData.end()) {
          nsTArray data =
              std::move(leftoverData->second);
          mRemainingTouchData.erase(leftoverData);
          touch.mHistoricalData.InsertElementsAt(0, data);
        }

        if (TimeStamp cutoffTime = mLastEmittedEventTime) {
          // If we received historical touch data that was further in the past than
          // the last resampled event, discard that data so that the touch data
          // points are emitted in order.
          touch.mHistoricalData.RemoveElementsBy(
              [cutoffTime](const SingleTouchData::HistoricalTouchData& aTouchData) {
                return aTouchData.mTimeStamp < cutoffTime;
              });
        }
      }
      mRemainingTouchData.clear();
    }

## Minimal test - lines (304, 359)

path: .spaces[0].spaces[0].spaces[7].metrics.cognitive.average
old: 5.0
new: 8.0
path: .spaces[0].spaces[0].spaces[7].metrics.cognitive.sum
old: 5.0
new: 8.0
path: .spaces[0].spaces[0].spaces[7].metrics.nexits.sum
old: 3.0
new: 0.0
path: .spaces[0].spaces[0].spaces[7].metrics.nexits.average
old: 3.0
new: 0.0
path: .spaces[0].spaces[0].spaces[7].metrics.cyclomatic.sum
old: 6.0
new: 10.0
path: .spaces[0].spaces[0].spaces[7].metrics.cyclomatic.average
old: 6.0
new: 10.0
path: .spaces[0].spaces[0].spaces[7].metrics.loc.lloc
old: 7.0
new: 24.0
path: .spaces[0].spaces[0].spaces[7].metrics.loc.cloc
old: 0.0
new: 8.0
path: .spaces[0].spaces[0].spaces[7].metrics.loc.sloc
old: 23.0
new: 56.0
path: .spaces[0].spaces[0].spaces[7].metrics.loc.ploc
old: 20.0
new: 45.0
path: .spaces[0].spaces[0].spaces[7].metrics.nargs.sum
old: 6.0
new: 1.0
path: .spaces[0].spaces[0].spaces[7].metrics.nargs.average
old: 6.0
new: 1.0
path: .spaces[0].spaces[0].spaces[7].metrics.mi.mi_sei
old: 46.9360149436372
new: 48.00148527986839
path: .spaces[0].spaces[0].spaces[7].metrics.mi.mi_visual_studio
old: 49.46312377679747
new: 38.529117662237184
path: .spaces[0].spaces[0].spaces[7].metrics.mi.mi_original
old: 84.58194165832367
new: 65.88479120242559
path: .spaces[0].spaces[0].spaces[7].metrics.halstead.level
old: 0.057416267942583726
new: 0.03272557269752221
path: .spaces[0].spaces[0].spaces[7].metrics.halstead.estimated_program_length
old: 227.91734062368369
new: 283.56683058238514
path: .spaces[0].spaces[0].spaces[7].metrics.halstead.purity_ratio
old: 1.7668010901060751
new: 1.2015543668745132
path: .spaces[0].spaces[0].spaces[7].metrics.halstead.vocabulary
old: 49.0
new: 58.0
path: .spaces[0].spaces[0].spaces[7].metrics.halstead.difficulty
old: 17.416666666666668
new: 30.557142857142857
path: .spaces[0].spaces[0].spaces[7].metrics.halstead.volume
old: 724.2975698908618
new: 1382.483514850107
path: .spaces[0].spaces[0].spaces[7].metrics.halstead.length
old: 129.0
new: 236.0
path: .spaces[0].spaces[0].spaces[7].metrics.halstead.bugs
old: 0.1806342599124885
new: 0.4043218538754398
path: .spaces[0].spaces[0].spaces[7].metrics.halstead.n1
old: 19.0
new: 23.0
path: .spaces[0].spaces[0].spaces[7].metrics.halstead.N1
old: 74.0
new: 143.0
path: .spaces[0].spaces[0].spaces[7].metrics.halstead.time
old: 700.8249634592136
new: 2346.930347828872
path: .spaces[0].spaces[0].spaces[7].metrics.halstead.N2
old: 55.0
new: 93.0
path: .spaces[0].spaces[0].spaces[7].metrics.halstead.effort
old: 12614.849342265845
new: 42244.746260919695
path: .spaces[0].spaces[0].spaces[7].metrics.halstead.n2
old: 30.0
new: 35.0

### Code

    void TouchResampler::CurrentTouches::UpdateFromEvent(
        const MultiTouchInput& aInput) {
      switch (aInput.mType) {
        case MultiTouchInput::MULTITOUCH_START: {
          // A new touch has been added; make sure mTouches reflects the current
          // touches in the event.
          nsTArray newTouches;
          for (const auto& touch : aInput.mTouches) {
            const auto touchInfo = TouchByIdentifier(touch.mIdentifier);
            if (touchInfo != mTouches.end()) {
              // This is one of the existing touches.
              newTouches.AppendElement(std::move(*touchInfo));
              mTouches.RemoveElementAt(touchInfo);
            } else {
              // This is the new touch.
              newTouches.AppendElement(TouchInfo{
                  touch.mIdentifier, Nothing(),
                  Some(DataPoint{aInput.mTimeStamp, touch.mScreenPoint})});
            }
          }
          MOZ_ASSERT(mTouches.IsEmpty(), "Missing touch end before touch start?");
          mTouches = std::move(newTouches);
          break;
        }

        case MultiTouchInput::MULTITOUCH_MOVE: {
          // The touches have moved.
          // Add position information to the history data points.
          for (const auto& touch : aInput.mTouches) {
            const auto touchInfo = TouchByIdentifier(touch.mIdentifier);
            MOZ_ASSERT(touchInfo != mTouches.end());
            if (touchInfo != mTouches.end()) {
              touchInfo->Update(touch, aInput.mTimeStamp);
            }
          }
          mLatestDataPointTime = aInput.mTimeStamp;
          break;
        }

        case MultiTouchInput::MULTITOUCH_END: {
          // A touch has been removed.
          MOZ_RELEASE_ASSERT(aInput.mTouches.Length() == 1);
          const auto touchInfo = TouchByIdentifier(aInput.mTouches[0].mIdentifier);
          MOZ_ASSERT(touchInfo != mTouches.end());
          if (touchInfo != mTouches.end()) {
            mTouches.RemoveElementAt(touchInfo);
          }
          break;
        }

        case MultiTouchInput::MULTITOUCH_CANCEL:
          // All touches are canceled.
          mTouches.Clear();
          break;
      }
    }

## Minimal test - lines (369, 378)

path: .spaces[0].spaces[0].spaces[9].metrics.nargs.sum
old: 2.0
new: 3.0
path: .spaces[0].spaces[0].spaces[9].metrics.nargs.average
old: 2.0
new: 3.0
path: .spaces[0].spaces[0].spaces[9].metrics.cognitive.average
old: 0.0
new: 1.0
path: .spaces[0].spaces[0].spaces[9].metrics.cognitive.sum
old: 0.0
new: 1.0
path: .spaces[0].spaces[0].spaces[9].metrics.loc.cloc
old: 1.0
new: 0.0
path: .spaces[0].spaces[0].spaces[9].metrics.loc.ploc
old: 7.0
new: 10.0
path: .spaces[0].spaces[0].spaces[9].metrics.loc.sloc
old: 9.0
new: 10.0
path: .spaces[0].spaces[0].spaces[9].metrics.loc.lloc
old: 2.0
new: 4.0
path: .spaces[0].spaces[0].spaces[9].metrics.loc.blank
old: 1.0
new: 0.0
path: .spaces[0].spaces[0].spaces[9].metrics.mi.mi_sei
old: 103.133039588922
new: 75.56279719065977
path: .spaces[0].spaces[0].spaces[9].metrics.mi.mi_original
old: 106.70495514208774
new: 104.70681965523568
path: .spaces[0].spaces[0].spaces[9].metrics.mi.mi_visual_studio
old: 62.40055856262441
new: 61.232058277915606
path: .spaces[0].spaces[0].spaces[9].metrics.nexits.sum
old: 1.0
new: 2.0
path: .spaces[0].spaces[0].spaces[9].metrics.nexits.average
old: 1.0
new: 2.0
path: .spaces[0].spaces[0].spaces[9].metrics.halstead.vocabulary
old: 29.0
new: 25.0
path: .spaces[0].spaces[0].spaces[9].metrics.halstead.purity_ratio
old: 2.380985698785835
new: 1.752408968163232
path: .spaces[0].spaces[0].spaces[9].metrics.halstead.volume
old: 228.3251067709959
new: 241.48052186828568
path: .spaces[0].spaces[0].spaces[9].metrics.halstead.N1
old: 26.0
new: 30.0
path: .spaces[0].spaces[0].spaces[9].metrics.halstead.level
old: 0.1020408163265306
new: 0.08391608391608392
path: .spaces[0].spaces[0].spaces[9].metrics.halstead.effort
old: 2237.5860463557597
new: 2877.6428855970707
path: .spaces[0].spaces[0].spaces[9].metrics.halstead.length
old: 47.0
new: 52.0
path: .spaces[0].spaces[0].spaces[9].metrics.halstead.n1
old: 14.0
new: 13.0
path: .spaces[0].spaces[0].spaces[9].metrics.halstead.estimated_program_length
old: 111.90632784293425
new: 91.12526634448808
path: .spaces[0].spaces[0].spaces[9].metrics.halstead.bugs
old: 0.057024993180707015
new: 0.06743779213953927
path: .spaces[0].spaces[0].spaces[9].metrics.halstead.n2
old: 15.0
new: 12.0
path: .spaces[0].spaces[0].spaces[9].metrics.halstead.N2
old: 21.0
new: 22.0
path: .spaces[0].spaces[0].spaces[9].metrics.halstead.difficulty
old: 9.8
new: 11.916666666666666
path: .spaces[0].spaces[0].spaces[9].metrics.halstead.time
old: 124.31033590865331
new: 159.86904919983726

### Code

    ScreenIntPoint TouchResampler::CurrentTouches::ResampleTouchPositionAtTime(
        int32_t aIdentifier, const ScreenIntPoint& aLastObservedPosition,
        const TimeStamp& aTimeStamp) {
      const auto touchInfo = TouchByIdentifier(aIdentifier);
      MOZ_ASSERT(touchInfo != mTouches.end());
      if (touchInfo != mTouches.end()) {
        return touchInfo->ResampleAtTime(aLastObservedPosition, aTimeStamp);
      }
      return aLastObservedPosition;
    }

## Minimal test - lines (229, 265)

path: .spaces[0].spaces[0].spaces[4].metrics.mi.mi_visual_studio
old: 65.81687303745743
new: 45.827681614794784
path: .spaces[0].spaces[0].spaces[4].metrics.mi.mi_sei
old: 86.77175440530047
new: 73.8186588141208
path: .spaces[0].spaces[0].spaces[4].metrics.mi.mi_original
old: 112.5468528940522
new: 78.36533556129908
path: .spaces[0].spaces[0].spaces[4].metrics.cyclomatic.sum
old: 1.0
new: 4.0
path: .spaces[0].spaces[0].spaces[4].metrics.cyclomatic.average
old: 1.0
new: 4.0
path: .spaces[0].spaces[0].spaces[4].metrics.nargs.sum
old: 1.0
new: 0.0
path: .spaces[0].spaces[0].spaces[4].metrics.nargs.average
old: 1.0
new: 0.0
path: .spaces[0].spaces[0].spaces[4].metrics.loc.blank
old: 2.0
new: 4.0
path: .spaces[0].spaces[0].spaces[4].metrics.loc.ploc
old: 6.0
new: 23.0
path: .spaces[0].spaces[0].spaces[4].metrics.loc.cloc
old: 0.0
new: 10.0
path: .spaces[0].spaces[0].spaces[4].metrics.loc.lloc
old: 3.0
new: 11.0
path: .spaces[0].spaces[0].spaces[4].metrics.loc.sloc
old: 8.0
new: 37.0
path: .spaces[0].spaces[0].spaces[4].metrics.nom.closures
old: 0.0
new: 1.0
path: .spaces[0].spaces[0].spaces[4].metrics.nom.total
old: 1.0
new: 2.0
path: .spaces[0].spaces[0].spaces[4].metrics.halstead.estimated_program_length
old: 48.18080946738404
new: 179.5259683185635
path: .spaces[0].spaces[0].spaces[4].metrics.halstead.difficulty
old: 7.714285714285714
new: 15.9375
path: .spaces[0].spaces[0].spaces[4].metrics.halstead.vocabulary
old: 16.0
new: 41.0
path: .spaces[0].spaces[0].spaces[4].metrics.halstead.N2
old: 12.0
new: 45.0
path: .spaces[0].spaces[0].spaces[4].metrics.halstead.volume
old: 112.0
new: 594.6882725126073
path: .spaces[0].spaces[0].spaces[4].metrics.halstead.effort
old: 864.0
new: 9477.84434316968
path: .spaces[0].spaces[0].spaces[4].metrics.halstead.purity_ratio
old: 1.7207431952637156
new: 1.6173510659330046
path: .spaces[0].spaces[0].spaces[4].metrics.halstead.N1
old: 16.0
new: 66.0
path: .spaces[0].spaces[0].spaces[4].metrics.halstead.bugs
old: 0.03023810519747695
new: 0.14928578628259745
path: .spaces[0].spaces[0].spaces[4].metrics.halstead.length
old: 28.0
new: 111.0
path: .spaces[0].spaces[0].spaces[4].metrics.halstead.n2
old: 7.0
new: 24.0
path: .spaces[0].spaces[0].spaces[4].metrics.halstead.level
old: 0.12962962962962962
new: 0.06274509803921569
path: .spaces[0].spaces[0].spaces[4].metrics.halstead.time
old: 48.0
new: 526.5469079538711
path: .spaces[0].spaces[0].spaces[4].metrics.halstead.n1
old: 9.0
new: 17.0
path: .spaces[0].spaces[0].spaces[4].metrics.nexits.average
old: 1.0
new: 0.5
path: .spaces[0].spaces[0].spaces[4].metrics.cognitive.sum
old: 0.0
new: 1.0
path: .spaces[0].spaces[0].spaces[4].metrics.cognitive.average
old: 0.0
new: 0.5

### Code

    void TouchResampler::ReturnToNonResampledState() {
      MOZ_RELEASE_ASSERT(mInResampledState);
      MOZ_RELEASE_ASSERT(mDeferredTouchMoveEvents.empty(),
                         "Don't call this if there is a deferred touch move event. "
                         "We can return to the non-resampled state by sending that "
                         "event, rather than a copy of a previous event.");

      // The last outgoing event was a resampled touch move event.
      // Return to the non-resampled state, by sending a touch move event to
      // "overwrite" any resampled positions with the original observed positions.
      MultiTouchInput input = std::move(*mOriginalOfResampledTouchMove);
      mOriginalOfResampledTouchMove = Nothing();

      // For the event's timestamp, we want to backdate the correction as far as we
      // can, while still preserving timestamp ordering. But we also don't want to
      // backdate it to be older than it was originally.
      if (mLastEmittedEventTime > input.mTimeStamp) {
        input.mTimeStamp = mLastEmittedEventTime;
      }

      // Assemble the correct historical touch data for this event.
      // We don't want to include data points that we've already sent out with the
      // resampled event. And from the leftover data points, we only want those that
      // don't duplicate the final time + position of this event.
      for (auto& touch : input.mTouches) {
        touch.mHistoricalData.Clear();
      }
      PrependLeftoverHistoricalData(&input);
      for (auto& touch : input.mTouches) {
        touch.mHistoricalData.RemoveElementsBy([&](const auto& histData) {
          return histData.mTimeStamp >= input.mTimeStamp;
        });
      }

      EmitExtraEvent(std::move(input));
      mInResampledState = false;
    }

## Minimal test - lines (216, 227)

path: .spaces[0].spaces[0].spaces[3].metrics.mi.mi_original
old: 105.04724328551264
new: 100.71571367949426
path: .spaces[0].spaces[0].spaces[3].metrics.mi.mi_sei
old: 99.58071679280668
new: 69.8048483917953
path: .spaces[0].spaces[0].spaces[3].metrics.mi.mi_visual_studio
old: 61.4311364242764
new: 58.89807817514284
path: .spaces[0].spaces[0].spaces[3].metrics.halstead.bugs
old: 0.05599031818965022
new: 0.06028408425377481
path: .spaces[0].spaces[0].spaces[3].metrics.halstead.difficulty
old: 9.625
new: 8.25
path: .spaces[0].spaces[0].spaces[3].metrics.halstead.n2
old: 12.0
new: 16.0
path: .spaces[0].spaces[0].spaces[3].metrics.halstead.N2
old: 21.0
new: 24.0
path: .spaces[0].spaces[0].spaces[3].metrics.halstead.vocabulary
old: 23.0
new: 27.0
path: .spaces[0].spaces[0].spaces[3].metrics.halstead.estimated_program_length
old: 81.07329781366414
new: 102.05374780501026
path: .spaces[0].spaces[0].spaces[3].metrics.halstead.length
old: 50.0
new: 62.0
path: .spaces[0].spaces[0].spaces[3].metrics.halstead.volume
old: 226.17809780285063
new: 294.80302513413505
path: .spaces[0].spaces[0].spaces[3].metrics.halstead.level
old: 0.1038961038961039
new: 0.12121212121212122
path: .spaces[0].spaces[0].spaces[3].metrics.halstead.purity_ratio
old: 1.6214659562732827
new: 1.646028190403391
path: .spaces[0].spaces[0].spaces[3].metrics.halstead.effort
old: 2176.9641913524374
new: 2432.1249573566142
path: .spaces[0].spaces[0].spaces[3].metrics.halstead.time
old: 120.9424550751354
new: 135.11805318647856
path: .spaces[0].spaces[0].spaces[3].metrics.halstead.N1
old: 29.0
new: 38.0
path: .spaces[0].spaces[0].spaces[3].metrics.loc.cloc
old: 1.0
new: 0.0
path: .spaces[0].spaces[0].spaces[3].metrics.loc.sloc
old: 10.0
new: 12.0
path: .spaces[0].spaces[0].spaces[3].metrics.loc.ploc
old: 9.0
new: 12.0
path: .spaces[0].spaces[0].spaces[3].metrics.loc.lloc
old: 5.0
new: 7.0

### Code

    void TouchResampler::FlushDeferredTouchMoveEventsUnresampled() {
      while (!mDeferredTouchMoveEvents.empty()) {
        MultiTouchInput input;
        uint64_t eventId;
        std::tie(input, eventId) = std::move(mDeferredTouchMoveEvents.front());
        mDeferredTouchMoveEvents.pop();
        PrependLeftoverHistoricalData(&input);
        EmitEvent(std::move(input), eventId);
        mInResampledState = false;
        mOriginalOfResampledTouchMove = Nothing();
      }
    }

## Minimal test - lines (71, 191)

path: .spaces[0].spaces[0].spaces[1].metrics.cognitive.average
old: 1.0
new: 9.0
path: .spaces[0].spaces[0].spaces[1].metrics.cognitive.sum
old: 1.0
new: 18.0
path: .spaces[0].spaces[0].spaces[1].metrics.halstead.volume
old: 243.00301253822127
new: 2844.7474784073006
path: .spaces[0].spaces[0].spaces[1].metrics.halstead.length
old: 53.0
new: 433.0
path: .spaces[0].spaces[0].spaces[1].metrics.halstead.n1
old: 14.0
new: 26.0
path: .spaces[0].spaces[0].spaces[1].metrics.halstead.n2
old: 10.0
new: 69.0
path: .spaces[0].spaces[0].spaces[1].metrics.halstead.N1
old: 31.0
new: 249.0
path: .spaces[0].spaces[0].spaces[1].metrics.halstead.vocabulary
old: 24.0
new: 95.0
path: .spaces[0].spaces[0].spaces[1].metrics.halstead.N2
old: 22.0
new: 184.0
path: .spaces[0].spaces[0].spaces[1].metrics.halstead.bugs
old: 0.08034650638737752
new: 0.7115126335112582
path: .spaces[0].spaces[0].spaces[1].metrics.halstead.estimated_program_length
old: 86.52224985768008
new: 543.6996201893621
path: .spaces[0].spaces[0].spaces[1].metrics.halstead.level
old: 0.06493506493506493
new: 0.028846153846153848
path: .spaces[0].spaces[0].spaces[1].metrics.halstead.purity_ratio
old: 1.6324952803335864
new: 1.2556573214534923
path: .spaces[0].spaces[0].spaces[1].metrics.halstead.time
old: 207.90257739381155
new: 5478.7729213770235
path: .spaces[0].spaces[0].spaces[1].metrics.halstead.difficulty
old: 15.4
new: 34.666666666666664
path: .spaces[0].spaces[0].spaces[1].metrics.halstead.effort
old: 3742.246393088608
new: 98617.91258478642
path: .spaces[0].spaces[0].spaces[1].metrics.nom.closures
old: 0.0
new: 1.0
path: .spaces[0].spaces[0].spaces[1].metrics.nom.total
old: 1.0
new: 2.0
path: .spaces[0].spaces[0].spaces[1].metrics.mi.mi_sei
old: 75.51564684010567
new: 29.20981880895701
path: .spaces[0].spaces[0].spaces[1].metrics.mi.mi_visual_studio
old: 61.21294591969983
new: 28.36339142139961
path: .spaces[0].spaces[0].spaces[1].metrics.mi.mi_original
old: 104.6741375226867
new: 48.50139933059333
path: .spaces[0].spaces[0].spaces[1].metrics.nargs.average
old: 1.0
new: 0.5
path: .spaces[0].spaces[0].spaces[1].metrics.cyclomatic.average
old: 2.0
new: 15.0
path: .spaces[0].spaces[0].spaces[1].metrics.cyclomatic.sum
old: 2.0
new: 15.0
path: .spaces[0].spaces[0].spaces[1].metrics.nexits.sum
old: 1.0
new: 2.0
path: .spaces[0].spaces[0].spaces[1].metrics.loc.ploc
old: 8.0
new: 80.0
path: .spaces[0].spaces[0].spaces[1].metrics.loc.sloc
old: 10.0
new: 121.0
path: .spaces[0].spaces[0].spaces[1].metrics.loc.cloc
old: 0.0
new: 27.0
path: .spaces[0].spaces[0].spaces[1].metrics.loc.blank
old: 2.0
new: 14.0
path: .spaces[0].spaces[0].spaces[1].metrics.loc.lloc
old: 4.0
new: 35.0

### Code

    void TouchResampler::NotifyFrame(const TimeStamp& aTimeStamp) {
      TimeStamp lastTouchTime = mCurrentTouches.LatestDataPointTime();
      if (mDeferredTouchMoveEvents.empty() ||
          (lastTouchTime &&
           lastTouchTime < aTimeStamp - TimeDuration::FromMilliseconds(
                                            kTouchResampleOldTouchThresholdMs))) {
        // We haven't received a touch move event in a while, so the fingers must
        // have stopped moving. Flush any old touch move events.
        FlushDeferredTouchMoveEventsUnresampled();

        if (mInResampledState) {
          // Make sure we pause at the resting position that we actually observed,
          // and not at a resampled position.
          ReturnToNonResampledState();
        }

        // Clear touch location history so that we don't resample across a pause.
        mCurrentTouches.ClearDataPoints();
        return;
      }

      MOZ_RELEASE_ASSERT(lastTouchTime);
      TimeStamp lowerBound = lastTouchTime - TimeDuration::FromMilliseconds(
                                                 kTouchResampleMaxBacksampleMs);
      TimeStamp upperBound = lastTouchTime + TimeDuration::FromMilliseconds(
                                                 kTouchResampleMaxPredictMs);
      TimeStamp sampleTime = clamped(aTimeStamp, lowerBound, upperBound);

      if (mLastEmittedEventTime && sampleTime < mLastEmittedEventTime) {
        // Keep emitted timestamps in order.
        sampleTime = mLastEmittedEventTime;
      }

      // We have at least one pending touch move event. Pick one of the events from
      // mDeferredTouchMoveEvents as the base event for the resampling adjustment.
      // We want to produce an event stream whose timestamps are in the right order.
      // As the base event, use the first event that's at or after sampleTime,
      // unless there is no such event, in that case use the last one we have. We
      // will set the timestamp on the resampled event to sampleTime later.
      // Flush out any older events so that everything remains in the right order.
      MultiTouchInput input;
      uint64_t eventId;
      while (true) {
        MOZ_RELEASE_ASSERT(!mDeferredTouchMoveEvents.empty());

        std::tie(input, eventId) = std::move(mDeferredTouchMoveEvents.front());
        mDeferredTouchMoveEvents.pop();
        if (mDeferredTouchMoveEvents.empty() || input.mTimeStamp >= sampleTime) {
          break;
        }

        // Flush this event to the outgoing queue without resampling. What ends up
        // on the screen will still be smooth because we will proceed to emit a
        // resampled event before the paint for this frame starts.
        PrependLeftoverHistoricalData(&input);
        MOZ_RELEASE_ASSERT(input.mTimeStamp < sampleTime);
        EmitEvent(std::move(input), eventId);
      }

      mOriginalOfResampledTouchMove = Nothing();

      // Compute the resampled touch positions.
      nsTArray resampledPositions;
      bool anyPositionDifferentFromOriginal = false;
      for (const auto& touch : input.mTouches) {
        ScreenIntPoint resampledPosition =
            mCurrentTouches.ResampleTouchPositionAtTime(
                touch.mIdentifier, touch.mScreenPoint, sampleTime);
        if (resampledPosition != touch.mScreenPoint) {
          anyPositionDifferentFromOriginal = true;
        }
        resampledPositions.AppendElement(resampledPosition);
      }

      if (anyPositionDifferentFromOriginal) {
        // Store a copy of the original event, so that we can return to an
        // non-resampled position later, if necessary.
        mOriginalOfResampledTouchMove = Some(input);

        // Add the original observed position to the historical data, as well as any
        // leftover historical positions from the previous touch move event, and
        // store the resampled values in the "final" position of the event.
        PrependLeftoverHistoricalData(&input);
        for (size_t i = 0; i < input.mTouches.Length(); i++) {
          auto& touch = input.mTouches[i];
          touch.mHistoricalData.AppendElement(SingleTouchData::HistoricalTouchData{
              input.mTimeStamp,
              touch.mScreenPoint,
              touch.mLocalScreenPoint,
              touch.mRadius,
              touch.mRotationAngle,
              touch.mForce,
          });

          // Remove any historical touch data that's in the future, compared to
          // sampleTime. This data will be included by upcoming touch move
          // events. This only happens if the frame timestamp can be older than the
          // event timestamp, i.e. if interpolation occurs (rather than
          // extrapolation).
          auto futureDataStart = std::find_if(
              touch.mHistoricalData.begin(), touch.mHistoricalData.end(),
              [sampleTime](
                  const SingleTouchData::HistoricalTouchData& aHistoricalData) {
                return aHistoricalData.mTimeStamp > sampleTime;
              });
          if (futureDataStart != touch.mHistoricalData.end()) {
            nsTArray futureData(
                Span(touch.mHistoricalData)
                    .From(futureDataStart.GetIndex()));
            touch.mHistoricalData.TruncateLength(futureDataStart.GetIndex());
            mRemainingTouchData.insert({touch.mIdentifier, std::move(futureData)});
          }

          touch.mScreenPoint = resampledPositions[i];
        }
        input.mTimeStamp = sampleTime;
      }

      EmitEvent(std::move(input), eventId);
      mInResampledState = anyPositionDifferentFromOriginal;
    }

## Minimal test - lines (361, 367)

path: .spaces[0].spaces[0].spaces[8].metrics.cognitive.average
old: 11.0
new: 0.0
path: .spaces[0].spaces[0].spaces[8].metrics.cognitive.sum
old: 11.0
new: 0.0
path: .spaces[0].spaces[0].spaces[8].metrics.nom.closures
old: 0.0
new: 1.0
path: .spaces[0].spaces[0].spaces[8].metrics.nom.total
old: 1.0
new: 2.0
path: .spaces[0].spaces[0].spaces[8].metrics.cyclomatic.sum
old: 8.0
new: 1.0
path: .spaces[0].spaces[0].spaces[8].metrics.cyclomatic.average
old: 8.0
new: 1.0
path: .spaces[0].spaces[0].spaces[8].metrics.nargs.sum
old: 3.0
new: 1.0
path: .spaces[0].spaces[0].spaces[8].metrics.nargs.average
old: 3.0
new: 0.5
path: .spaces[0].spaces[0].spaces[8].metrics.mi.mi_sei
old: 69.38777452819193
new: 85.6503418792255
path: .spaces[0].spaces[0].spaces[8].metrics.mi.mi_visual_studio
old: 45.8825438709911
new: 65.3623093352975
path: .spaces[0].spaces[0].spaces[8].metrics.mi.mi_original
old: 78.45915001939478
new: 111.76954896335872
path: .spaces[0].spaces[0].spaces[8].metrics.loc.blank
old: 3.0
new: 0.0
path: .spaces[0].spaces[0].spaces[8].metrics.loc.cloc
old: 6.0
new: 0.0
path: .spaces[0].spaces[0].spaces[8].metrics.loc.lloc
old: 9.0
new: 2.0
path: .spaces[0].spaces[0].spaces[8].metrics.loc.ploc
old: 23.0
new: 7.0
path: .spaces[0].spaces[0].spaces[8].metrics.loc.sloc
old: 32.0
new: 7.0
path: .spaces[0].spaces[0].spaces[8].metrics.nexits.sum
old: 3.0
new: 2.0
path: .spaces[0].spaces[0].spaces[8].metrics.nexits.average
old: 3.0
new: 1.0
path: .spaces[0].spaces[0].spaces[8].metrics.halstead.vocabulary
old: 49.0
new: 24.0
path: .spaces[0].spaces[0].spaces[8].metrics.halstead.difficulty
old: 17.93103448275862
new: 9.454545454545457
path: .spaces[0].spaces[0].spaces[8].metrics.halstead.length
old: 137.0
new: 43.0
path: .spaces[0].spaces[0].spaces[8].metrics.halstead.N1
old: 85.0
new: 27.0
path: .spaces[0].spaces[0].spaces[8].metrics.halstead.bugs
old: 0.191711235362376
new: 0.0504865092661884
path: .spaces[0].spaces[0].spaces[8].metrics.halstead.level
old: 0.05576923076923077
new: 0.10576923076923075
path: .spaces[0].spaces[0].spaces[8].metrics.halstead.effort
old: 13792.825148095428
new: 1863.9956639295465
path: .spaces[0].spaces[0].spaces[8].metrics.halstead.n2
old: 29.0
new: 11.0
path: .spaces[0].spaces[0].spaces[8].metrics.halstead.time
old: 766.2680637830794
new: 103.55531466275258
path: .spaces[0].spaces[0].spaces[8].metrics.halstead.estimated_program_length
old: 227.32001075644683
new: 86.15946414084446
path: .spaces[0].spaces[0].spaces[8].metrics.halstead.n1
old: 20.0
new: 13.0
path: .spaces[0].spaces[0].spaces[8].metrics.halstead.purity_ratio
old: 1.6592701515069113
new: 2.0037084683917317
path: .spaces[0].spaces[0].spaces[8].metrics.halstead.volume
old: 769.2152486437835
new: 197.1533875310097
path: .spaces[0].spaces[0].spaces[8].metrics.halstead.N2
old: 52.0
new: 16.0

### Code

    nsTArray::iterator
    TouchResampler::CurrentTouches::TouchByIdentifier(int32_t aIdentifier) {
      return std::find_if(mTouches.begin(), mTouches.end(),
                          [aIdentifier](const TouchInfo& info) {
                            return info.mIdentifier == aIdentifier;
                          });
    }

## Minimal test - lines (15, 381)

path: .spaces[0].metrics.halstead.time
old: 146371.6782975398
new: 35943.70910277471
path: .spaces[0].metrics.halstead.length
old: 3465.0
new: 1332.0
path: .spaces[0].metrics.halstead.N2
old: 1417.0
new: 551.0
path: .spaces[0].metrics.halstead.vocabulary
old: 335.0
new: 172.0
path: .spaces[0].metrics.halstead.N1
old: 2048.0
new: 781.0
path: .spaces[0].metrics.halstead.level
old: 0.01103146008988597
new: 0.015289006214596052
path: .spaces[0].metrics.halstead.difficulty
old: 90.64983164983164
new: 65.40647482014388
path: .spaces[0].metrics.halstead.n1
old: 38.0
new: 33.0
path: .spaces[0].metrics.halstead.n2
old: 297.0
new: 139.0
path: .spaces[0].metrics.halstead.effort
old: 2634690.209355716
new: 646986.7638499448
path: .spaces[0].metrics.halstead.volume
old: 29064.479893720894
new: 9891.784653263194
path: .spaces[0].metrics.halstead.estimated_program_length
old: 2639.074024388684
new: 1155.9978150473964
path: .spaces[0].metrics.halstead.purity_ratio
old: 0.7616375250760993
new: 0.8678662275130604
path: .spaces[0].metrics.halstead.bugs
old: 6.358652873936142
new: 2.4934979138794
path: .spaces[0].metrics.cognitive.sum
old: 161.0
new: 37.0
path: .spaces[0].metrics.cognitive.average
old: 4.6
new: 2.642857142857143
path: .spaces[0].metrics.cyclomatic.sum
old: 151.0
new: 51.0
path: .spaces[0].metrics.cyclomatic.average
old: 3.682926829268293
new: 4.25
path: .spaces[0].metrics.loc.lloc
old: 244.0
new: 106.0
path: .spaces[0].metrics.loc.cloc
old: 128.0
new: 77.0
path: .spaces[0].metrics.loc.blank
old: 133.0
new: 42.0
path: .spaces[0].metrics.loc.ploc
old: 605.0
new: 248.0
path: .spaces[0].metrics.loc.sloc
old: 866.0
new: 367.0
path: .spaces[0].metrics.nom.functions
old: 35.0
new: 10.0
path: .spaces[0].metrics.nom.total
old: 35.0
new: 14.0
path: .spaces[0].metrics.nom.closures
old: 0.0
new: 4.0
path: .spaces[0].metrics.mi.mi_original
old: -26.74675036271151
new: 15.765946796707766
path: .spaces[0].metrics.mi.mi_visual_studio
old: 0.0
new: 9.21985192789928
path: .spaces[0].metrics.mi.mi_sei
old: -70.86335215696016
new: -15.185797941514052
path: .spaces[0].metrics.nargs.average
old: 2.2
new: 0.8571428571428571
path: .spaces[0].metrics.nargs.sum
old: 77.0
new: 12.0
path: .spaces[0].metrics.nexits.average
old: 0.9142857142857144
new: 0.7857142857142857
path: .spaces[0].metrics.nexits.sum
old: 32.0
new: 11.0

### Code

    namespace mozilla {
    namespace widget {

    // The values below have been tested and found to be acceptable on a device
    // with a display refresh rate of 60Hz and touch sampling rate of 100Hz.
    // While their "ideal" values are dependent on the exact rates of each device,
    // the values we've picked below should be somewhat robust across a variation of
    // different rates. They mostly aim to avoid making predictions that are too far
    // away (in terms of distance) from the finger, and to detect pauses in the
    // finger motion without too much delay.

    // Maximum time between two consecutive data points to consider resampling
    // between them.
    // Values between 1x and 5x of the touch sampling interval are reasonable.
    static const double kTouchResampleWindowSize = 40.0;

    // These next two values constrain the sampling timestamp.
    // Our caller will usually adjust frame timestamps to be slightly in the past,
    // for example by 5ms. This means that, during normal operation, we will
    // maximally need to predict by [touch sampling rate] minus 5ms.
    // So we would like kTouchResampleMaxPredictMs to satisfy the following:
    // kTouchResampleMaxPredictMs + [frame time adjust] > [touch sampling rate]
    static const double kTouchResampleMaxPredictMs = 8.0;
    // This one is a protection against very outdated frame timestamps.
    // Values larger than the touch sampling interval and less than 3x of the vsync
    // interval are reasonable.
    static const double kTouchResampleMaxBacksampleMs = 20.0;

    // The maximum age of the most recent data point to consider resampling.
    // Should be between 1x and 3x of the touch sampling interval.
    static const double kTouchResampleOldTouchThresholdMs = 17.0;

    uint64_t TouchResampler::ProcessEvent(MultiTouchInput&& aInput) {
      mCurrentTouches.UpdateFromEvent(aInput);

      uint64_t eventId = mNextEventId;
      mNextEventId++;

      if (aInput.mType == MultiTouchInput::MULTITOUCH_MOVE) {
        // Touch move events are deferred until NotifyFrame.
        mDeferredTouchMoveEvents.push({std::move(aInput), eventId});
      } else {
        // Non-move events are transferred to the outgoing queue unmodified.
        // If there are pending touch move events, flush those out first, so that
        // events are emitted in the right order.
        FlushDeferredTouchMoveEventsUnresampled();
        if (mInResampledState) {
          // Return to a non-resampled state before emitting a non-move event.
          ReturnToNonResampledState();
        }
        EmitEvent(std::move(aInput), eventId);
      }

      return eventId;
    }

    void TouchResampler::NotifyFrame(const TimeStamp& aTimeStamp) {
      TimeStamp lastTouchTime = mCurrentTouches.LatestDataPointTime();
      if (mDeferredTouchMoveEvents.empty() ||
          (lastTouchTime &&
           lastTouchTime < aTimeStamp - TimeDuration::FromMilliseconds(
                                            kTouchResampleOldTouchThresholdMs))) {
        // We haven't received a touch move event in a while, so the fingers must
        // have stopped moving. Flush any old touch move events.
        FlushDeferredTouchMoveEventsUnresampled();

        if (mInResampledState) {
          // Make sure we pause at the resting position that we actually observed,
          // and not at a resampled position.
          ReturnToNonResampledState();
        }

        // Clear touch location history so that we don't resample across a pause.
        mCurrentTouches.ClearDataPoints();
        return;
      }

      MOZ_RELEASE_ASSERT(lastTouchTime);
      TimeStamp lowerBound = lastTouchTime - TimeDuration::FromMilliseconds(
                                                 kTouchResampleMaxBacksampleMs);
      TimeStamp upperBound = lastTouchTime + TimeDuration::FromMilliseconds(
                                                 kTouchResampleMaxPredictMs);
      TimeStamp sampleTime = clamped(aTimeStamp, lowerBound, upperBound);

      if (mLastEmittedEventTime && sampleTime < mLastEmittedEventTime) {
        // Keep emitted timestamps in order.
        sampleTime = mLastEmittedEventTime;
      }

      // We have at least one pending touch move event. Pick one of the events from
      // mDeferredTouchMoveEvents as the base event for the resampling adjustment.
      // We want to produce an event stream whose timestamps are in the right order.
      // As the base event, use the first event that's at or after sampleTime,
      // unless there is no such event, in that case use the last one we have. We
      // will set the timestamp on the resampled event to sampleTime later.
      // Flush out any older events so that everything remains in the right order.
      MultiTouchInput input;
      uint64_t eventId;
      while (true) {
        MOZ_RELEASE_ASSERT(!mDeferredTouchMoveEvents.empty());

        std::tie(input, eventId) = std::move(mDeferredTouchMoveEvents.front());
        mDeferredTouchMoveEvents.pop();
        if (mDeferredTouchMoveEvents.empty() || input.mTimeStamp >= sampleTime) {
          break;
        }

        // Flush this event to the outgoing queue without resampling. What ends up
        // on the screen will still be smooth because we will proceed to emit a
        // resampled event before the paint for this frame starts.
        PrependLeftoverHistoricalData(&input);
        MOZ_RELEASE_ASSERT(input.mTimeStamp < sampleTime);
        EmitEvent(std::move(input), eventId);
      }

      mOriginalOfResampledTouchMove = Nothing();

      // Compute the resampled touch positions.
      nsTArray resampledPositions;
      bool anyPositionDifferentFromOriginal = false;
      for (const auto& touch : input.mTouches) {
        ScreenIntPoint resampledPosition =
            mCurrentTouches.ResampleTouchPositionAtTime(
                touch.mIdentifier, touch.mScreenPoint, sampleTime);
        if (resampledPosition != touch.mScreenPoint) {
          anyPositionDifferentFromOriginal = true;
        }
        resampledPositions.AppendElement(resampledPosition);
      }

      if (anyPositionDifferentFromOriginal) {
        // Store a copy of the original event, so that we can return to an
        // non-resampled position later, if necessary.
        mOriginalOfResampledTouchMove = Some(input);

        // Add the original observed position to the historical data, as well as any
        // leftover historical positions from the previous touch move event, and
        // store the resampled values in the "final" position of the event.
        PrependLeftoverHistoricalData(&input);
        for (size_t i = 0; i < input.mTouches.Length(); i++) {
          auto& touch = input.mTouches[i];
          touch.mHistoricalData.AppendElement(SingleTouchData::HistoricalTouchData{
              input.mTimeStamp,
              touch.mScreenPoint,
              touch.mLocalScreenPoint,
              touch.mRadius,
              touch.mRotationAngle,
              touch.mForce,
          });

          // Remove any historical touch data that's in the future, compared to
          // sampleTime. This data will be included by upcoming touch move
          // events. This only happens if the frame timestamp can be older than the
          // event timestamp, i.e. if interpolation occurs (rather than
          // extrapolation).
          auto futureDataStart = std::find_if(
              touch.mHistoricalData.begin(), touch.mHistoricalData.end(),
              [sampleTime](
                  const SingleTouchData::HistoricalTouchData& aHistoricalData) {
                return aHistoricalData.mTimeStamp > sampleTime;
              });
          if (futureDataStart != touch.mHistoricalData.end()) {
            nsTArray futureData(
                Span(touch.mHistoricalData)
                    .From(futureDataStart.GetIndex()));
            touch.mHistoricalData.TruncateLength(futureDataStart.GetIndex());
            mRemainingTouchData.insert({touch.mIdentifier, std::move(futureData)});
          }

          touch.mScreenPoint = resampledPositions[i];
        }
        input.mTimeStamp = sampleTime;
      }

      EmitEvent(std::move(input), eventId);
      mInResampledState = anyPositionDifferentFromOriginal;
    }

    void TouchResampler::PrependLeftoverHistoricalData(MultiTouchInput* aInput) {
      for (auto& touch : aInput->mTouches) {
        auto leftoverData = mRemainingTouchData.find(touch.mIdentifier);
        if (leftoverData != mRemainingTouchData.end()) {
          nsTArray data =
              std::move(leftoverData->second);
          mRemainingTouchData.erase(leftoverData);
          touch.mHistoricalData.InsertElementsAt(0, data);
        }

        if (TimeStamp cutoffTime = mLastEmittedEventTime) {
          // If we received historical touch data that was further in the past than
          // the last resampled event, discard that data so that the touch data
          // points are emitted in order.
          touch.mHistoricalData.RemoveElementsBy(
              [cutoffTime](const SingleTouchData::HistoricalTouchData& aTouchData) {
                return aTouchData.mTimeStamp < cutoffTime;
              });
        }
      }
      mRemainingTouchData.clear();
    }

    void TouchResampler::FlushDeferredTouchMoveEventsUnresampled() {
      while (!mDeferredTouchMoveEvents.empty()) {
        MultiTouchInput input;
        uint64_t eventId;
        std::tie(input, eventId) = std::move(mDeferredTouchMoveEvents.front());
        mDeferredTouchMoveEvents.pop();
        PrependLeftoverHistoricalData(&input);
        EmitEvent(std::move(input), eventId);
        mInResampledState = false;
        mOriginalOfResampledTouchMove = Nothing();
      }
    }

    void TouchResampler::ReturnToNonResampledState() {
      MOZ_RELEASE_ASSERT(mInResampledState);
      MOZ_RELEASE_ASSERT(mDeferredTouchMoveEvents.empty(),
                         "Don't call this if there is a deferred touch move event. "
                         "We can return to the non-resampled state by sending that "
                         "event, rather than a copy of a previous event.");

      // The last outgoing event was a resampled touch move event.
      // Return to the non-resampled state, by sending a touch move event to
      // "overwrite" any resampled positions with the original observed positions.
      MultiTouchInput input = std::move(*mOriginalOfResampledTouchMove);
      mOriginalOfResampledTouchMove = Nothing();

      // For the event's timestamp, we want to backdate the correction as far as we
      // can, while still preserving timestamp ordering. But we also don't want to
      // backdate it to be older than it was originally.
      if (mLastEmittedEventTime > input.mTimeStamp) {
        input.mTimeStamp = mLastEmittedEventTime;
      }

      // Assemble the correct historical touch data for this event.
      // We don't want to include data points that we've already sent out with the
      // resampled event. And from the leftover data points, we only want those that
      // don't duplicate the final time + position of this event.
      for (auto& touch : input.mTouches) {
        touch.mHistoricalData.Clear();
      }
      PrependLeftoverHistoricalData(&input);
      for (auto& touch : input.mTouches) {
        touch.mHistoricalData.RemoveElementsBy([&](const auto& histData) {
          return histData.mTimeStamp >= input.mTimeStamp;
        });
      }

      EmitExtraEvent(std::move(input));
      mInResampledState = false;
    }

    void TouchResampler::TouchInfo::Update(const SingleTouchData& aTouch,
                                           const TimeStamp& aEventTime) {
      for (const auto& historicalData : aTouch.mHistoricalData) {
        mBaseDataPoint = mLatestDataPoint;
        mLatestDataPoint =
            Some(DataPoint{historicalData.mTimeStamp, historicalData.mScreenPoint});
      }
      mBaseDataPoint = mLatestDataPoint;
      mLatestDataPoint = Some(DataPoint{aEventTime, aTouch.mScreenPoint});
    }

    ScreenIntPoint TouchResampler::TouchInfo::ResampleAtTime(
        const ScreenIntPoint& aLastObservedPosition, const TimeStamp& aTimeStamp) {
      TimeStamp cutoff =
          aTimeStamp - TimeDuration::FromMilliseconds(kTouchResampleWindowSize);
      if (!mBaseDataPoint || !mLatestDataPoint ||
          !(mBaseDataPoint->mTimeStamp < mLatestDataPoint->mTimeStamp) ||
          mBaseDataPoint->mTimeStamp < cutoff) {
        return aLastObservedPosition;
      }

      // For the actual resampling, connect the last two data points with a line and
      // sample along that line.
      TimeStamp t1 = mBaseDataPoint->mTimeStamp;
      TimeStamp t2 = mLatestDataPoint->mTimeStamp;
      double t = (aTimeStamp - t1) / (t2 - t1);

      double x1 = mBaseDataPoint->mPosition.x;
      double x2 = mLatestDataPoint->mPosition.x;
      double y1 = mBaseDataPoint->mPosition.y;
      double y2 = mLatestDataPoint->mPosition.y;

      int32_t resampledX = round(x1 + t * (x2 - x1));
      int32_t resampledY = round(y1 + t * (y2 - y1));
      return ScreenIntPoint(resampledX, resampledY);
    }

    void TouchResampler::CurrentTouches::UpdateFromEvent(
        const MultiTouchInput& aInput) {
      switch (aInput.mType) {
        case MultiTouchInput::MULTITOUCH_START: {
          // A new touch has been added; make sure mTouches reflects the current
          // touches in the event.
          nsTArray newTouches;
          for (const auto& touch : aInput.mTouches) {
            const auto touchInfo = TouchByIdentifier(touch.mIdentifier);
            if (touchInfo != mTouches.end()) {
              // This is one of the existing touches.
              newTouches.AppendElement(std::move(*touchInfo));
              mTouches.RemoveElementAt(touchInfo);
            } else {
              // This is the new touch.
              newTouches.AppendElement(TouchInfo{
                  touch.mIdentifier, Nothing(),
                  Some(DataPoint{aInput.mTimeStamp, touch.mScreenPoint})});
            }
          }
          MOZ_ASSERT(mTouches.IsEmpty(), "Missing touch end before touch start?");
          mTouches = std::move(newTouches);
          break;
        }

        case MultiTouchInput::MULTITOUCH_MOVE: {
          // The touches have moved.
          // Add position information to the history data points.
          for (const auto& touch : aInput.mTouches) {
            const auto touchInfo = TouchByIdentifier(touch.mIdentifier);
            MOZ_ASSERT(touchInfo != mTouches.end());
            if (touchInfo != mTouches.end()) {
              touchInfo->Update(touch, aInput.mTimeStamp);
            }
          }
          mLatestDataPointTime = aInput.mTimeStamp;
          break;
        }

        case MultiTouchInput::MULTITOUCH_END: {
          // A touch has been removed.
          MOZ_RELEASE_ASSERT(aInput.mTouches.Length() == 1);
          const auto touchInfo = TouchByIdentifier(aInput.mTouches[0].mIdentifier);
          MOZ_ASSERT(touchInfo != mTouches.end());
          if (touchInfo != mTouches.end()) {
            mTouches.RemoveElementAt(touchInfo);
          }
          break;
        }

        case MultiTouchInput::MULTITOUCH_CANCEL:
          // All touches are canceled.
          mTouches.Clear();
          break;
      }
    }

    nsTArray::iterator
    TouchResampler::CurrentTouches::TouchByIdentifier(int32_t aIdentifier) {
      return std::find_if(mTouches.begin(), mTouches.end(),
                          [aIdentifier](const TouchInfo& info) {
                            return info.mIdentifier == aIdentifier;
                          });
    }

    ScreenIntPoint TouchResampler::CurrentTouches::ResampleTouchPositionAtTime(
        int32_t aIdentifier, const ScreenIntPoint& aLastObservedPosition,
        const TimeStamp& aTimeStamp) {
      const auto touchInfo = TouchByIdentifier(aIdentifier);
      MOZ_ASSERT(touchInfo != mTouches.end());
      if (touchInfo != mTouches.end()) {
        return touchInfo->ResampleAtTime(aLastObservedPosition, aTimeStamp);
      }
      return aLastObservedPosition;
    }

    }  // namespace widget
    }  // namespace mozilla

## Minimal test - lines (278, 302)

path: .spaces[0].spaces[0].spaces[6].metrics.nargs.sum
old: 1.0
new: 2.0
path: .spaces[0].spaces[0].spaces[6].metrics.nargs.average
old: 1.0
new: 2.0
path: .spaces[0].spaces[0].spaces[6].metrics.cognitive.average
old: 6.0
new: 2.0
path: .spaces[0].spaces[0].spaces[6].metrics.cognitive.sum
old: 6.0
new: 2.0
path: .spaces[0].spaces[0].spaces[6].metrics.loc.ploc
old: 13.0
new: 20.0
path: .spaces[0].spaces[0].spaces[6].metrics.loc.cloc
old: 1.0
new: 2.0
path: .spaces[0].spaces[0].spaces[6].metrics.loc.blank
old: 0.0
new: 3.0
path: .spaces[0].spaces[0].spaces[6].metrics.loc.lloc
old: 5.0
new: 3.0
path: .spaces[0].spaces[0].spaces[6].metrics.loc.sloc
old: 14.0
new: 25.0
path: .spaces[0].spaces[0].spaces[6].metrics.halstead.time
old: 349.1338056342785
new: 1147.9313331764463
path: .spaces[0].spaces[0].spaces[6].metrics.halstead.n1
old: 17.0
new: 19.0
path: .spaces[0].spaces[0].spaces[6].metrics.halstead.bugs
old: 0.11351546092344686
new: 0.25099843995294474
path: .spaces[0].spaces[0].spaces[6].metrics.halstead.purity_ratio
old: 1.9513692208539513
new: 1.2888494781941613
path: .spaces[0].spaces[0].spaces[6].metrics.halstead.effort
old: 6284.408501417012
new: 20662.763997176033
path: .spaces[0].spaces[0].spaces[6].metrics.halstead.estimated_program_length
old: 179.5259683185635
new: 190.74972277273588
path: .spaces[0].spaces[0].spaces[6].metrics.halstead.volume
old: 492.8947844248637
new: 803.0871836959105
path: .spaces[0].spaces[0].spaces[6].metrics.halstead.N1
old: 56.0
new: 83.0
path: .spaces[0].spaces[0].spaces[6].metrics.halstead.level
old: 0.0784313725490196
new: 0.0388663967611336
path: .spaces[0].spaces[0].spaces[6].metrics.halstead.vocabulary
old: 41.0
new: 43.0
path: .spaces[0].spaces[0].spaces[6].metrics.halstead.N2
old: 36.0
new: 65.0
path: .spaces[0].spaces[0].spaces[6].metrics.halstead.length
old: 92.0
new: 148.0
path: .spaces[0].spaces[0].spaces[6].metrics.halstead.difficulty
old: 12.75
new: 25.729166666666668
path: .spaces[0].spaces[0].spaces[6].metrics.cyclomatic.sum
old: 4.0
new: 5.0
path: .spaces[0].spaces[0].spaces[6].metrics.cyclomatic.average
old: 4.0
new: 5.0
path: .spaces[0].spaces[0].spaces[6].metrics.mi.mi_original
old: 95.08573345253748
new: 82.92420257839832
path: .spaces[0].spaces[0].spaces[6].metrics.mi.mi_sei
old: 82.00167479725492
new: 65.65710029021675
path: .spaces[0].spaces[0].spaces[6].metrics.mi.mi_visual_studio
old: 55.6056920775073
new: 48.4936857183616
path: .spaces[0].spaces[0].spaces[6].metrics.nexits.sum
old: 0.0
new: 2.0
path: .spaces[0].spaces[0].spaces[6].metrics.nexits.average
old: 0.0
new: 2.0

### Code

    ScreenIntPoint TouchResampler::TouchInfo::ResampleAtTime(
        const ScreenIntPoint& aLastObservedPosition, const TimeStamp& aTimeStamp) {
      TimeStamp cutoff =
          aTimeStamp - TimeDuration::FromMilliseconds(kTouchResampleWindowSize);
      if (!mBaseDataPoint || !mLatestDataPoint ||
          !(mBaseDataPoint->mTimeStamp < mLatestDataPoint->mTimeStamp) ||
          mBaseDataPoint->mTimeStamp < cutoff) {
        return aLastObservedPosition;
      }

      // For the actual resampling, connect the last two data points with a line and
      // sample along that line.
      TimeStamp t1 = mBaseDataPoint->mTimeStamp;
      TimeStamp t2 = mLatestDataPoint->mTimeStamp;
      double t = (aTimeStamp - t1) / (t2 - t1);

      double x1 = mBaseDataPoint->mPosition.x;
      double x2 = mLatestDataPoint->mPosition.x;
      double y1 = mBaseDataPoint->mPosition.y;
      double y2 = mLatestDataPoint->mPosition.y;

      int32_t resampledX = round(x1 + t * (x2 - x1));
      int32_t resampledY = round(y1 + t * (y2 - y1));
      return ScreenIntPoint(resampledX, resampledY);
    }

## Minimal test - lines (47, 69)

path: .spaces[0].spaces[0].spaces[0].metrics.mi.mi_original
old: 60.7161384665593
new: 89.1492616824421
path: .spaces[0].spaces[0].spaces[0].metrics.mi.mi_visual_studio
old: 35.50651372313409
new: 52.134071159322865
path: .spaces[0].spaces[0].spaces[0].metrics.mi.mi_sei
old: 37.730768824375474
new: 86.37779630898021
path: .spaces[0].spaces[0].spaces[0].metrics.nexits.sum
old: 9.0
new: 1.0
path: .spaces[0].spaces[0].spaces[0].metrics.nexits.average
old: 1.5
new: 1.0
path: .spaces[0].spaces[0].spaces[0].metrics.cognitive.average
old: 1.3333333333333333
new: 4.0
path: .spaces[0].spaces[0].spaces[0].metrics.cognitive.sum
old: 8.0
new: 4.0
path: .spaces[0].spaces[0].spaces[0].metrics.nom.functions
old: 6.0
new: 1.0
path: .spaces[0].spaces[0].spaces[0].metrics.nom.total
old: 6.0
new: 1.0
path: .spaces[0].spaces[0].spaces[0].metrics.halstead.N2
old: 107.0
new: 25.0
path: .spaces[0].spaces[0].spaces[0].metrics.halstead.purity_ratio
old: 0.7956315720598552
new: 1.7507905807284547
path: .spaces[0].spaces[0].spaces[0].metrics.halstead.N1
old: 163.0
new: 42.0
path: .spaces[0].spaces[0].spaces[0].metrics.halstead.level
old: 0.025233644859813085
new: 0.09142857142857144
path: .spaces[0].spaces[0].spaces[0].metrics.halstead.time
old: 3301.894484052818
new: 199.7683758107982
path: .spaces[0].spaces[0].spaces[0].metrics.halstead.vocabulary
old: 47.0
new: 30.0
path: .spaces[0].spaces[0].spaces[0].metrics.halstead.bugs
old: 0.5076556326042772
new: 0.07823688927938995
path: .spaces[0].spaces[0].spaces[0].metrics.halstead.n1
old: 20.0
new: 14.0
path: .spaces[0].spaces[0].spaces[0].metrics.halstead.estimated_program_length
old: 214.8205244561609
new: 117.30296890880646
path: .spaces[0].spaces[0].spaces[0].metrics.halstead.effort
old: 59434.10071295072
new: 3595.830764594368
path: .spaces[0].spaces[0].spaces[0].metrics.halstead.length
old: 270.0
new: 67.0
path: .spaces[0].spaces[0].spaces[0].metrics.halstead.n2
old: 27.0
new: 16.0
path: .spaces[0].spaces[0].spaces[0].metrics.halstead.difficulty
old: 39.629629629629626
new: 10.9375
path: .spaces[0].spaces[0].spaces[0].metrics.halstead.volume
old: 1499.738989952962
new: 328.76166990577076
path: .spaces[0].spaces[0].spaces[0].metrics.loc.blank
old: 12.0
new: 3.0
path: .spaces[0].spaces[0].spaces[0].metrics.loc.cloc
old: 7.0
new: 5.0
path: .spaces[0].spaces[0].spaces[0].metrics.loc.ploc
old: 48.0
new: 15.0
path: .spaces[0].spaces[0].spaces[0].metrics.loc.sloc
old: 67.0
new: 23.0
path: .spaces[0].spaces[0].spaces[0].metrics.loc.lloc
old: 14.0
new: 9.0
path: .spaces[0].spaces[0].spaces[0].metrics.nargs.average
old: 1.8333333333333333
new: 1.0
path: .spaces[0].spaces[0].spaces[0].metrics.nargs.sum
old: 11.0
new: 1.0
path: .spaces[0].spaces[0].spaces[0].metrics.cyclomatic.average
old: 1.8
new: 4.0
path: .spaces[0].spaces[0].spaces[0].metrics.cyclomatic.sum
old: 18.0
new: 4.0

### Code

    uint64_t TouchResampler::ProcessEvent(MultiTouchInput&& aInput) {
      mCurrentTouches.UpdateFromEvent(aInput);

      uint64_t eventId = mNextEventId;
      mNextEventId++;

      if (aInput.mType == MultiTouchInput::MULTITOUCH_MOVE) {
        // Touch move events are deferred until NotifyFrame.
        mDeferredTouchMoveEvents.push({std::move(aInput), eventId});
      } else {
        // Non-move events are transferred to the outgoing queue unmodified.
        // If there are pending touch move events, flush those out first, so that
        // events are emitted in the right order.
        FlushDeferredTouchMoveEventsUnresampled();
        if (mInResampledState) {
          // Return to a non-resampled state before emitting a non-move event.
          ReturnToNonResampledState();
        }
        EmitEvent(std::move(aInput), eventId);
      }

      return eventId;
    }

## Minimal test - lines (267, 276)

path: .spaces[0].spaces[0].spaces[5].metrics.mi.mi_visual_studio
old: 60.521643511003816
new: 61.04551267914326
path: .spaces[0].spaces[0].spaces[5].metrics.mi.mi_original
old: 103.49201040381654
new: 104.38782668133496
path: .spaces[0].spaces[0].spaces[5].metrics.mi.mi_sei
old: 73.91201776741578
new: 75.10258760913477
path: .spaces[0].spaces[0].spaces[5].metrics.halstead.n1
old: 15.0
new: 11.0
path: .spaces[0].spaces[0].spaces[5].metrics.halstead.bugs
old: 0.0936691449056419
new: 0.06488348759510171
path: .spaces[0].spaces[0].spaces[5].metrics.halstead.n2
old: 20.0
new: 13.0
path: .spaces[0].spaces[0].spaces[5].metrics.halstead.N2
old: 31.0
new: 25.0
path: .spaces[0].spaces[0].spaces[5].metrics.halstead.effort
old: 4710.6052906868335
new: 2715.708558119454
path: .spaces[0].spaces[0].spaces[5].metrics.halstead.estimated_program_length
old: 145.04192083187502
new: 86.15946414084446
path: .spaces[0].spaces[0].spaces[5].metrics.halstead.length
old: 79.0
new: 56.0
path: .spaces[0].spaces[0].spaces[5].metrics.halstead.time
old: 261.7002939270463
new: 150.872697673303
path: .spaces[0].spaces[0].spaces[5].metrics.halstead.level
old: 0.08602150537634409
new: 0.09454545454545454
path: .spaces[0].spaces[0].spaces[5].metrics.halstead.purity_ratio
old: 1.8359736814161391
new: 1.538561859657937
path: .spaces[0].spaces[0].spaces[5].metrics.halstead.N1
old: 48.0
new: 31.0
path: .spaces[0].spaces[0].spaces[5].metrics.halstead.vocabulary
old: 35.0
new: 24.0
path: .spaces[0].spaces[0].spaces[5].metrics.halstead.volume
old: 405.2133583386524
new: 256.75790004038475
path: .spaces[0].spaces[0].spaces[5].metrics.halstead.difficulty
old: 11.625
new: 10.576923076923077
path: .spaces[0].spaces[0].spaces[5].metrics.cyclomatic.average
old: 3.0
new: 2.0
path: .spaces[0].spaces[0].spaces[5].metrics.cyclomatic.sum
old: 3.0
new: 2.0
path: .spaces[0].spaces[0].spaces[5].metrics.nargs.average
old: 1.0
new: 2.0
path: .spaces[0].spaces[0].spaces[5].metrics.nargs.sum
old: 1.0
new: 2.0
path: .spaces[0].spaces[0].spaces[5].metrics.cognitive.average
old: 3.0
new: 0.0
path: .spaces[0].spaces[0].spaces[5].metrics.cognitive.sum
old: 3.0
new: 0.0
path: .spaces[0].spaces[0].spaces[5].metrics.loc.sloc
old: 9.0
new: 10.0
path: .spaces[0].spaces[0].spaces[5].metrics.loc.ploc
old: 9.0
new: 10.0

### Code

    void TouchResampler::TouchInfo::Update(const SingleTouchData& aTouch,
                                           const TimeStamp& aEventTime) {
      for (const auto& historicalData : aTouch.mHistoricalData) {
        mBaseDataPoint = mLatestDataPoint;
        mLatestDataPoint =
            Some(DataPoint{historicalData.mTimeStamp, historicalData.mScreenPoint});
      }
      mBaseDataPoint = mLatestDataPoint;
      mLatestDataPoint = Some(DataPoint{aEventTime, aTouch.mScreenPoint});
    }