Program Listing for File haploid_genome_cleaner.hpp

Return to documentation for file (fwdpp/internal/haploid_genome_cleaner.hpp)

#ifndef __FWDPP_INTERNAL_HAPLOID_GENOME_CLEANER_HPP__
#define __FWDPP_INTERNAL_HAPLOID_GENOME_CLEANER_HPP__

#include <vector>
#include <algorithm>
#include <limits>
#include <utility>
#include <type_traits>
#include <fwdpp/forward_types.hpp>
#include <fwdpp/fwd_functional.hpp>

namespace fwdpp
{
    namespace fwdpp_internal
    {

        // First, we have a set of helper functions:

        template <typename GenomeContainerType_iterator>
        inline GenomeContainerType_iterator
        next_extant_haploid_genome(GenomeContainerType_iterator beg,
                                   GenomeContainerType_iterator end) noexcept
        {
            return std::find_if(
                beg, end,
                [](const typename GenomeContainerType_iterator::value_type &g) noexcept {
                    return g.n;
                });
        }

        struct find_fixation
        {
            template <typename mut_index_cont, typename mcounts_t>
            inline typename mut_index_cont::const_iterator
            operator()(const mut_index_cont &mc, const mcounts_t &mcounts,
                       const uint_t twoN) const noexcept
            {
                return std::find_if(mc.cbegin(), mc.cend(),
                                    [&mcounts, &twoN](const std::size_t &i) noexcept {
                                        return mcounts[i] == twoN;
                                    });
            }

            template <typename mut_index_cont, typename MutationContainerType,
                      typename mcounts_t, typename mutation_removal_policy>
            inline typename mut_index_cont::const_iterator
            operator()(const mut_index_cont &mc, const MutationContainerType &mutations,
                       const mcounts_t &mcounts, const uint_t twoN,
                       mutation_removal_policy &&mp) const noexcept
            {
                return std::find_if(
                    mc.cbegin(), mc.cend(),
                    [&mutations, &mcounts, &twoN, &mp](const std::size_t &i) noexcept {
                        return mcounts[i] == twoN && mp(mutations[i]);
                    });
            }
        };

        struct haploid_genome_cleaner_erase_remove_idiom_wrapper
        {
            template <typename mut_index_cont, typename mcounts_t>
            inline void
            operator()(mut_index_cont &mc, const mcounts_t &mcounts,
                       const typename mut_index_cont::value_type &first_fixation,
                       const uint_t twoN) const noexcept
            {
                /*
                  The first call to std::find relies on a de-referencing of the
                  return value of
                  find_fixation, passed to this function as first_fixation.
                  Because mc is sorted according to mutation position,
                  first_fixation is the fixation with the smallest position.
                  Using std::find like this avoids some calls to the lambda
                  expression, which experiences cache-misses because mcounts is
                  NOT sorted according to mutation position.
                */
                mc.erase(std::remove_if(
                             std::find(mc.begin(), mc.end(), first_fixation), mc.end(),
                             [&mcounts, &twoN](
                                 const typename mut_index_cont::value_type &i) noexcept {
                                 return mcounts[i] == twoN;
                             }),
                         mc.end());
            }

            template <typename mut_index_cont, typename MutationContainerType,
                      typename mcounts_t, typename mutation_removal_policy>
            inline void
            operator()(mut_index_cont &mc, const MutationContainerType &mutations,
                       const mcounts_t &mcounts,
                       const typename mut_index_cont::value_type &first_fixation,
                       const uint_t twoN, mutation_removal_policy &&mp) const noexcept
            {
                /*
                  The first call to std::find relies on a de-referencing of the
                  return value of
                  find_fixation, passed to this function as first_fixation.
                  Because mc is sorted according to mutation position,
                  first_fixation is the fixation with the smallest position.
                  Using std::find like this avoids some calls to the lambda
                  expression, which experiences cache-misses because mcounts is
                  NOT sorted according to mutation position.
                */
                mc.erase(std::remove_if(
                             std::find(mc.begin(), mc.end(), first_fixation), mc.end(),
                             [&mcounts, &mutations, &twoN, &mp](
                                 const typename mut_index_cont::value_type &i) noexcept {
                                 return mcounts[i] == twoN && mp(mutations[i]);
                             }),
                         mc.end());
            }

            template <typename mut_index_cont, typename mcounts_t>
            inline void
            operator()(mut_index_cont &mc, const mcounts_t &mcounts,
                       const uint_t twoN) const noexcept
            {
                /*
                  \note Added in 0.5.0 to address Issue #41
                */
                mc.erase(std::remove_if(
                             mc.begin(), mc.end(),
                             [&mcounts, &twoN](
                                 const typename mut_index_cont::value_type &i) noexcept {
                                 return mcounts[i] == twoN;
                             }),
                         mc.end());
            }

            template <typename mut_index_cont, typename MutationContainerType,
                      typename mcounts_t, typename mutation_removal_policy>
            inline void
            operator()(mut_index_cont &mc, const MutationContainerType &mutations,
                       const mcounts_t &mcounts, const uint_t twoN,
                       mutation_removal_policy &&mp) const noexcept
            {
                /*
                  \note Added in 0.5.0 to address Issue #41
                */
                mc.erase(std::remove_if(
                             mc.begin(), mc.end(),
                             [&mcounts, &mutations, &twoN, &mp](
                                 const typename mut_index_cont::value_type &i) noexcept {
                                 return mcounts[i] == twoN && mp(mutations[i]);
                             }),
                         mc.end());
            }
        };

        template <typename GenomeContainerType, typename MutationContainerType,
                  typename mutation_removal_policy>
        inline typename std::enable_if<
            std::is_same<mutation_removal_policy, fwdpp::remove_nothing>::value>::type
        haploid_genome_cleaner(GenomeContainerType &, const MutationContainerType &,
                               const std::vector<uint_t> &, const uint_t,
                               const mutation_removal_policy &)
        {
            return;
        }

        template <typename GenomeContainerType, typename fixation_finder,
                  typename idiom_wrapper>
        inline void
        haploid_genome_cleaner_details(GenomeContainerType &haploid_genomes,
                                       const fixation_finder &ff,
                                       const idiom_wrapper &iw) noexcept
        {
            auto extant_haploid_genome = next_extant_haploid_genome(
                haploid_genomes.begin(), haploid_genomes.end());
            const auto fixation_n = ff(extant_haploid_genome->mutations);
            bool neutral_fixations_exist
                = (fixation_n != extant_haploid_genome->mutations.cend());
            const auto fixation_s = ff(extant_haploid_genome->smutations);
            bool selected_fixations_exist
                = (fixation_s != extant_haploid_genome->smutations.cend());
            if (!neutral_fixations_exist && !selected_fixations_exist)
                return;

            const auto gend = haploid_genomes.end();
            // Assign values to avoid tons of de-referencing later
            const auto fixation_n_value
                = (fixation_n == extant_haploid_genome->mutations.cend())
                      ? typename decltype(fixation_n)::value_type()
                      : *fixation_n;
            const auto fixation_s_value
                = (fixation_s == extant_haploid_genome->smutations.cend())
                      ? typename decltype(fixation_s)::value_type()
                      : *fixation_s;
            while (extant_haploid_genome < gend)
                {
                    if (neutral_fixations_exist)
                        {
                            iw(extant_haploid_genome->mutations, fixation_n_value);
                        }
                    if (selected_fixations_exist)
                        {
                            iw(extant_haploid_genome->smutations, fixation_s_value);
                        }
                    extant_haploid_genome
                        = next_extant_haploid_genome(extant_haploid_genome + 1, gend);
                }
        }

        template <typename GenomeContainerType, typename fixation_finder>
        std::pair<bool, bool>
        fixation_finder_search_all(const GenomeContainerType &haploid_genomes,
                                   const fixation_finder &ff) noexcept
        {
            bool neutral = false, selected = false;
            for (const auto &g : haploid_genomes)
                {
                    if (!neutral)
                        {
                            auto itr = ff(g.mutations);
                            if (itr != g.mutations.cend())
                                {
                                    neutral = true;
                                }
                        }
                    if (!selected)
                        {
                            auto itr = ff(g.smutations);
                            if (itr != g.smutations.cend())
                                {
                                    selected = true;
                                }
                        }
                    if (neutral && selected)
                        return std::make_pair(neutral, selected);
                }
            return std::make_pair(neutral, selected);
        }

        template <typename GenomeContainerType, typename fixation_finder,
                  typename idiom_wrapper>
        inline void
        haploid_genome_cleaner_details(GenomeContainerType &haploid_genomes,
                                       const fixation_finder &ff,
                                       const idiom_wrapper &iw, std::true_type) noexcept
        {
            auto t = fixation_finder_search_all(haploid_genomes, ff);
            auto neutral_fixations_exist = t.first;
            auto selected_fixations_exist = t.second;
            if (!neutral_fixations_exist && !selected_fixations_exist)
                return;

            auto extant_haploid_genome = next_extant_haploid_genome(
                haploid_genomes.begin(), haploid_genomes.end());
            const auto gend = haploid_genomes.end();
            while (extant_haploid_genome < gend)
                {
                    if (neutral_fixations_exist)
                        {
                            iw(extant_haploid_genome->mutations);
                        }
                    if (selected_fixations_exist)
                        {
                            iw(extant_haploid_genome->smutations);
                        }
                    extant_haploid_genome
                        = next_extant_haploid_genome(extant_haploid_genome + 1, gend);
                }
        }

        /*
          Now, we have the two overloads of haploid_genome_cleaner.
          The std::cref(...) below are required, o/w copies will get bound,
          which is bad for performance.
        */

        template <typename GenomeContainerType, typename MutationContainerType,
                  typename mutation_removal_policy>
        inline typename std::enable_if<
            std::is_same<mutation_removal_policy, std::true_type>::value>::type
        haploid_genome_cleaner(GenomeContainerType &haploid_genomes,
                               const MutationContainerType &,
                               const std::vector<uint_t> &mcounts, const uint_t twoN,
                               const mutation_removal_policy &)
        {
            haploid_genome_cleaner_details(
                haploid_genomes,
                [&mcounts,
                 twoN](const typename GenomeContainerType::value_type::mutation_container
                           &mc) { return find_fixation()(mc, mcounts, twoN); },
                [&mcounts,
                 twoN](typename GenomeContainerType::value_type::mutation_container &mc,
                       const typename GenomeContainerType::value_type::
                           mutation_container::value_type v) {
                    return haploid_genome_cleaner_erase_remove_idiom_wrapper()(
                        mc, mcounts, v, twoN);
                });
        }

        template <typename GenomeContainerType, typename MutationContainerType,
                  typename mutation_removal_policy>
        inline typename std::enable_if<
            !std::is_same<mutation_removal_policy, std::true_type>::value
            && !std::is_same<mutation_removal_policy,
                             fwdpp::remove_nothing>::value>::type
        haploid_genome_cleaner(GenomeContainerType &haploid_genomes,
                               const MutationContainerType &mutations,
                               const std::vector<uint_t> &mcounts, const uint_t twoN,
                               const mutation_removal_policy &mp)
        {
            haploid_genome_cleaner_details(
                haploid_genomes,
                [&mutations, &mcounts, &mp,
                 twoN](const typename GenomeContainerType::value_type::mutation_container
                           &mc) {
                    return find_fixation()(mc, mutations, mcounts, twoN, mp);
                },
                [&mutations, &mcounts, &mp,
                 twoN](typename GenomeContainerType::value_type::mutation_container &mc,
                       typename GenomeContainerType::value_type::mutation_container::
                           value_type v) {
                    return haploid_genome_cleaner_erase_remove_idiom_wrapper()(
                        mc, mutations, mcounts, v, twoN, mp);
                });
        }

        template <typename GenomeContainerType, typename MutationContainerType,
                  typename mutation_removal_policy>
        inline typename std::enable_if<
            std::is_same<mutation_removal_policy, std::true_type>::value>::type
        haploid_genome_cleaner(GenomeContainerType &haploid_genomes,
                               const MutationContainerType &,
                               const std::vector<uint_t> &mcounts, const uint_t twoN,
                               const mutation_removal_policy &, std::true_type)
        {
            haploid_genome_cleaner_details(
                haploid_genomes,
                [&mcounts,
                 twoN](const typename GenomeContainerType::value_type::mutation_container
                           &mc) { return find_fixation()(mc, mcounts, twoN); },
                [&mcounts, twoN](
                    typename GenomeContainerType::value_type::mutation_container &mc) {
                    return haploid_genome_cleaner_erase_remove_idiom_wrapper()(
                        mc, mcounts, twoN);
                },
                std::true_type());
        }

        template <typename GenomeContainerType, typename MutationContainerType,
                  typename mutation_removal_policy>
        inline typename std::enable_if<
            !std::is_same<mutation_removal_policy, std::true_type>::value
            && !std::is_same<mutation_removal_policy,
                             fwdpp::remove_nothing>::value>::type
        haploid_genome_cleaner(GenomeContainerType &haploid_genomes,
                               const MutationContainerType &mutations,
                               const std::vector<uint_t> &mcounts, const uint_t twoN,
                               const mutation_removal_policy &mp, std::true_type)
        {
            haploid_genome_cleaner_details(
                haploid_genomes,
                [&mutations, &mcounts, &mp,
                 twoN](const typename GenomeContainerType::value_type::mutation_container
                           &mc) {
                    return find_fixation()(mc, mutations, mcounts, twoN, mp);
                },
                [&mutations, &mcounts, &mp, twoN](
                    typename GenomeContainerType::value_type::mutation_container &mc) {
                    return haploid_genome_cleaner_erase_remove_idiom_wrapper()(
                        mc, mutations, mcounts, twoN, mp);
                },
                std::true_type());
        }
    }
}

#endif