Project: s41467-024-47562-4 | Function: construct_graph (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:** Constructs de Bruijn graph from SNP k-mers extracted from sequencing reads for haplotype assembly

**KEY TERMS:** {
  "node_word": "k-mer vertex identifier",
  "edge_word": "(k+1)-mer edge identifier",
  "NODE_MASK": "k-mer bit mask",
  "EDGE_MASK": "(k+1)-mer bit mask",
  "add_node": "add k-mer vertex to graph",
  "add_edge": "add edge between k-mer vertices",
  "k2_node": "2-mer nodes for connectivity patching"
}

**CONTEXT CODE:**         edge_cnt = static_cast<int*>(std::calloc(EDGE_SHIFT * snp.size(), sizeof(int)));
        weight = static_cast<int*>(std::calloc(EDGE_SHIFT * snp.size(), sizeof(int)));
        hint_post.resize(snp.size(), 0);
    }

    ~SNP_DBG() {
        std::free(node_cnt);
        std::free(edge_cnt);
        std::free(weight);
    }

    int in_degree(int s, int w) const {
        // Implement the method for in-degree if needed elsewhere in the class
        return 0;
    }

    int out_degree(int s, int w) const {
        // Implement the method for out-degree if needed elsewhere in the class
        return 0;
    }

    int get_out_weight(int s, int w, int idx) const {
        // Implement a method to get the out-going weight from edge structure, assuming a placeholder implementation
        return 0;
    }

    std::pair<int, int> next_node(int current_node, int direction) const {
        // Implement the logic to get the next node in the graph, assuming a placeholder implementation
        return {current_node + 1, (current_node + 1) & NODE_MASK};
    }

    int VID(int s, int w) const {
        // Implement the vertex identifier creation logic
        return (s * NODE_SHIFT) + w;
    }

    void add_node(int s, int w) {
        if (!node_cnt[VID(s, w)]) {
            node_cnt[VID(s, w)]++;
        }
    }

    void add_edge(int s, int e, int w) {
        int edge_id = (s * EDGE_SHIFT) + (e & EDGE_MASK);
        if (!edge_cnt[edge_id]) {
            edge_cnt[edge_id]++;
        }
        weight[edge_id] += w;
    }

    void SNP_DBG::construct_graph() {
        // Iterate over each read and extract k-mers to build the de Bruijn graph
        for (const auto& readAllele : read_row) {
            if (readAllele.size() < static_cast<size_t>(K))
                continue; // Skip reads shorter than k

            // Slide a window of size K over the SNP alleles in the read
            for (size_t i = 0; i + K <= readAllele.size(); ++i) {
                // Build node (k-mer) identifiers: from current window and next window
                uint32_t node_id = 0;
                for (size_t j = 0; j < static_cast<size_t>(K); ++j) {
                    int allele = readAllele[i + j].allele;
                    if (allele < 0) { node_id = NODE_MASK + 1; break; } // invalid allele
                    node_id = ((node_id << 1) | static_cast<uint32_t>(allele)) & NODE_MASK;
                }
                if (node_id > NODE_MASK) continue; // skip invalid k-mer

                add_node(readAllele[i].snp_idx, node_id);

                if (i + K < readAllele.size()) {
                    uint32_t next_node_id = 0;
                    for (size_t j = 1; j <= static_cast<size_t>(K); ++j) {
                        int allele = readAllele[i + j].allele;
                        if (allele < 0) { next_node_id = NODE_MASK + 1; break; }
                        next_node_id = ((next_node_id << 1) | static_cast<uint32_t>(allele)) & NODE_MASK;
                    }
                    if (next_node_id > NODE_MASK) continue;

                    // Build edge identifier (k+1-mer)
                    uint32_t edge_id = ((node_id << 1) | static_cast<uint32_t>(readAllele[i + K].allele)) & EDGE_MASK;
                    add_edge(readAllele[i].snp_idx, edge_id);

                    // Increment edge weight
                    weight[readAllele[i].snp_idx * EDGE_SHIFT + edge_id]++;
                }
            }
        }

        // Optional: Patch connectivity with k2_node (2-mer nodes) if required
        for (int s = 0; s < KMER_N - 1; ++s) {
            for (int w = 0; w < NODE_SHIFT; ++w) {
                if (node_cnt[VID(s, w)] > 0) {
                    k2_node.insert(w & 0x3); // Store last 2 bits for connectivity
                }
            }
        }

        output_graph("1_construct");
    }

    void output_graph(const std::string &filename) {
        // Placeholder for the graph output logic, assuming filenames and graph serialization
    }
};

This implementation constructs a de Bruijn graph from SNP k-mers based on the provided context and key terms. It fills in the mechanics of adding nodes and edges with simplified logic, essentially implementing placeholder methods where appropriate. Adjust as needed for the specific application requirements, especially in methods like `in_degree`, `out_degree`, `get_out_weight`, and `next_node`.

void SNP_DBG::trim_graph() {
	// Remove poor-quality competing edges compared with the most high-confident edge
	int trim_n = 0;
	const int CONFIDENT = 15, ABOVE_FILTER = 2, BELOW_FILTER = 5;
	for (int32_t s = 0; s < KMER_N - 1; s++) {
		int32_t max_weight = 0;
		for (int32_t w = 0; w < EDGE_SHIFT; w++) {
			max_weight = std::max(max_weight, weight[s * EDGE_SHIFT + w]);
		}
		if (max_weight >= CONFIDENT) {
			for (int32_t w = 0; w < EDGE_SHIFT; w++) {
				if (weight[s * EDGE_SHIFT + w] * ABOVE_FILTER < max_weight) {
					trim_n++; weight[s * EDGE_SHIFT + w] = 0;
				}
			}
		} else {
			for (int32_t w = 0; w < EDGE_SHIFT; w++) {
				if (weight[s * EDGE_SHIFT + w] * BELOW_FILTER < max_weight) {
					trim_n++; weight[s * EDGE_SHIFT + w] = 0;
				}
			}
		}
	}
	for (int32_t s = 0; s < KMER_N; s++) {
		for (int32_t w = 0; w < NODE_SHIFT; w++) {
			if (in_degree(s, w) == 0 and out_degree(s, w) == 0) {
				node_cnt[VID(s, w)] = 0;
			}
		}
	}
	fprintf(stderr, "    Trim out %d poor-quality edges from the graph\n", trim_n);
	output_graph("2_trim");
}

void SNP_DBG::remove_small_tips() {
	// Backward pass
	const int SHORT_LENGTH = 5, LONG_LENGTH = 15;
	const double WEIGHT_COEF = 1.5;
	int backward_remove = 0;
	for (int32_t s = KMER_N-1; s >= 0; s--) {
		for (int32_t w = 0; w < NODE_SHIFT/2; w++) {
			if (out_degree(s, w) != 2) continue;
			int32_t ns, nw;


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