Project: s41467-025-56893-9 | Function: is_viable_atom (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:** Validates carbon atoms for heteroatom substitution by checking valence, hybridization, and Bredt's rule constraints to ensure chemically valid functionalization

**KEY TERMS:** {
  "min_hydrogens": "minimum H atoms for substitution",
  "hetero_bound_carbons": "carbons bound to N/O",
  "sphyb_bound_carbons": "carbons bound to sp-hybridized atoms",
  "bridgeheads": "bridgehead positions in rings",
  "can_bredt_amide": "Bredt's rule validation",
  "atom_adjacencies": "neighboring atom connections",
  "elem_num": "atomic number (6 for carbon)"
}

**CONTEXT CODE:**
    std::unordered_map<unsigned int,std::unordered_set<unsigned int>> atom_adjacencies = get_atom_adjacencies(mol);
    
    // A set to store indices of carbons that are already bound to heteroatoms
    std::unordered_set<int> hetero_bound_carbons = {};

    // A set to store indices of carbons that are bound to sp hybrized atoms
    std::unordered_set<int> sphyb_bound_carbons = {};

    // Find all carbons that are bound to a hetero-atom, or an sp-hybridized carbon
    get_bound_carbons_information(mol, hetero_bound_carbons, sphyb_bound_carbons);

    // Create a bound hydrogen count map
    std::unordered_map<unsigned int, unsigned int> hydrogen_map = get_bound_hydrogens(mol);

    // Find the bridgehead atoms and list all rings in the molecule
    std::pair<std::unordered_set<unsigned int>,std::vector<std::unordered_set<unsigned int>>> information = find_bridgehead_atoms(mol);
    std::unordered_set<unsigned int> bridgeheads = information.first;
    std::vector<std::unordered_set<unsigned int>> cycles = information.second;

    // A store to store all atom indices of all (putatively valid) carbon atoms
    std::unordered_set<unsigned int> carbon_indices = {};

    // Get the number of atoms in this molecule
    const unsigned int num_atoms = mol.getNumAtoms();

    // Iterate over all atoms in the molecule
    for (unsigned int atom_idx = 0 ; atom_idx < num_atoms ; ++atom_idx)
    {
       
        // Has to be a mutatable atom
        if (!is_viable_atom(mol, atom_idx, min_hydrogen, hetero_bound_carbons,  
                            sphyb_bound_carbons, atom_adjacencies, bridgeheads, verbose))
        {            
            continue;
        }

        // Store this atom's index as viable
        carbon_indices.emplace(atom_idx);
    }

    // Get the number of carbon atoms to mutate
    const unsigned int num_mutations = static_cast<unsigned int>(carbon_indices.size());

    // Mutate 1 to N carbon atoms in this molecule
    for (unsigned int n = 1; n <= num_mutations; ++n)
    {
        // Avoid doing the same operations, if the atoms are symmetrically identical
        std::set<std::vector<int>> seen_mutation_patterns = {};

        // Generate all combinations to mutate input molecule in N different spots
        const std::vector<std::vector<int>> combinations = generateCombinations(carbon_indices, n); 

        // Iterate over each combination of mutations
        for (const std::vector<int>& mutation_pattern : combinations)
        {    
            // See if this mutation pattern has been evaluated before in terms of symmetrical atoms
            std::vector<int> standard_mutation_pattern = standardize_operation(mutation_pattern, equivalent_atoms);

            // Store the mutation pattern
            if (seen_mutation_patterns.count(standard_mutation_pattern) == 0)
            {
                seen_mutation_patterns.emplace(standard_mutation_pattern);
            }

            // an equivalent operation has been done before, skip loop and save time
            // do this before any evaluation of valid operations
            else
            {   
                ; //continue
            }

            // Do a atom-adjacency check, no hetero_hetero to be formed!
            bool contains_adjacent_mutations = false;

            // Do a common neighbor check, no aminals etc to be formed!
            bool invalid_sharing_neighbor = false;

            // Number of atom mutations in this pattern
            const int num_mutations = static_cast<int>(mutation_pattern.size());

            // Analyse the upper triangular submatrix of mutations (i,j)
            // Iterate over the indices of atom (i) that will be mutated 
            for (int i = 0; i < num_mutations; ++i) 
            {    
                // Get the adjacent atom indices of the atom with index i 
                const std::unordered_set<unsigned int> adjacent_atoms = atom_adjacencies[mutation_pattern[i]];

                // Iterate over the indices of atoms (j) that will be mutated
                for (int j = i + 1; j < num_mutations; ++j) 
                {
                    // Check if the mutations are adjacent
                    if (adjacent_atoms.count(mutation_pattern[j]) > 0)
                    {
                        contains_adjacent_mutations = true;
                        break;
                    }

                    // Check if they share a common neighbor
                    for (const unsigned int& k : atom_adjacencies[mutation_pattern[j]])
                    {
                        // Share a common neighbor
                        if (adjacent_atoms.count(k) > 0)
                        {
                            // Get the hybridization state of the common neighbor
                            RDKit::Atom::HybridizationType hybridization = mol.getAtomWithIdx(k)->getHybridization();

                            // If the common neighbor is sp3 hybridized
                            if (hybridization == RDKit::Atom::HybridizationType::SP3)
                            {
                                // Check if they are in the same ring
                                if (ring_info->areAtomsInSameRing(mutation_pattern[i], mutation_pattern[j]))
                                {
                                    // Can't have N-@Csp3-@N
                                    if (element == "N")
                                    {
                                        invalid_sharing_neighbor = true;
                                        break;
                                    }

                                    // Can have O-@Csp3-@O
                                    else
                                    {
                                        // If the ringsize is larger than 4
                                        if (ring_info->areAtomsInSameRingOfSize(mutation_pattern[i], mutation_pattern[j],4u))
                                        {
                                            invalid_sharing_neighbor = true;
                                            break;
                                        }
                                    }
                                }

                                // They're not in the same ring
                                else
                                {
                                    invalid_sharing_neighbor = true;
                                    break;
                                }              
                            }
                        }
                    
                    }

                    // If they're sharing an invalid neighbor, break loop
                    if (invalid_sharing_neighbor)
                    {
                        break;
                    }
                }

                // if the pattern contains two adjacent atoms