# ============================================================
# Internal helper: compute edge density and network moments
# for a (small, dense) adjacency matrix.
# ============================================================


#' Compute edge density and network moments
#'
#' Given a square binary adjacency matrix and a compiled motif specification,
#' returns the edge density and the vector of network moments (rescaled motif
#' counts).
#'
#' For a network with \eqn{k} nodes the edge density is
#' \eqn{\hat\rho = |E| / [k(k-1)]}.  The network moment for motif \eqn{R}
#' with \eqn{r} nodes is \eqn{U_R = \binom{k}{r}^{-1} X_R}, where \eqn{X_R}
#' is the (non-induced) motif count.  Built-in moments use matrix-algebraic
#' formulas that avoid explicit enumeration.
#'
#' @param A A square binary adjacency matrix (\strong{dense}).  Must have at
#'   least 3 rows/columns for V-shape and triangle moments, and at least 4 for
#'   the 3-star moment.
#' @param motif_specs Compiled motif specification, as returned by
#'   \code{\link{init_motif_specs}}.
#'
#' @return A list with:
#' \describe{
#'   \item{\code{rho}}{Scalar edge density.}
#'   \item{\code{UR}}{Named numeric vector of network moments (one per motif
#'     in \code{motif_specs}).}
#' }
#' Rows corresponding to graphs with zero density return \code{rho = 0} and
#' all moments equal to zero.
#'
#' @importFrom stats setNames
#' @keywords internal
compute_network_moments <- function(A, motif_specs) {

  k   <- nrow(A)
  rho <- sum(A) / (k * (k - 1L))

  m        <- length(motif_specs$names)
  zero_vec <- stats::setNames(rep(0, m), motif_specs$names)

  if (rho < 1e-9 || k < 3L) {
    return(list(rho = 0, UR = zero_vec))
  }

  list(rho = rho, UR = motif_specs$compute(A))
}
