Project: s41467-024-55639-3 | Function: flip_spin (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 Metropolis-Hastings spin flip dynamics for interdependent Ising networks with coupling between layers to model phase transitions in multiplex systems

**KEY TERMS:** {
  "spins1/2/3": "spin states in each layer",
  "inter_connected": "interdependence between layers",
  "local_m": "local magnetization",
  "E": "energy change from flip",
  "beta": "inverse temperature",
  "M1/M2/M3": "total magnetization per layer",
  "G1/G2/G3": "network topology of each layer"
}

**CONTEXT CODE:**  *
 *  Created on: Feb 20, 2018
 *      Author: benaya
 */

#ifndef SRC_ising_multiplex_3_q_HPP_
#define SRC_ising_multiplex_3_q_HPP_

#include <iostream>
#include <vector>
#include <cmath>
#include <string>
#include <numeric>
#include <fstream>
#include <algorithm>
#include <stdexcept>
#include "Graph.hpp"



class ising_multiplex_3_q{
public:

    double L,N;  // size, N=LXL
    double q;    // fraction of interdependent nodes
    double p;
    Graph G1, G2, G3; // The networks
    vector<bool> inter_connected12, inter_connected13, inter_connected23; //vector which state is the node is interdependent or not
    vector<int> spins1, spins2, spins3;  // The state of the spins in each network. [-1,1].
    vector<double> local_m1, local_m2, local_m3;

    vector<double> local_m1_thermal_avg, local_m2_thermal_avg, local_m3_thermal_avg;
    vector<double> local_m1_counter, local_m2_counter, local_m3_counter;

    double M1, M2, M3;
    double E1, E2, E3, E;

    double giant1, giant2, giant3;

    ising_multiplex_3_q(double L_, double d, double q_);
    void Create_interlinks();
    void initialize_spins_disordered();
    void initialize_spins_ordered_up();
    void initialize_spins_ordered_down();
    void initializing_local_m();
    #include <vector>
    #include <cmath>
    #include <stdexcept>
    #include <algorithm>

    void ising_multiplex_3_q::flip_spin(int spin, int net_idx, int step) {
        // Validate input parameters to prevent out-of-bounds errors and invalid layer indices
        if (net_idx < 1 || net_idx > 3) {
            throw std::invalid_argument("net_idx must be 1, 2, or 3 (layer index)");
        }
        if (spin < 0 || static_cast<size_t>(spin) >= spins1.size()) {
            throw std::out_of_range("spin index exceeds network size N");
        }

        // Get pointer to the current spin in the specified layer for efficient updates
        int* current_spin_ptr = nullptr;
        switch (net_idx) {
            case 1: current_spin_ptr = &spins1[spin]; break;
            case 2: current_spin_ptr = &spins2[spin]; break;
            case 3: current_spin_ptr = &spins3[spin]; break;
            default: throw std::logic_error("Invalid net_idx after validation"); // Should never reach here
        }
        const int s = *current_spin_ptr;
        const int s_prime = -s; // Flipped spin state (Ising spins are ±1)

        // Calculate same-layer energy change: 2*J*s*sum(neighbor spins) (J=1 assumed)
        double sum_neighbors_same = 0.0;
        std::vector<int> neighbors;
        switch (net_idx) {
            case 1:
                neighbors = G1.get_neighbors(spin);
                for (int n : neighbors) sum_neighbors_same += spins1[n];
                break;
            case 2:
                neighbors = G2.get_neighbors(spin);
                for (int n : neighbors) sum_neighbors_same += spins2[n];
                break;
            case 3:
                neighbors = G3.get_neighbors(spin);
                for (int n : neighbors) sum_neighbors_same += spins3[n];
                break;
        }

        // Calculate inter-layer energy change: 2*K*s*sum(interdependent spins in other layers) (K=1 assumed)
        double sum_inter = 0.0;
        switch (net_idx) {
            case 1:
                if (inter_connected12[spin]) sum_inter += spins2[spin];
                if (inter_connected13[spin]) sum_inter += spins3[spin];
                break;
            case 2:
                if (inter_connected12[spin]) sum_inter += spins1[spin];
                if (inter_connected23[spin]) sum_inter += spins3[spin];
                break;
            case 3:
                if (inter_connected13[spin]) sum_inter += spins1[spin];
                if (inter_connected23[spin]) sum_inter += spins2[spin];
                break;
        }

        // Total energy change from spin flip (ΔE = ΔE_same + ΔE_inter)
        const double delta_E = 2.0 * s * (sum_neighbors_same + sum_inter);

        // Metropolis-Hastings acceptance criterion
        bool accept = false;
        const double rand_uniform = static_cast<double>(G1.gen()) / G1.gen.max(); // Uniform [0,1) random number
        if (delta_E <= 0.0) {
            accept = true; // Accept flip if energy decreases
        } else {
            // Accept with probability exp(-βΔE) for positive energy change (requires class member 'beta')
            const double acceptance_prob = std::exp(-beta * delta_E);
            accept = (rand_uniform < acceptance_prob);
        }

        // Update system state if flip is accepted
        if (accept) {
            // Flip the spin to its new state
            *current_spin_ptr = s_prime;

            // Update total magnetization (only for active nodes, per constructor logic)
            switch (net_idx) {
                case 1: if (G1.active[spin]) M1 += -2.0 * s; break;
                case 2: if (G1.active[spin]) M2 += -2.0 * s; break;
                case 3: if (G1.active[spin]) M3 += -2.0 * s; break;
            }

            // Update local magnetization (per node in the specified layer)
            switch (net_idx) {
                case 1: local_m1[spin] += -2.0 * s; break;
                case 2: local_m2[spin] += -2.0 * s; break;
                case 3: local_m3[spin] += -2.0 * s; break;
            }

            // Update layer-specific and total energy (assumes J=1, K=1 for coupling strengths)
            const double delta_E_same = 2.0 * s * sum_neighbors_same;
            const double delta_E_inter = 2.0 * s * sum_inter;
            switch (net_idx) {
                case 1:
                    E1 += delta_E_same; // Same-layer energy for layer 1
                    if (inter_connected12[spin]) E2 += 2.0 * s * spins2[spin]; // Inter-layer with layer 2
                    if (inter_connected13[spin]) E3 += 2.0 * s * spins3[spin]; // Inter-layer with layer 3
                    break;
                case 2:
                    E2 += delta_E_same; // Same-layer energy for layer 2
                    if (inter_connected12[spin]) E1 += 2.0 * s * spins1[spin]; // Inter-layer with layer 1


Complete the function implementation replacing "throw std::runtime_error("Not implemented");".