void OutputMetrics::calculateMatchCount()

in fbpcs/emp_games/lift/calculator/OutputMetrics.hpp [522:592]


void OutputMetrics<MY_ROLE>::calculateMatchCount(
    const OutputMetrics::GroupType& groupType,
    const std::vector<emp::Bit>& populationBits,
    const std::vector<std::vector<emp::Integer>>& purchaseValueArrays) {
  XLOG(INFO) << "Calculate " << getGroupTypeStr(groupType) << " MatchCount";
  // a valid test/control match is when a person with an opportunity who made
  // ANY nonzero conversion. Therefore we can just check first if an opportunity
  // is valid, then bitwise AND this with the bitwise OR over all purchases (to
  // check for purchases). This gets us a binary indication if a user is
  // matched with any opportunity

  XLOG(INFO) << "Share opportunity timestamps";
  const std::vector<emp::Integer> opportunityTimestamps =
      privatelyShareIntsFromPublisher<MY_ROLE>(
          inputData_.getOpportunityTimestamps(), n_, QUICK_BITS);
  XLOG(INFO) << "Share purchase timestamps";
  const std::vector<std::vector<emp::Integer>> purchaseTimestampArrays =
      privatelyShareIntArraysFromPartner<MY_ROLE>(
          inputData_.getPurchaseTimestampArrays(),
          n_, /* numVals */
          numConversionsPerUser_ /* arraySize */,
          QUICK_BITS /* bitLen */);
  auto matchArrays = private_measurement::functional::zip_apply(
      [](emp::Bit isUser,
         emp::Integer opportunityTimestamp,
         std::vector<emp::Integer> purchaseTimestampArray) -> emp::Bit {
        const emp::Integer zero = emp::Integer{
            static_cast<int>(opportunityTimestamp.size()), 0, emp::PUBLIC};
        emp::Bit validOpportunity =
            (isUser &
             (opportunityTimestamp > zero)); // check if opportunity is valid
        emp::Bit isUserMatched = emp::Bit{0, emp::PUBLIC};
        for (const auto& purchaseTS : purchaseTimestampArray) {
          // check for the existence of a valid purchase
          isUserMatched = isUserMatched | (purchaseTS > zero);
        }
        return isUserMatched & validOpportunity;
      },
      populationBits.begin(),
      populationBits.end(),
      opportunityTimestamps.begin(),
      purchaseTimestampArrays.begin());

  if (groupType == GroupType::TEST) {
    metrics_.testMatchCount = sum(matchArrays);
  } else {
    metrics_.controlMatchCount = sum(matchArrays);
  }

  // And compute for breakdowns + cohorts
  // TODO: These could be abstracted into a common function
  for (size_t i = 0; i < numPublisherBreakdowns_; ++i) {
    auto groupBits = private_measurement::secret_sharing::multiplyBitmask(
        matchArrays, publisherBitmasks_.at(i));
    if (groupType == GroupType::TEST) {
      publisherBreakdowns_[i].testMatchCount = sum(groupBits);
    } else {
      publisherBreakdowns_[i].controlMatchCount = sum(groupBits);
    }
  }

  for (size_t i = 0; i < numPartnerCohorts_; ++i) {
    auto groupBits = private_measurement::secret_sharing::multiplyBitmask(
        matchArrays, partnerBitmasks_.at(i));
    if (groupType == GroupType::TEST) {
      cohortMetrics_[i].testMatchCount = sum(groupBits);
    } else {
      cohortMetrics_[i].controlMatchCount = sum(groupBits);
    }
  }
}