Build a CSR graph from a deduplicated undirected edge list. */
| 6647 | |
| 6648 | /* Build a CSR graph from a deduplicated undirected edge list. */ |
| 6649 | static int lg_build(int n, const int *wsi, const int *wdi, const double *ww, int wn, |
| 6650 | cbm_lg_t *out) { |
| 6651 | int *off = calloc((size_t)n + 1, sizeof(int)); |
| 6652 | double *k = calloc((size_t)n, sizeof(double)); |
| 6653 | int *fill = malloc((size_t)n * sizeof(int)); |
| 6654 | if (!off || !k || !fill) { |
| 6655 | free(off); |
| 6656 | free(k); |
| 6657 | free(fill); |
| 6658 | return CBM_NOT_FOUND; |
| 6659 | } |
| 6660 | for (int e = 0; e < wn; e++) { |
| 6661 | off[wsi[e] + 1]++; |
| 6662 | off[wdi[e] + 1]++; |
| 6663 | } |
| 6664 | for (int i = 0; i < n; i++) { |
| 6665 | off[i + 1] += off[i]; |
| 6666 | } |
| 6667 | int total = off[n]; |
| 6668 | /* calloc, not malloc: every slot IS written (off[n] equals the summed |
| 6669 | * degrees, two writes per edge), but that invariant is invisible to |
| 6670 | * path-sensitive analysis, and zero-filled backing turns any future |
| 6671 | * degree-miscount bug into a benign self-loop instead of UB. */ |
| 6672 | int *nbr = calloc((size_t)(total > 0 ? total : 1), sizeof(int)); |
| 6673 | double *w = calloc((size_t)(total > 0 ? total : 1), sizeof(double)); |
| 6674 | if (!nbr || !w) { |
| 6675 | free(off); |
| 6676 | free(k); |
| 6677 | free(fill); |
| 6678 | free(nbr); |
| 6679 | free(w); |
| 6680 | return CBM_NOT_FOUND; |
| 6681 | } |
| 6682 | memcpy(fill, off, (size_t)n * sizeof(int)); |
| 6683 | for (int e = 0; e < wn; e++) { |
| 6684 | int a = wsi[e]; |
| 6685 | int b = wdi[e]; |
| 6686 | double we = ww[e]; |
| 6687 | nbr[fill[a]] = b; |
| 6688 | w[fill[a]] = we; |
| 6689 | fill[a]++; |
| 6690 | nbr[fill[b]] = a; |
| 6691 | w[fill[b]] = we; |
| 6692 | fill[b]++; |
| 6693 | k[a] += we; |
| 6694 | k[b] += we; |
| 6695 | } |
| 6696 | free(fill); |
| 6697 | out->n = n; |
| 6698 | out->off = off; |
| 6699 | out->nbr = nbr; |
| 6700 | out->w = w; |
| 6701 | out->k = k; |
| 6702 | return CBM_STORE_OK; |
| 6703 | } |
| 6704 | |
| 6705 | /* Local-moving phase: greedily move each node to the neighbouring community |
| 6706 | * with the highest modularity gain, using a work queue seeded with every node |