chromium/chrome/browser/metrics/perf/random_selector.h

// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CHROME_BROWSER_METRICS_PERF_RANDOM_SELECTOR_H_
#define CHROME_BROWSER_METRICS_PERF_RANDOM_SELECTOR_H_

#include <stddef.h>

#include <string>
#include <vector>

// RandomSelector can be used to pick vectors of strings according to certain
// probabilities. The probabilities are set using SetOdds(). A randomly picked
// vector can be obtained by calling Select().
//
// Sample usage:
//
// RandomSelector random_selector;
// std::vector<RandomSelector::WeightAndValue> odds {
//   {50, "a"},
//   {40, "b"},
//   {10, "c"}
// };
// random_selector.SetOdds(odds);
//
// std::vector<std::string>& selection = random_selector.Select();
//
// The above line should return "a" with a probability of 50%,
// "b" with a probability of 40%, and "c" with a probability of 10%:
class RandomSelector {
 public:
  struct WeightAndValue {
    WeightAndValue(double weight, const std::string& value)
      : weight(weight), value(value) {
    }

    bool operator==(const WeightAndValue& other) const {
      return weight == other.weight && value == other.value;
    }

    // Probability weight for selecting this value.
    double weight;
    // Value to be returned by Select(), if selected.
    std::string value;
  };

  RandomSelector();

  RandomSelector(const RandomSelector&) = delete;
  RandomSelector& operator=(const RandomSelector&) = delete;

  virtual ~RandomSelector();

  // Set the probabilities for various strings. Returns false and doesn't
  // modify the values if odds contains any invalid weights (<=0.0) or if
  // odds is empty.
  bool SetOdds(const std::vector<WeightAndValue>& odds);

  // Randomly select one of the values from the set.
  const std::string& Select();

  // Returns the number of string entries.
  size_t num_values() const {
    return odds_.size();
  }

  const std::vector<WeightAndValue>& odds() const { return odds_; }

  // Sum of the |weight| fields in the vector. Returns -1.0 if odds contains any
  // weight <= 0.0.
  static double SumWeights(const std::vector<WeightAndValue>& odds);

 private:
  // Get a floating point number between 0.0 and |max|.
  virtual double RandDoubleUpTo(double max);

  // Get a string corresponding to |random| that is in the odds vector.
  // |random| must be a number between zero and the sum of the probability
  // weights.
  const std::string& GetValueFor(double random);

  // A dictionary representing the strings to choose from and associated odds.
  std::vector<WeightAndValue> odds_;

  // Sum of the probability weights.
  double sum_of_weights_;
};

::std::ostream& operator<<(
    ::std::ostream& os, const RandomSelector::WeightAndValue& value);

#endif  // CHROME_BROWSER_METRICS_PERF_RANDOM_SELECTOR_H_