Project: s41467-024-51468-6 | Function: recombine_strains (cpp)

system: You are an expert C++ programmer implementing algorithms from scientific papers.
Complete the masked function based on the algorithm description.
Follow C++ best practices and maintain consistent code style.
Return **only** the complete C++ function code.

user: Complete this C++ function:

**DESCRIPTION:** Implements genetic recombination between two malaria parasite strains to generate new antigenic diversity through var gene reshuffling

**KEY TERMS:** {
  "s1, s2": "parent strains",
  "daughter_genes": "offspring var gene repertoire",
  "N_GENES_PER_STRAIN": "number of var genes per strain (50-60)",
  "used": "bitset tracking selected genes",
  "src_index": "source gene index from parents"
}

**CONTEXT CODE:** #include "varmodel.hpp"
#include "StrainManager.hpp"
#include "GeneManager.hpp"
#include "PopulationManager.hpp"
#include "HostManager.hpp"
#include "InfectionManager.hpp"
#include "ImmuneHistoryManager.hpp"
#include "LocusImmunityManager.hpp"
#include "AlleleRefManager.hpp"
#include "random.hpp"
#include "util.hpp"
#include "parameters.hpp"
#include "EventQueue.hpp"

#include <unistd.h>
#include <math.h>
#include <sstream>
#include <algorithm>
#include <bitset>
#include <chrono>
using namespace std::chrono;

namespace varmodel {

#pragma mark \
*** SIMULATION STATE ***
    
    double INF = std::numeric_limits<double>::infinity();

std::mt19937_64 rng(RANDOM_SEED);

double now = 0.0;
uint64_t recomb_counter = 0;
uint64_t functional_count = 0;
uint64_t mut_count = 0;
double next_sampling_time = HOST_SAMPLING_START_YEAR * T_YEAR + HOST_SAMPLING_PERIOD[0];
uint64_t current_host_sampling_period_counter = 1;
//always output summary every 30 days;
double next_summary_time = T_BURNIN;
double next_verification_time = VERIFICATION_ON ? 0.0 : INF;
double next_checkpoint_time = SAVE_TO_CHECKPOINT ? 0.0 : INF;
//add a new type of event that introduce global mutations to the population
double next_global_mutation_time = EXPECTED_EQUILIBRIUM;
// Get starting timepoint
auto start = high_resolution_clock::now();
auto stop = high_resolution_clock::now();


uint64_t n_infections_cumulative = 0;

std::array<uint64_t, N_LOCI> n_alleles = N_ALLELES_INITIAL;

std::array<double, 360> pop_size_before_IRS;
double current_pop_size = 0;

StrainManager strain_manager;

GeneManager gene_manager;
std::unordered_map<
    std::array<uint64_t, N_LOCI>,
    Gene *,
    HashArray<uint64_t, N_LOCI>
> alleles_genes_map;

PopulationManager population_manager;
HostManager host_manager;
InfectionManager infection_manager;
ImmuneHistoryManager immune_history_manager;
LocusImmunityManager locus_immunity_manager;

AlleleRefManager allele_ref_manager;
std::array<std::vector<AlleleRef *>, N_LOCI> allele_refs;

sqlite3 * sample_db;

sqlite3_stmt * summary_stmt;
sqlite3_stmt * summary_alleles_stmt;

sqlite3_stmt * host_stmt;
sqlite3_stmt * strain_stmt;
sqlite3_stmt * gene_stmt;
sqlite3_stmt * allele_stmt;

sqlite3_stmt * sampled_inf_stmt;
sqlite3_stmt * sampled_imm_stmt;
sqlite3_stmt * sampled_strain_stmt;
sqlite3_stmt * sampled_gene_stmt;
sqlite3_stmt * sampled_allele_stmt;
sqlite3_stmt * sampled_duration_stmt;

#pragma mark \
*** EVENT QUEUES ***
    
    double get_biting_time(Population * p) { return p->next_biting_time; }
EventQueue<Population, get_biting_time> biting_queue; 

double get_immigration_time(Population * p) { return p->next_immigration_time; }
EventQueue<Population, get_immigration_time> immigration_queue;

double get_next_immunity_loss_time(Host * h) { return h->next_immunity_loss_time; }
EventQueue<Host, get_next_immunity_loss_time> immunity_loss_queue;

double get_death_time(Host * host) { return host->death_time; }
EventQueue<Host, get_death_time> death_queue;

double get_transition_time(Infection * infection) { return infection->transition_time; }
EventQueue<Infection, get_transition_time> transition_queue;

double get_mutation_time(Infection * infection) { return infection->mutation_time; }
EventQueue<Infection, get_mutation_time> mutation_queue;

double get_recombination_time(Infection * infection) { return infection->recombination_time; }
EventQueue<Infection, get_recombination_time> recombination_queue;

double get_clearance_time(Infection * infection) { return infection->clearance_time; }
EventQueue<Infection, get_clearance_time> clearance_queue;

double get_IRS_time(Population * p) {return p->next_IRS_rate_change_time;}
EventQueue<Population, get_IRS_time> IRS_queue;

double get_MDA_time(Population * p) {return p->next_MDA_time;}
EventQueue<Population, get_MDA_time> MDA_queue;

#pragma mark \
*** Helper function declarations ***
    
    void verify_immunity_consistency();

void initialize(bool override_seed, uint64_t random_seed);
void clean_up();

void validate_and_load_parameters();

void write_summary();
void sample_hosts();
void write_host(Host * host);
void write_strain(Strain * strain, sqlite3_stmt * s_stmt, sqlite3_stmt * g_stmt, sqlite3_stmt * a_stmt);
void write_gene(Gene * gene, sqlite3_stmt * g_stmt, sqlite3_stmt * a_stmt);
void write_sampled_infection(Host * host, Infection * infection);
void write_sampled_immunity(Host * host);
void write_duration(Host * host, Infection * infection);
void load_checkpoint(bool should_load_rng_state);
void save_checkpoint();

void save_global_state_to_checkpoint(sqlite3 * db);
void save_global_pop_size_to_checkpoint(sqlite3 * db);
void load_global_state_from_checkpoint(sqlite3 * db, bool should_load_rng_state);
void load_allele_refs();
void initialize_event_queues_from_state();

std::string get_rng_as_string();
void set_rng_from_string(std::string const & rng_str);

void initialize_sample_db();
void finalize_sample_db();

void initialize_gene_pool();

void initialize_populations();
void initialize_population(uint64_t order);
void initialize_population_events(Population * pop);
void initialize_population_hosts(Population * pop);
void initialize_population_infections(Population * pop);

Host * create_host(Population * pop, bool newborn);
void destroy_host(Host * host);

Gene * get_gene_with_alleles(std::array<uint64_t, N_LOCI> const & alleles);
Gene * get_or_create_gene(std::array<uint64_t, N_LOCI> const & alleles, GeneSource source, bool is_functional, bool in_pool);
Strain * generate_random_strain();

Strain * create_strain();
void destroy_strain(Strain * strain);
void retain_strain(Strain * strain);
void release_strain(Strain * strain);

Gene * draw_random_gene();
std::array<uint64_t, N_LOCI> recombine_alleles(std::array<uint64_t, N_LOCI> const & a1, std::array<uint64_t, N_LOCI> const & a2, uint64_t breakpoint);
Gene * recombine_alleles(Gene * gene1, Gene * gene2, uint64_t breakpoint, double similarity, bool create_new_allele);
bool contains_different_genes(Strain * strain);


#include <bitset>
#include <array>
#include <vector>
#include <random>
#include <algorithm>
#include <stdexcept>

constexpr uint64_t N_GENES_PER_STRAIN = 50; // Assumed number, adjust if needed
const uint64_t N_LOCI = 3; // Assumed number of loci, adjust if needed

struct Gene {
    std::array<uint64_t, N_LOCI> alleles;
};

struct Strain {
    std::vector<Gene*> var_genes;
};

Strain* recombine_strains(Strain* s1, Strain* s2) {
    std::bitset<N_GENES_PER_STRAIN> used;
    Strain* daughter = new Strain();
    daughter->var_genes.reserve(N_GENES_PER_STRAIN);

    for (uint64_t src_index = 0; src_index < N_GENES_PER_STRAIN; ++src_index) {
        Gene* gene = nullptr;
        uint64_t src_parent = (std::rand() % 2 == 0) ? 0 : 1;

        if (used.test(src_index)) {
            gene = daughter->var_genes.back(); // Select previously used gene
        } else {
            if (!s1->var_genes.empty() && !s2->var_genes.empty()) {
                gene = recombine_alleles(
                    s1->var_genes[src_index],
                    s2->var_genes[src_index],
                    std::rand() % N_GENES_PER_STRAIN);
            } else {
                throw std::runtime_error("Parent strain has no genes.");
            }

            // Track used gene
            used.set(src_index);

            // Add recombined gene to offspring
            daughter->var_genes.push_back(gene);
        }
    }

    return daughter;
}

Gene* recombine_alleles(Gene* gene1, Gene* gene2, uint64_t breakpoint) {
    Gene* child = new Gene();
    std::bitset<N_LOCI> mask;

    for (uint64_t i = 0; i < N_LOCI; ++i) {
        if (breakpoint % N_LOCI == i) {
            mask.set(i);
        }

        if (mask.test(i)) {
            child->alleles[i] = gene2->alleles[i];
        } else {
            child->alleles[i] = gene1->alleles[i];
        }
    }

    // Mixing loci part (simple single recombination)
    // Optional: More sophisticated recombination logic here

    return child;
}


#define RETURN(x) {                            \
if(PRINT_FUNCTION_TRACE) {                     \
    for(uint64_t i = 0; i < call_depth; i++) { \
        printf(" ");                           \
    }                                          \
    printf("RETURN: %s\n", __func__);          \
}                                              \
call_depth--;                                  \
return x;                                      \
}

#define PRINT_DEBUG(level, ...) {               \
if(level <= PRINT_DEBUG_LEVEL) {                \
    for(uint64_t i = 0; i <= call_depth; i++) { \
        printf(" ");                            \
    }                                           \
    printf(__VA_ARGS__);                        \
    printf("\n");                               \
}                                               \
}

#pragma mark \
*** Initialization function implementations ***
    
    void validate_and_load_parameters() {
        BEGIN();
        
        assert(RANDOM_SEED > 0);
        
        assert(T_YEAR > 0.0);
        assert(T_END >= 0.0);
        assert(T_BURNIN >= 0.0);
        assert(T_BURNIN <= T_END);
        assert(T_BURNIN + 360 <= EXPECTED_EQUILIBRIUM);
        
        assert(SAMPLE_DB_FILENAME == "" || !file_exists(SAMPLE_DB_FILENAME));
        for (uint64_t i = 0; i < HOST_SAMPLING_PERIOD.size(); i++) {
            assert(HOST_SAMPLING_PERIOD[i] >= 0.0 && HOST_SAMPLING_PERIOD[i] <= T_YEAR);
        }
        
        assert(!SAVE_TO_CHECKPOINT || !file_exists(CHECKPOINT_SAVE_FILENAME));
        assert(!SAVE_TO_CHECKPOINT || CHECKPOINT_SAVE_PERIOD > 0.0);
        
        assert(!LOAD_FROM_CHECKPOINT || file_exists(CHECKPOINT_LOAD_FILENAME));
        
        assert(!VERIFICATION_ON || VERIFICATION_PERIOD > 0.0);
        
        assert(N_GENES_INITIAL >= 1);
        assert(N_GENES_PER_STRAIN >= 2);
        
        assert(N_LOCI >= 1);
        assert(N_ALLELES_INITIAL.size() == N_LOCI);
        for(auto value : N_ALLELES_INITIAL) {
            assert(value >= 1);
        }
        
        assert(GENE_TRANSMISSIBILITY >= 0.0 && GENE_TRANSMISSIBILITY <= 1.0);
        assert(IMMUNITY_LOSS_RATE >= 0.0);
        
        assert(MUTATION_RATE >= 0.0);
        assert(T_LIVER_STAGE >= 0.0);
        
        assert(P_ECTOPIC_RECOMBINATION_IS_CONVERSION >= 0.0 && P_ECTOPIC_RECOMBINATION_IS_CONVERSION <= 1.0);
        
        assert(MEAN_HOST_LIFETIME > 0.0);
        assert(MAX_HOST_LIFETIME > 0.0);
        
        assert(N_POPULATIONS >= 1);
        for(auto value : N_HOSTS) {
            assert(value >= 1);
        }
        
        assert(BITING_RATE_MEAN.size() == N_POPULATIONS);
        for(auto value : BITING_RATE_MEAN) {
            assert(value >= 0.0);
        }
        assert(BITING_RATE_RELATIVE_AMPLITUDE.size() == N_POPULATIONS);
        for(auto value : BITING_RATE_RELATIVE_AMPLITUDE) {
            assert(value >= 0.0 && value <= 1.0);
        }
        assert(BITING_RATE_PEAK_PHASE.size() == N_POPULATIONS);
        for(auto value : BITING_RATE_PEAK_PHASE) {
            assert(value >= 0.0 && value <= 1.0);
        }
        
        assert(!IMMIGRATION_ON || IMMIGRATION_RATE.size() == N_POPULATIONS);
        if(IMMIGRATION_ON) {
            for(auto value : IMMIGRATION_RATE) {
                assert(value >= 0.0);
            }
        }
        
        assert(DISTANCE_MAT.size() == N_POPULATIONS);
        for (auto distRow : DISTANCE_MAT){
            assert(distRow.size() == N_POPULATIONS);
        }
        /*if(SELECTION_MODE == GENERAL_IMMUNITY) {
         assert(GENERAL_IMMUNITY_PARAMS[0] > 0.0);
         assert(GENERAL_IMMUNITY_PARAMS[1] > 0.0);
         assert(GENERAL_IMMUNITY_PARAMS[2] > 0.0);
         assert(GENERAL_IMMUNITY_PARAMS[3] > 0.0);
        }*/
        
        if (ECTOPIC_RECOMBINATION_CREATE_NEW_ALLELE) {
            assert(P_ECTOPIC_RECOMBINATION_CREATE_NEW_ALLELE > 0.0 && P_ECTOPIC_RECOMBINATION_CREATE_NEW_ALLELE <= 1.0);
        }
        
        RETURN();
    }

void initialize(bool override_seed, uint64_t random_seed) {
    BEGIN();
    
    validate_and_load_parameters();
    initialize_sample_db();
    if(override_seed) {
        rng.seed(random_seed);
    }
    
    if(LOAD_FROM_CHECKPOINT) {
        //do not use the seed from the previous run!!
        load_checkpoint(override_seed);
    }
    else {
        initialize_gene_pool();
        initialize_populations();
    }
    
    RETURN();
}

void clean_up() {
    BEGIN();
    
    finalize_sample_db();
    
    RETURN();
}

void initialize_gene_pool() {
    BEGIN();
    
    // Create AlleleRefs
    for(uint64_t i = 0; i < N_LOCI; i++) {
        assert(allele_refs[i].size() == 0);