diff options
Diffstat (limited to 'src/qdoc/catch_generators/src/catch_generators/generators/combinators/oneof_generator.h')
-rw-r--r-- | src/qdoc/catch_generators/src/catch_generators/generators/combinators/oneof_generator.h | 185 |
1 files changed, 185 insertions, 0 deletions
diff --git a/src/qdoc/catch_generators/src/catch_generators/generators/combinators/oneof_generator.h b/src/qdoc/catch_generators/src/catch_generators/generators/combinators/oneof_generator.h new file mode 100644 index 000000000..5de9dcb6c --- /dev/null +++ b/src/qdoc/catch_generators/src/catch_generators/generators/combinators/oneof_generator.h @@ -0,0 +1,185 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "../../namespaces.h" +#include "../../utilities/statistics/percentages.h" +#include "../../utilities/semantics/generator_handler.h" + +#include <catch/catch.hpp> + +#include <vector> +#include <random> +#include <algorithm> +#include <numeric> + +namespace QDOC_CATCH_GENERATORS_ROOT_NAMESPACE { + namespace QDOC_CATCH_GENERATORS_PRIVATE_NAMESPACE { + + template<typename T> + class OneOfGenerator : public Catch::Generators::IGenerator<T> { + public: + OneOfGenerator( + std::vector<Catch::Generators::GeneratorWrapper<T>>&& generators, + const std::vector<double>& weights + ) : generators{std::move(generators)}, + random_engine{std::random_device{}()}, + choice_distribution{weights.cbegin(), weights.cend()} + { + assert(weights.size() == this->generators.size()); + assert(std::reduce(weights.cbegin(), weights.cend()) == Approx(100.0)); + + std::transform( + this->generators.begin(), this->generators.end(), this->generators.begin(), + [](auto& generator){ return QDOC_CATCH_GENERATORS_UTILITIES_ABSOLUTE_NAMESPACE::handler(std::move(generator)); } + ); + + static_cast<void>(next()); + } + + T const& get() const override { return current_value; } + + bool next() override { + std::size_t generator_index{choice_distribution(random_engine)}; + + if (!generators[generator_index].next()) return false; + current_value = generators[generator_index].get(); + + return true; + } + + private: + std::vector<Catch::Generators::GeneratorWrapper<T>> generators; + + std::mt19937 random_engine; + std::discrete_distribution<std::size_t> choice_distribution; + + T current_value; + }; + + } // end QDOC_CATCH_GENERATORS_PRIVATE_NAMESPACE + + /*! + * Returns a generator whose set of elements is the union of the + * set of elements of the generators in \a generators. + * + * Each time the generator produces a value, a generator from \a + * generators is randomly chosen to produce the value. + * + * The distribution for the choice is given by \a weights. + * The \e {ith} element in \a weights represent the percentage + * probability of the \e {ith} element of \a generators to be + * chosen. + * + * It follows that the size of \a weights must be the same as the + * size of \a generators. + * + * Furthermore, the sum of elements in \a weights should be a + * hundred. + * + * The generator produces values until a generator that is chosen + * to produce a value is unable to do so. + * The first such generator to do so will stop the generation + * independently of the availability of the other generators. + * + * Similarly, values will be produced as long as the chosen + * generator can produce a value, independently of the other + * generators being exhausted already. + */ + template<typename T> + inline Catch::Generators::GeneratorWrapper<T> oneof( + std::vector<Catch::Generators::GeneratorWrapper<T>>&& generators, + const std::vector<double>& weights + ) { + return Catch::Generators::GeneratorWrapper<T>(std::unique_ptr<Catch::Generators::IGenerator<T>>(new QDOC_CATCH_GENERATORS_PRIVATE_NAMESPACE::OneOfGenerator(std::move(generators), weights))); + } + + + /*! + * Returns a generator whose set of elements is the union of the + * set of elements of the generators in \a generators and in which + * the distribution of the generated elements is uniform over \a + * generators. + * + * Each time the generator produces a value, a generator from \a + * generators is randomly chosen to produce the value. + * + * Each generator from \a generators has the same chance of being + * chosen. + * + * Do note that the distribution over the set of values is not + * necessarily uniform. + * + * The generator produces values until a generator that is chosen + * to produce a value is unable to do so. + * The first such generator to do so will stop the generation + * independently of the availability of the other generators. + * + * Similarly, values will be produced as long as the chosen + * generator can produce a value, independently of the other + * generators being exhausted already. + */ + template<typename T> + inline Catch::Generators::GeneratorWrapper<T> uniform_oneof( + std::vector<Catch::Generators::GeneratorWrapper<T>>&& generators + ) { + std::vector<double> weights( + generators.size(), + QDOC_CATCH_GENERATORS_UTILITIES_ABSOLUTE_NAMESPACE::uniform_probability(generators.size()) + ); + return oneof(std::move(generators), std::move(weights)); + } + + /*! + * Returns a generator whose set of elements is the union of the + * set of elements of the generators in \a generators and in which + * the distribution of the generated elements is uniform over the + * elements of \a generators. + * + * The generators in \a generator should have a uniform + * distribution and be finite. + * If the set of elements that the generators in \a generator is + * not disjoint, the distribution will be skewed towards repeated + * elements. + * + * Each time the generator produces a value, a generator from \a + * generators is randomly chosen to produce the value. + * + * Each generator from \a generators has a probability of being + * chosen based on the proportion of the cardinality of the subset + * it produces. + * + * The \e {ith} element of \a amounts should contain the + * cardinality of the set produced by the \e {ith} generator in \a + * generators. + * + * The generator produces values until a generator that is chosen + * to produce a value is unable to do so. + * The first such generator to do so will stop the generation + * independently of the availability of the other generators. + * + * Similarly, values will be produced as long as the chosen + * generator can produce a value, independently of the other + * generators being exhausted already. + */ + template<typename T> + inline Catch::Generators::GeneratorWrapper<T> uniformly_valued_oneof( + std::vector<Catch::Generators::GeneratorWrapper<T>>&& generators, + const std::vector<std::size_t>& amounts + ) { + std::size_t total_amount{std::accumulate(amounts.cbegin(), amounts.cend(), std::size_t{0})}; + + std::vector<double> weights; + weights.reserve(amounts.size()); + + std::transform( + amounts.cbegin(), amounts.cend(), + std::back_inserter(weights), + [total_amount](auto element){ return QDOC_CATCH_GENERATORS_UTILITIES_ABSOLUTE_NAMESPACE::percent_of(static_cast<double>(element), static_cast<double>(total_amount)); } + ); + + return oneof(std::move(generators), std::move(weights)); + } + +} // end QDOC_CATCH_GENERATORS_ROOT_NAMESPACE |