// gdpar Path 1 -- Full-Bayes (FB) multi-parametric multivariate
// distribution template, Path C (K >= 2 K-individual slots crossed
// with p >= 2 AMM coordinates). Sub-bloque 9.3.d (Bloque 9, Sesion
// B9.5, 2026-05-27) under decision I.iv lateral canonized in B9.4
// (DESIGN_9_3_D_PATH_C.md sub-decision 3.2 = J.iv.A: dedicated piece
// with canonical helpers reuse).
//
// Architectural composition (mirrors amm_eb_marginal_KxP.stan from
// Sub-fase 8.6.D, 2026-05-25): Path A multivariate (Theorem 7A* of
// v07b, coord-wise factorization across p) crossed with Path B
// multi-parametric (Theorem 7C* of v07b, distributional regression
// canonical link per slot). Single outcome y[n, p] multivariate
// (matrix-column outcome shared across the K slots, coord-wise
// factorized across p). The K slots are the canonical distributional
// parameters of the family (e.g., (mu, log sigma) for Gaussian K=2,
// (log mu, log phi) for NB K=2, (mu identity, log sigma, log nu) for
// Student-t K=3). Each slot has its own anchor in R^p per group,
// mirroring Path A.
//
// Reference framework, per individual i, (optional) group g[i], slot
// k = 1..K, and coordinate j = 1..p:
//   eta_kp[k, i, j] = theta_ref_kp[g[i], k][j]
//                     + a_kj(x_i)
//                     + b_kj(x_i) * theta_ref_kp[g[i], k][j]
//                     + (W_kj(theta_ref_kp[g[i], k][j])
//                        - W_kj(theta_anchor_kp[k][j])) * x_i
//   y[i, j] ~ D_{family_id_k_vector[1]}(
//     eta_kp[1, i, j],
//     apply_inv_link_by_id(inv_link_id_per_slot[2], eta_kp[2, i, j]),
//     ...
//   )
//
// Coverage (Sesion B9.5, J.iv.A): Path B family set
// {1 gaussian, 3 neg_binomial_2, 5 beta, 6 gamma, 7 lognormal_loc_scale,
//  8 student_t, 9 tweedie, 10 zip, 11 zinb, 12 hurdle_poisson,
//  13 hurdle_neg_binomial_2}. K.c roster minimal bootstrap covers
// families 1, 5, 6, 8 (seeds 91001..91004 reserved in B9.4
// DESIGN_9_3_D_PATH_C.md §4); remaining families covered by the same
// dispatch with the same caveats heredados de Sub-fase 8.6.D
// (Path B logit-strict bajo Path C cerca del borde de soporte; tweedie
// con slot p estructuralmente acotado in (1.01, 1.99) via
// THETA_REF_PRIOR_BLOCK por slice).
//
// Internal sampling parametrization (preserved from Path B K +
// Path A multi):
//   a    : NCP only per slot (per-slot scale sigma_a_k; per-slot
//          per-coord CP / mixed deferred to B9.6+ canonical KxP
//          a-blocks; B9.5 atomic).
//   b    : LINEAR reparametrization per slot per coord on c_b_kp =
//          theta_ref_kp * b_coef_kp; NCP sample of c_b_kp_raw and
//          derive b_coef_kp post-hoc in the single-anchor regime.
//   W    : configurable CP/NCP globally (placeholders W_SCALE and
//          W_PRIOR), inactive (use_W = 0) in B9.5 initial roster;
//          enabled via use_W = 1 with apply_W_basis_diff() dispatch
//          on W_type_id mirror of distrib_K and pmulti.
//   theta_ref_kp : per-group, per-slot, per-coord anchor; hierarchical
//                  under use_groups == 1, single-anchor coord-wise
//                  i.i.d. otherwise. THETA_REF_PRIOR_BLOCK placeholder
//                  emits the family-specific block (Tweedie K=3 with
//                  bounded p slot via slice).
//
// Generated by R/stan_codegen.R via placeholder substitution; do not
// edit the substituted output by hand. The dedicated helpers piece
// inst/stan/_canonical_pieces/amm_canonical_helpers.stan is injected
// at the CANONICAL_HELPERS placeholder inside the functions { } block
// (decision G.iv lateral of B9.4).
//
// Caveat heredado (DESIGN_9_3_D_PATH_C.md §5): Path B logit-strict
// bajo Path C puede causar inestabilidad numerica para families con
// link inv_logit (beta, bernoulli) cuando el dominio del covariado
// produce eta cerca de +/- inf. Mitigacion: el roster K.c B9.5 evita
// logit-strict puro (gaussian + beta + gamma + student_t); roster
// expandido a families {10 ZIP, 12 hurdle-Poisson} (link logit en
// pi) diferido a B9.6 con skip_if granular si emerge.

functions {
  // Sub-bloque 9.3.d colateral via G.iv lateral (Sesion B9.4): the
  // bspline_basis_eval + apply_W_basis_diff helpers are injected here
  // from the dedicated piece amm_canonical_helpers.stan.
  // {{CANONICAL_HELPERS}}

  // Sub-phase 8.3.5b: Tweedie compound Poisson-gamma log-density for
  // the regime 1 < p < 2 (hybrid Dunn-Smyth series + saddlepoint).
  // Mirror of the K = 1 + p = 1 template inst/stan/amm_distrib_K.stan
  // functions block (sub-phase 8.3.5b, 2026-05-21). The functions are
  // identical because the Tweedie likelihood is the same regardless
  // of the cross-product K x p (the function takes scalar arguments
  // y, mu, phi, p and returns a scalar log-density).
  real tweedie_log_W_series(real y, real phi, real p) {
    real alpha_pos = (2.0 - p) / (p - 1.0);
    real log_z = alpha_pos * log(y)
                 - alpha_pos * log(p - 1.0)
                 - (1.0 + alpha_pos) * log(phi)
                 - log(2.0 - p);
    real log_W = negative_infinity();
    real max_log_W = negative_infinity();
    int max_iter = 1000;
    int passed_peak = 0;
    for (j_loop in 1:max_iter) {
      real j = j_loop;
      real term = j * log_z - lgamma(j + 1.0) - lgamma(j * alpha_pos);
      if (term > max_log_W) {
        max_log_W = term;
      } else {
        passed_peak = 1;
      }
      log_W = log_sum_exp(log_W, term);
      if (passed_peak == 1 && term < max_log_W - 37.0) {
        break;
      }
    }
    return log_W;
  }

  real tweedie_log_f_series(real y, real mu, real phi, real p) {
    real theta = pow(mu, 1.0 - p) / (1.0 - p);
    real kappa = pow(mu, 2.0 - p) / (2.0 - p);
    return (y * theta - kappa) / phi - log(y)
           + tweedie_log_W_series(y, phi, p);
  }

  real tweedie_log_f_saddlepoint(real y, real mu, real phi, real p) {
    real deviance = 2.0 * (
      y * (pow(y, 1.0 - p) - pow(mu, 1.0 - p)) / (1.0 - p)
      - (pow(y, 2.0 - p) - pow(mu, 2.0 - p)) / (2.0 - p)
    );
    return -0.5 * log(2.0 * pi() * phi * pow(y, p))
           - deviance / (2.0 * phi);
  }

  real tweedie_lpdf(real y, real mu, real phi, real p) {
    real tau = 0.4;
    real lambda = pow(mu, 2.0 - p) / (phi * (2.0 - p));
    if (y == 0.0) {
      return -lambda;
    }
    if (abs(p - 1.5) < tau) {
      return tweedie_log_f_series(y, mu, phi, p);
    } else {
      return tweedie_log_f_saddlepoint(y, mu, phi, p);
    }
  }

  real tweedie_rng(real mu, real phi, real p) {
    real lambda = pow(mu, 2.0 - p) / (phi * (2.0 - p));
    int N = poisson_rng(lambda);
    if (N == 0) {
      return 0.0;
    }
    real shape = (2.0 - p) / (p - 1.0);
    real rate = 1.0 / (phi * (p - 1.0) * pow(mu, p - 1.0));
    return gamma_rng(N * shape, rate);
  }

  // Sub-phase 8.3.7 heterogeneous slot inverse-link dispatch. Mirror
  // of amm_distrib_K.stan apply_inv_link_by_id. Consumed by the K=2
  // likelihood branches (family_id_k_vector[1] in {1, 3, 5, 6, 7})
  // applied coord-wise.
  real apply_inv_link_by_id(int link_id, real eta) {
    if (link_id == 0) return eta;
    if (link_id == 1) return inv_logit(eta);
    return exp(eta);
  }
}

data {
  int<lower=1> n;
  int<lower=2> K;
  int<lower=2> p;

  // family_id_k_vector is homogeneous across slots in the B9.5
  // initial iteration (heterogeneous slot families deferred to a
  // future sub-phase). The dispatcher uses family_id_k_vector[1] as
  // the canonical family identifier.
  array[K] int<lower=1, upper=13> family_id_k_vector;

  // inv_link_id_per_slot[k] is the canonical Stan inverse link of
  // slot k (inherited from Path B K dispatch under L1 refined rule).
  // Values: 0 = identity, 1 = inv_logit, 2 = exp.
  array[K] int<lower=0, upper=2> inv_link_id_per_slot;

  // Per-slot use flags. When a slot is inactive, J_a_per_kp /
  // J_b_per_kp entries are zero and the dispatch loop reduces to a
  // zero-cost branch.
  array[K] int<lower=0, upper=1> use_a_k;
  array[K] int<lower=0, upper=1> use_b_k;
  int<lower=0, upper=1> use_W;

  // Per-slot per-coord free-column counts (full ragged structure
  // canonized in Sub-fase 8.6.D D41 for KxP regime). The flat-pack of
  // a_raw / c_b_kp_raw via per-(slot, coord) offsets respects this
  // ragged structure.
  int<lower=0> J_a_max;
  int<lower=0> J_b_max;
  array[K, p] int<lower=0> J_a_per_kp;
  array[K, p] int<lower=0> J_b_per_kp;

  // Per-slot per-coord padded design matrices. Z_a_kp[k, j][i,
  // 1:J_a_per_kp[k, j]] is referenced; the remaining padded columns
  // are never touched.
  array[K, p] matrix[n, J_a_max] Z_a_kp;
  array[K, p] matrix[n, J_b_max] Z_b_kp;

  // Modulating component W (globally shared across slots and coords
  // when use_W = 1; B9.5 initial roster keeps use_W = 0). W_per_kj_dim
  // is the per-(slot, coord) basis dimension; for separable polynomial
  // and B-spline bases the total dim_W decomposes as
  // K * p * W_per_kj_dim.
  int<lower=0> dim_W;
  int<lower=0> d;
  int<lower=0> W_per_kj_dim;
  matrix[n, d] X;

  // Outcomes (matrix-column): y_real for continuous families,
  // y_int for count families. Only the one matching
  // family_id_k_vector[1] is consumed.
  matrix[n, p] y_real;
  array[n, p] int y_int;

  // Per-slot per-coord anchor.
  array[K] vector[p] theta_anchor_kp;

  // Per-slot population-scope dispersion flags.
  array[K] int<lower=0, upper=1> use_dispersion_y_k;
  array[K] int<lower=0, upper=1> use_dispersion_phi_k;

  int<lower=0, upper=2> W_type_id;
  int<lower=0> W_n_knots_full;
  vector[W_n_knots_full] W_knots_full;
  int<lower=0> W_degree;

  // Grouping (Block 6.5).
  int<lower=0, upper=1> use_groups;
  int<lower=1> J_groups;
  array[n] int<lower=1, upper=J_groups> group_id;
}

transformed data {
  array[K, p] int J_a_free_kp;
  array[K, p] int J_b_free_kp;
  array[K, p] int a_raw_offset_kp;
  array[K, p] int c_b_raw_offset_kp;
  int total_J_a_free = 0;
  int total_J_b_free = 0;
  int dim_W_eff = (use_W == 1) ? dim_W : 0;
  int d_eff = (use_W == 1) ? d : 0;
  int any_use_a = 0;
  int any_use_b = 0;
  int sigma_W_size;
  int sigma_y_size = 0;
  int phi_size = 0;
  array[K] int sigma_y_offset_k;
  array[K] int phi_offset_k;

  for (k in 1:K) {
    for (j in 1:p) {
      J_a_free_kp[k, j] = (use_a_k[k] == 1 && J_a_per_kp[k, j] > 0)
                            ? J_a_per_kp[k, j] : 0;
      J_b_free_kp[k, j] = (use_b_k[k] == 1 && J_b_per_kp[k, j] > 0)
                            ? J_b_per_kp[k, j] : 0;
      a_raw_offset_kp[k, j] = total_J_a_free;
      c_b_raw_offset_kp[k, j] = total_J_b_free;
      total_J_a_free += J_a_free_kp[k, j];
      total_J_b_free += J_b_free_kp[k, j];
      if (J_a_free_kp[k, j] > 0) any_use_a = 1;
      if (J_b_free_kp[k, j] > 0) any_use_b = 1;
    }
    sigma_y_offset_k[k] = sigma_y_size;
    phi_offset_k[k] = phi_size;
    sigma_y_size += use_dispersion_y_k[k];
    phi_size += use_dispersion_phi_k[k];
  }
  sigma_W_size = (use_W == 1 && dim_W > 0) ? 1 : 0;
  // Option A (RG.6, D96): compact sigma_a_k to slots carrying free a
  // coefficients (sum_j J_a_free_kp[k, j] > 0). A slot whose a()
  // component contributes no free coefficient --- no a() at all
  // (use_a_k == 0) or an intercept-only a() (a = ~ 1) absorbed into
  // theta_ref --- would otherwise declare a sampled-but-unused
  // half-prior scale: a flat direction (non-identification). sigma_a_idx
  // maps each slot to its position in the compacted vector (0 if none).
  // When every slot carries free a coefficients, n_sigma_a == K and
  // sigma_a_idx is the identity (the model stays bit-identical).
  int n_sigma_a = 0;
  array[K] int sigma_a_idx;
  for (k in 1:K) {
    int slot_free_a = 0;
    for (j in 1:p) {
      slot_free_a += J_a_free_kp[k, j];
    }
    if (slot_free_a > 0) {
      n_sigma_a += 1;
      sigma_a_idx[k] = n_sigma_a;
    } else {
      sigma_a_idx[k] = 0;
    }
  }
}

parameters {
  // Per-group, per-slot, per-coord anchor.
  array[J_groups, K] vector[p] theta_ref_kp;

  // Hierarchical hyperparameters of the per-group anchor prior.
  array[use_groups, K] vector[p] mu_theta_ref_kp;
  array[use_groups, K] vector<lower=0>[p] sigma_theta_ref_kp;

  // Per-slot scale of the additive component (NCP-only B9.5; compacted
  // to slots with free a coefficients; see sigma_a_idx, D96).
  array[n_sigma_a] real<lower=0> sigma_a_k;
  vector[total_J_a_free] a_raw;

  // Per-slot scale of the multiplicative component (linear
  // reparametrization on c_b_kp = theta_ref_kp * b_coef_kp).
  array[K * any_use_b] real<lower=0> sigma_b_k;
  vector[total_J_b_free] c_b_kp_raw;

  // Global modulating component.
  array[sigma_W_size] real<lower=0> sigma_W;
  matrix[dim_W_eff, d_eff] W_raw;

  // Per-slot population-scoped dispersion entries.
  array[sigma_y_size] real<lower=0> sigma_y_pop_k;
  array[phi_size] real<lower=0> phi_pop_k;
}

transformed parameters {
  array[K] matrix[n, p] eta_kp;
  array[K, p] vector[J_a_max] a_coef_kp;
  array[K, p] vector[J_b_max] c_b_kp;
  array[K, p] vector[J_b_max] b_coef_kp;

  for (k in 1:K) {
    for (j in 1:p) {
      a_coef_kp[k, j] = rep_vector(0, J_a_max);
      c_b_kp[k, j] = rep_vector(0, J_b_max);
      b_coef_kp[k, j] = rep_vector(0, J_b_max);
    }
  }

  if (any_use_a == 1) {
    for (k in 1:K) {
      if (use_a_k[k] == 1) {
        for (j in 1:p) {
          if (J_a_free_kp[k, j] > 0) {
            for (jj in 1:J_a_free_kp[k, j]) {
              a_coef_kp[k, j][jj] = a_raw[a_raw_offset_kp[k, j] + jj]
                                      * sigma_a_k[sigma_a_idx[k]];
            }
          }
        }
      }
    }
  }

  if (any_use_b == 1) {
    for (k in 1:K) {
      if (use_b_k[k] == 1) {
        for (j in 1:p) {
          if (J_b_free_kp[k, j] > 0) {
            for (jj in 1:J_b_free_kp[k, j]) {
              c_b_kp[k, j][jj] = c_b_kp_raw[c_b_raw_offset_kp[k, j] + jj]
                                  * sigma_b_k[k];
            }
            // b_coef_kp[k, j] = c_b_kp[k, j] / theta_ref_kp[1, k][j]
            // is reported as a derived quantity only in the
            // single-anchor regime (use_groups == 0). With per-group
            // anchors b_coef_kp stays at zero; downstream callers
            // derive it per group from c_b_kp and theta_ref_kp[g, k][j]
            // as in amm_eb_marginal_KxP.stan and amm_distrib_K.stan.
            if (use_groups == 0 && theta_ref_kp[1, k][j] != 0) {
              for (jj in 1:J_b_per_kp[k, j]) {
                b_coef_kp[k, j][jj] = c_b_kp[k, j][jj]
                                        / theta_ref_kp[1, k][j];
              }
            }
          }
        }
      }
    }
  }

  for (k in 1:K) {
    for (i in 1:n) {
      int g_i = group_id[i];
      for (j in 1:p) {
        real theta_ref_ikj = theta_ref_kp[g_i, k][j];
        real eta_ikj = theta_ref_ikj;
        if (use_a_k[k] == 1 && J_a_per_kp[k, j] > 0) {
          eta_ikj += dot_product(
            to_vector(Z_a_kp[k, j][i, 1:J_a_per_kp[k, j]]),
            a_coef_kp[k, j][1:J_a_per_kp[k, j]]
          );
        }
        if (use_b_k[k] == 1 && J_b_per_kp[k, j] > 0) {
          eta_ikj += dot_product(
            to_vector(Z_b_kp[k, j][i, 1:J_b_per_kp[k, j]]),
            c_b_kp[k, j][1:J_b_per_kp[k, j]]
          );
        }
        if (use_W == 1 && dim_W > 0 && d > 0 && W_per_kj_dim > 0) {
          // W is globally shared across slots and coords. The total
          // W_raw row count is K * p * W_per_kj_dim; the (k, j) block
          // starts at row ((k - 1) * p + (j - 1)) * W_per_kj_dim + 1.
          vector[d] W_diff_x_kj = rep_vector(0, d);
          vector[W_per_kj_dim] basis_diff_kj = apply_W_basis_diff(
            W_type_id, theta_ref_ikj, theta_anchor_kp[k][j],
            W_per_kj_dim, W_degree, W_n_knots_full, W_knots_full
          );
          int row_start = ((k - 1) * p + (j - 1)) * W_per_kj_dim + 1;
          for (jj_local in 1:W_per_kj_dim) {
            int row = row_start + jj_local - 1;
            for (jjj in 1:d) {
              W_diff_x_kj[jjj] += basis_diff_kj[jj_local]
                                    * W_raw[row, jjj]{{W_SCALE}};
            }
          }
          eta_ikj += dot_product(W_diff_x_kj, to_vector(X[i]));
        }
        eta_kp[k][i, j] = eta_ikj;
      }
    }
  }
}

model {
  // Per-slot per-coord anchor priors. Hierarchical when use_groups
  // == 1; flat single-anchor coord-wise i.i.d. otherwise.
  // THETA_REF_PRIOR_BLOCK is the family-specific block emitted by
  // R/stan_codegen.R via the .gdpar_build_theta_ref_prior_block_KxP()
  // helper (Tweedie K=3 with bounded p slot via slice notation; other
  // families keep the canonical vectorized block).
  {{THETA_REF_PRIOR_BLOCK}}

  // Per-slot scale priors (NCP-only B9.5; per-slot per-coord CP /
  // mixed deferred to B9.6+).
  if (any_use_a == 1) {
    sigma_a_k ~ {{PRIOR_SIGMA_A}};
    a_raw ~ normal(0, 1);
  }
  if (any_use_b == 1) {
    sigma_b_k ~ {{PRIOR_SIGMA_B}};
    c_b_kp_raw ~ normal(0, 1);
  }
  if (use_W == 1 && dim_W > 0) {
    sigma_W[1] ~ {{PRIOR_SIGMA_W}};
    to_vector(W_raw) ~ {{W_PRIOR}};
  }
  if (sigma_y_size > 0) {
    sigma_y_pop_k ~ {{PRIOR_SIGMA_Y}};
  }
  if (phi_size > 0) {
    phi_pop_k ~ {{PRIOR_PHI}};
  }

  // Likelihood: family dispatch + coord-wise factorization.
  // family_id_k_vector[1] selects the canonical Path B family; the
  // slot dispatch composes apply_inv_link_by_id(inv_link_id_per_slot[2],
  // eta_kp[2][i, j]) for K=2 location families {1, 3, 5, 6, 7}; the
  // K=3 families (8 Student-t, 9 Tweedie, 11 ZINB, 13 Hurdle-NB)
  // consume eta_kp[3] explicitly per their canonical structure.
  if (family_id_k_vector[1] == 1) {
    // Gaussian K=2 per coord.
    for (i in 1:n) {
      for (j in 1:p) {
        real sigma_ij = apply_inv_link_by_id(inv_link_id_per_slot[2],
                                              eta_kp[2][i, j]);
        y_real[i, j] ~ normal(eta_kp[1][i, j], sigma_ij);
      }
    }
  } else if (family_id_k_vector[1] == 3) {
    // NB K=2 per coord.
    for (i in 1:n) {
      for (j in 1:p) {
        real phi_ij = apply_inv_link_by_id(inv_link_id_per_slot[2],
                                            eta_kp[2][i, j]);
        y_int[i, j] ~ neg_binomial_2(exp(eta_kp[1][i, j]), phi_ij);
      }
    }
  } else if (family_id_k_vector[1] == 5) {
    // Beta K=2 per coord.
    for (i in 1:n) {
      for (j in 1:p) {
        real phi_ij = apply_inv_link_by_id(inv_link_id_per_slot[2],
                                            eta_kp[2][i, j]);
        y_real[i, j] ~ beta_proportion(inv_logit(eta_kp[1][i, j]),
                                        phi_ij);
      }
    }
  } else if (family_id_k_vector[1] == 6) {
    // Gamma K=2 per coord.
    for (i in 1:n) {
      for (j in 1:p) {
        real mu_ij    = exp(eta_kp[1][i, j]);
        real shape_ij = apply_inv_link_by_id(inv_link_id_per_slot[2],
                                              eta_kp[2][i, j]);
        y_real[i, j] ~ gamma(shape_ij, shape_ij / mu_ij);
      }
    }
  } else if (family_id_k_vector[1] == 7) {
    // Lognormal_loc_scale K=2 per coord.
    for (i in 1:n) {
      for (j in 1:p) {
        real sigma_ij = apply_inv_link_by_id(inv_link_id_per_slot[2],
                                              eta_kp[2][i, j]);
        y_real[i, j] ~ lognormal(eta_kp[1][i, j], sigma_ij);
      }
    }
  } else if (family_id_k_vector[1] == 8) {
    // Student-t K=3 per coord.
    for (i in 1:n) {
      for (j in 1:p) {
        y_real[i, j] ~ student_t(exp(eta_kp[3][i, j]),
                                  eta_kp[1][i, j],
                                  exp(eta_kp[2][i, j]));
      }
    }
  } else if (family_id_k_vector[1] == 9) {
    // Tweedie K=3 per coord.
    for (i in 1:n) {
      for (j in 1:p) {
        y_real[i, j] ~ tweedie(exp(eta_kp[1][i, j]),
                                exp(eta_kp[2][i, j]),
                                eta_kp[3][i, j]);
      }
    }
  } else if (family_id_k_vector[1] == 10) {
    // ZIP K=2 per coord.
    for (i in 1:n) {
      for (j in 1:p) {
        real eta_mu_ij = eta_kp[1][i, j];
        real eta_pi_ij = eta_kp[2][i, j];
        if (y_int[i, j] == 0) {
          target += log_sum_exp(
            bernoulli_logit_lpmf(1 | eta_pi_ij),
            bernoulli_logit_lpmf(0 | eta_pi_ij)
              + poisson_log_lpmf(0 | eta_mu_ij)
          );
        } else {
          target += bernoulli_logit_lpmf(0 | eta_pi_ij)
                  + poisson_log_lpmf(y_int[i, j] | eta_mu_ij);
        }
      }
    }
  } else if (family_id_k_vector[1] == 11) {
    // ZINB K=3 per coord.
    for (i in 1:n) {
      for (j in 1:p) {
        real eta_mu_ij = eta_kp[1][i, j];
        real phi_ij    = exp(eta_kp[2][i, j]);
        real eta_pi_ij = eta_kp[3][i, j];
        if (y_int[i, j] == 0) {
          target += log_sum_exp(
            bernoulli_logit_lpmf(1 | eta_pi_ij),
            bernoulli_logit_lpmf(0 | eta_pi_ij)
              + neg_binomial_2_log_lpmf(0 | eta_mu_ij, phi_ij)
          );
        } else {
          target += bernoulli_logit_lpmf(0 | eta_pi_ij)
                  + neg_binomial_2_log_lpmf(y_int[i, j] | eta_mu_ij,
                                              phi_ij);
        }
      }
    }
  } else if (family_id_k_vector[1] == 12) {
    // Hurdle-Poisson K=2 per coord.
    for (i in 1:n) {
      for (j in 1:p) {
        real eta_mu_ij = eta_kp[1][i, j];
        real eta_pi_ij = eta_kp[2][i, j];
        if (y_int[i, j] == 0) {
          target += bernoulli_logit_lpmf(1 | eta_pi_ij);
        } else {
          target += bernoulli_logit_lpmf(0 | eta_pi_ij)
                  + poisson_log_lpmf(y_int[i, j] | eta_mu_ij)
                  - log1m_exp(-exp(eta_mu_ij));
        }
      }
    }
  } else if (family_id_k_vector[1] == 13) {
    // Hurdle-NB K=3 per coord.
    for (i in 1:n) {
      for (j in 1:p) {
        real eta_mu_ij = eta_kp[1][i, j];
        real phi_ij    = exp(eta_kp[2][i, j]);
        real eta_pi_ij = eta_kp[3][i, j];
        if (y_int[i, j] == 0) {
          target += bernoulli_logit_lpmf(1 | eta_pi_ij);
        } else {
          target += bernoulli_logit_lpmf(0 | eta_pi_ij)
                  + neg_binomial_2_log_lpmf(y_int[i, j] | eta_mu_ij,
                                              phi_ij)
                  - log1m_exp(neg_binomial_2_log_lpmf(0 | eta_mu_ij,
                                                      phi_ij));
        }
      }
    }
  }
}

generated quantities {
  // Per-(observation, coord) log-likelihood, fitted linear predictors
  // (location slot), and posterior-predictive draws. The shape
  // mirrors amm_eb_marginal_KxP.stan extended to the Path B family
  // set of amm_distrib_K.stan.
  matrix[n, p] log_lik;
  matrix[n, p] theta_i;
  matrix[n, p] y_pred;

  for (i in 1:n) {
    for (j in 1:p) {
      if (family_id_k_vector[1] == 1) {
        real sigma_ij = apply_inv_link_by_id(inv_link_id_per_slot[2],
                                              eta_kp[2][i, j]);
        theta_i[i, j] = eta_kp[1][i, j];
        log_lik[i, j] = normal_lpdf(y_real[i, j] | eta_kp[1][i, j],
                                     sigma_ij);
        y_pred[i, j] = normal_rng(eta_kp[1][i, j], sigma_ij);
      } else if (family_id_k_vector[1] == 3) {
        real mu_ij  = exp(eta_kp[1][i, j]);
        real phi_ij = apply_inv_link_by_id(inv_link_id_per_slot[2],
                                            eta_kp[2][i, j]);
        theta_i[i, j] = eta_kp[1][i, j];
        log_lik[i, j] = neg_binomial_2_lpmf(y_int[i, j] | mu_ij,
                                             phi_ij);
        y_pred[i, j]  = neg_binomial_2_rng(mu_ij, phi_ij);
      } else if (family_id_k_vector[1] == 5) {
        real mu_ij  = inv_logit(eta_kp[1][i, j]);
        real phi_ij = apply_inv_link_by_id(inv_link_id_per_slot[2],
                                            eta_kp[2][i, j]);
        theta_i[i, j] = eta_kp[1][i, j];
        log_lik[i, j] = beta_proportion_lpdf(y_real[i, j] | mu_ij,
                                              phi_ij);
        y_pred[i, j]  = beta_proportion_rng(mu_ij, phi_ij);
      } else if (family_id_k_vector[1] == 6) {
        real mu_ij    = exp(eta_kp[1][i, j]);
        real shape_ij = apply_inv_link_by_id(inv_link_id_per_slot[2],
                                              eta_kp[2][i, j]);
        theta_i[i, j] = eta_kp[1][i, j];
        log_lik[i, j] = gamma_lpdf(y_real[i, j] | shape_ij,
                                    shape_ij / mu_ij);
        y_pred[i, j]  = gamma_rng(shape_ij, shape_ij / mu_ij);
      } else if (family_id_k_vector[1] == 7) {
        real sigma_ij = apply_inv_link_by_id(inv_link_id_per_slot[2],
                                              eta_kp[2][i, j]);
        theta_i[i, j] = eta_kp[1][i, j];
        log_lik[i, j] = lognormal_lpdf(y_real[i, j] | eta_kp[1][i, j],
                                        sigma_ij);
        y_pred[i, j]  = lognormal_rng(eta_kp[1][i, j], sigma_ij);
      } else if (family_id_k_vector[1] == 8) {
        real nu_ij    = exp(eta_kp[3][i, j]);
        real mu_ij    = eta_kp[1][i, j];
        real sigma_ij = exp(eta_kp[2][i, j]);
        theta_i[i, j] = mu_ij;
        log_lik[i, j] = student_t_lpdf(y_real[i, j] | nu_ij, mu_ij,
                                        sigma_ij);
        y_pred[i, j]  = student_t_rng(nu_ij, mu_ij, sigma_ij);
      } else if (family_id_k_vector[1] == 9) {
        real mu_ij  = exp(eta_kp[1][i, j]);
        real phi_ij = exp(eta_kp[2][i, j]);
        real p_ij   = eta_kp[3][i, j];
        theta_i[i, j] = eta_kp[1][i, j];
        log_lik[i, j] = tweedie_lpdf(y_real[i, j] | mu_ij, phi_ij,
                                      p_ij);
        y_pred[i, j]  = tweedie_rng(mu_ij, phi_ij, p_ij);
      } else if (family_id_k_vector[1] == 10) {
        real eta_mu_ij = eta_kp[1][i, j];
        real eta_pi_ij = eta_kp[2][i, j];
        theta_i[i, j] = eta_mu_ij;
        if (y_int[i, j] == 0) {
          log_lik[i, j] = log_sum_exp(
            bernoulli_logit_lpmf(1 | eta_pi_ij),
            bernoulli_logit_lpmf(0 | eta_pi_ij)
              + poisson_log_lpmf(0 | eta_mu_ij)
          );
        } else {
          log_lik[i, j] = bernoulli_logit_lpmf(0 | eta_pi_ij)
                        + poisson_log_lpmf(y_int[i, j] | eta_mu_ij);
        }
        if (bernoulli_logit_rng(eta_pi_ij) == 1) {
          y_pred[i, j] = 0;
        } else {
          y_pred[i, j] = poisson_log_rng(eta_mu_ij);
        }
      } else if (family_id_k_vector[1] == 11) {
        real eta_mu_ij = eta_kp[1][i, j];
        real phi_ij    = exp(eta_kp[2][i, j]);
        real eta_pi_ij = eta_kp[3][i, j];
        theta_i[i, j] = eta_mu_ij;
        if (y_int[i, j] == 0) {
          log_lik[i, j] = log_sum_exp(
            bernoulli_logit_lpmf(1 | eta_pi_ij),
            bernoulli_logit_lpmf(0 | eta_pi_ij)
              + neg_binomial_2_log_lpmf(0 | eta_mu_ij, phi_ij)
          );
        } else {
          log_lik[i, j] = bernoulli_logit_lpmf(0 | eta_pi_ij)
                        + neg_binomial_2_log_lpmf(y_int[i, j]
                                                    | eta_mu_ij,
                                                      phi_ij);
        }
        if (bernoulli_logit_rng(eta_pi_ij) == 1) {
          y_pred[i, j] = 0;
        } else {
          y_pred[i, j] = neg_binomial_2_log_rng(eta_mu_ij, phi_ij);
        }
      } else if (family_id_k_vector[1] == 12) {
        real eta_mu_ij = eta_kp[1][i, j];
        real eta_pi_ij = eta_kp[2][i, j];
        theta_i[i, j] = eta_mu_ij;
        if (y_int[i, j] == 0) {
          log_lik[i, j] = bernoulli_logit_lpmf(1 | eta_pi_ij);
        } else {
          log_lik[i, j] = bernoulli_logit_lpmf(0 | eta_pi_ij)
                        + poisson_log_lpmf(y_int[i, j] | eta_mu_ij)
                        - log1m_exp(-exp(eta_mu_ij));
        }
        if (bernoulli_logit_rng(eta_pi_ij) == 1) {
          y_pred[i, j] = 0;
        } else {
          int y_draw = 0;
          int iter_h = 0;
          while (y_draw == 0 && iter_h < 10000) {
            y_draw = poisson_log_rng(eta_mu_ij);
            iter_h = iter_h + 1;
          }
          y_pred[i, j] = y_draw;
        }
      } else if (family_id_k_vector[1] == 13) {
        real eta_mu_ij = eta_kp[1][i, j];
        real phi_ij    = exp(eta_kp[2][i, j]);
        real eta_pi_ij = eta_kp[3][i, j];
        theta_i[i, j] = eta_mu_ij;
        if (y_int[i, j] == 0) {
          log_lik[i, j] = bernoulli_logit_lpmf(1 | eta_pi_ij);
        } else {
          log_lik[i, j] = bernoulli_logit_lpmf(0 | eta_pi_ij)
                        + neg_binomial_2_log_lpmf(y_int[i, j]
                                                    | eta_mu_ij,
                                                      phi_ij)
                        - log1m_exp(neg_binomial_2_log_lpmf(0
                                                          | eta_mu_ij,
                                                            phi_ij));
        }
        if (bernoulli_logit_rng(eta_pi_ij) == 1) {
          y_pred[i, j] = 0;
        } else {
          int y_draw = 0;
          int iter_h = 0;
          while (y_draw == 0 && iter_h < 10000) {
            y_draw = neg_binomial_2_log_rng(eta_mu_ij, phi_ij);
            iter_h = iter_h + 1;
          }
          y_pred[i, j] = y_draw;
        }
      } else {
        theta_i[i, j] = eta_kp[1][i, j];
        log_lik[i, j] = negative_infinity();
        y_pred[i, j] = 0;
      }
    }
  }
}
