#' Automatically compute effect sizes from a well formatted dataset
#'
#' @param x a well formatted dataset
#' @param measure the effect size measure that will be estimated from the information stored in the dataset. See details.
#' @param main_es a logical variable indicating whether a main effect size should be selected when overlapping data are present. See details.
#' @param split_adjusted a logical value indicating whether crude and adjusted effect sizes should be presented separately. See details.
#' @param format_adjusted presentation format of the adjusted effect sizes. See details.
#' @param hierarchy a character string indicating the hierarchy in the information to be prioritized for the effect size calculations. See details.
#' @param selection_auto a character string giving details on the best "auto" hierarchy to use (only useful when \code{hierarchy="auto"} and \code{measure= "d", "g" or "md"}). See details.
#' @param es_selected the method used to select the main effect size when several information allows to estimate an effect size for the same association/comparison. Must be either "minimum" (the smallest effect size will be selected), "maximum" (the largest effect size will be selected) or "hierarchy" (the effect size computed from the information specified highest in the hierarchy will be selected). See details.
#' @param table_2x2_to_cor formula used to obtain a correlation coefficient from the contingency table. For now only 'tetrachoric' is available.
#' @param rr_to_or formula used to convert the \code{rr} value into an odds ratio.
#' @param or_to_rr formula used to convert the \code{or} value into a risk ratio.
#' @param or_to_cor formula used to convert the \code{or} value into a correlation coefficient.
#' @param pre_post_to_smd formula used to obtain a SMD from pre/post means and SD of two independent groups.
#' @param pool_sd a logical value indicating whether the standardizing SD should be pooled across the two
#'   groups (default \code{FALSE}). The two options target the same estimand when the arms' true SDs are
#'   equal (as randomization implies at baseline) and differ otherwise; the literature does not agree on
#'   which to prefer, so this is a deliberate choice and not a technical detail.
#'   \itemize{
#'     \item \code{FALSE} (default): each arm's change is standardized by that arm's OWN SD and the two
#'       within-group values are subtracted, their variances adding because the arms are independent. This
#'       is Morris's (2008) \eqn{d_{ppc1}}, from Becker (1988). It makes no assumption that the arms' true
#'       SDs are equal, and Viechtbauer (see the metafor-project Morris 2008 page) describes it as the more
#'       broadly applicable of the two.
#'     \item \code{TRUE}: the difference in mean change is divided by a single SD pooled across arms. This
#'       is Morris's (2008) \eqn{d_{ppc2}} (his eq. 8-9), which he recommends: it is more efficient, but it
#'       assumes the two arms' true standardizing SDs are equal.
#'   }
#' @param r_pre_post pre-post correlation across the two groups (use this argument only if the precise correlation in each group is unknown). Note that when this correlation is defaulted rather than reported, the pre/post standard errors and confidence intervals are unreliable: at a true correlation of 0.3 with the default 0.8, the reported pre/post variance is about 68% too small (95% CI coverage ~0.75). Supply \code{r_pre_post_exp}/\code{r_pre_post_nexp} when available, or run a sensitivity analysis over plausible values.
#' @param smd_to_cor formula used to convert the \code{cohen_d} value into a coefficient correlation.
#' @param smd_var name of the sampling-variance formula for the standardized mean difference: "borenstein" (default) or "hedges_olkin" (alias "viechtbauer"). The two differ by a squared small-sample-correction factor (J^2); "hedges_olkin" is a few percent larger at small samples.
#' @param smd_denom standardizer for the standardized mean difference. "pooled" (default) uses the pooled endpoint SD (Cohen's d / Hedges' g); "glass" (alias "control") uses the control (non-experimental) endpoint SD (Glass's delta); "glass_robust" (alias "control_robust") is Glass's delta with a heteroscedasticity-consistent sampling variance. Only the endpoint means family (es_from_means_sd/se/ci) honours this argument: rows whose effect size comes from any other method (t/F, cohen_d/hedges_g, eta-squared, point-biserial r, medians/ranges, plots, ANCOVA, raw mean differences) always use the pooled-SD standardizer, and a message lists the scoping when a non-pooled value is requested ("glass_robust" additionally has a single variance form, so smd_var is ignored for it).
#' @param cor_to_smd formula used to convert a correlation coefficient value into a SMD.
#' @param prop_to_es method used to compute the effect size from the proportion. Must be either "raw", "logit" or "freeman_tukey" (see \code{\link{es_from_prop_single_group}}).
#' @param alpha_to_es method used to compute the effect size from Cronbach's alpha. Must be either "bonett" or "raw" (see \code{\link{es_from_cronbach_alpha}}).
#' @param icc_to_es method used to compute the effect size from an ICC. Must be either "bonett" or "raw" (see \code{\link{es_from_icc}}).
#' @param yates_chisq a logical value indicating whether the Chi square has been performed using Yates' correction for continuity. Can also be given as a column of the dataset when studies differ (rows left NA use this argument).
#' @param unit_type the type of unit for the \code{unit_increase_iv} argument. Must be either "sd" or "value" (see \code{\link{es_from_pearson_r}}).
#' @param max_asymmetry A percentage indicating the tolerance before detecting asymmetry in the 95% CI bounds. Asymmetric CIs are only flagged, not modified (see \code{\link{summary.metaConvert}}).
#' @param verbose a logical variable indicating whether text outputs and messages should be generated. We recommend turning this option to FALSE only after having carefully read all the generated messages.
#' @param correct_inputs a logical value indicating whether invalid input values (negative SDs, inverted CI bounds, etc.) should be set to NA before the calculations (default TRUE). If FALSE, invalid values are only flagged in the 'es_flags' column and kept in the data.
#' @param flag_options a named list of thresholds used by the quality flags (see \code{\link{summary.metaConvert}} for the list of options and defaults).
#'
#' @details
#' This function automatically computes or converts between 14 effect sizes
#' measures from any relevant type of input data stored in the
#' dataset you pass to this function.
#'
#' ## Input validation
#' Before the calculations, the input data are checked (negative sample
#' sizes/SD/SE/p-values, inverted CI bounds, values outside their CI,
#' asymmetric CI). By default invalid values are set to NA; issues appear
#' in the 'es_flags' column of summary().
#'
#' ## Effect size measures
#' Possible effect size measures are:
#' 1. Cohen's d ("d")
#' 2. Hedges' g ("g")
#' 3. mean difference ("md")
#' 4. (log) odds ratio ("or" and "logor")
#' 5. (log) risk ratio ("rr" and "logrr")
#' 6. (log) incidence rate ratio ("irr" and "logirr")
#' 7. correlation coefficient ("r")
#' 8. transformed r-to-z correlation coefficient ("z")
#' 9. partial correlation coefficient ("rp")
#' 10. Fisher's z of partial correlation ("zp")
#' 11. log variability ratio ("logvr")
#' 12. log coefficient of variation ("logcvr")
#' 13. number needed to treat ("nnt")
#' 14. risk difference ("rd")
#'
#' ## Computation of a main effect size
#' If you enter multiple types of input data
#' (e.g., means/sd of two groups and a student t-test value)
#' for the same comparison i.e., for the same row of the dataset,
#' the \code{convert_df()} function can have two behaviours.
#' If you set:
#' - \code{main_es = FALSE} the function will estimate all possible effect sizes from all
#' types of input data (which implies that if a comparison has **several types of input data**,
#' it will result in **multiple rows** in the dataframe returned by the function)
#' - \code{main_es = TRUE} the function will select one effect size per comparison
#' (which implies that if a comparison has **several types of input data**,
#' it will result in a **unique row** in the dataframe returned by the function)
#'
#' ## Selection of input data for the computation of the main effect size
#' If you choose to estimate one main effect size (i.e., by setting \code{main_es = TRUE}),
#' you have several options to select this main effect size.
#' If you set:
#' - \code{es_selected = "auto"}: the main effect size will be **automatically** selected, by prioritizing
#' specific types of input data over other (see next section "Hierarchy").
#' - \code{es_selected = "hierarchy"}: the main effect size will be selected, by prioritizing
#' specific types of input data over other (see next section "Hierarchy").
#' - \code{es_selected = "minimum"}: the main effect size will be selected, by selecting
#' the lowest effect size available.
#' - \code{es_selected = "maximum"}: the main effect size will be selected, by selecting
#' the highest effect size available.
#'
#' ## Hierarchy
#' More than 70 different combinations of input data can be used to estimate an effect size.
#' You can retrieve the effect size measures estimated by each combination of input data
#' in the \code{\link{see_input_data}()} function and online \code{https://metaconvert.org/input.html}.
#'
#' You have two options to use a hierarchy in the types of input data.
#' * an automatic way (\code{es_selected = "auto"})
#' * an manual way (\code{es_selected = "hierarchy"})
#'
#' ### Automatic
#' If you select an automatic hierarchy, here are the types of input data that will be prioritized.
#'
#' #### Crude SMD or MD (\code{measure=c("d", "g", "md")} and \code{selection_auto="crude"})
#' 1. User's input effect size value
#' 2. SMD value
#' 3. Means at post-test
#' 4. ANOVA/Student's t-test/point biserial correlation statistics
#' 5. Linear regression estimates
#' 6. Mean difference values
#' 7. Quartiles/median/maximum values
#' 8. Post-test means extracted from a plot
#' 9. Pre-test+post-test means or mean change
#' 10. Paired ANOVA/t-test statistics
#' 11. Odds ratio value
#' 12. Contingency table
#' 13. Correlation coefficients
#' 14. Phi/chi-square value
#'
#' #### Paired SMD or MD (\code{measure=c("d", "g", "md")} and \code{selection_auto="paired"})
#' 1. User's input effect size value
#' 2. Paired SMD value
#' 3. Pre-test+post-test means or mean change
#' 4. Paired ANOVA/t-test statistics
#' 5. Means at post-test
#' 6. ANOVA/Student's t-test/point biserial correlation
#' 7. Linear regression estimates
#' 8. Mean difference values
#' 9. Quartiles/median/maximum values
#' 10. Odds ratio value
#' 11. Contingency table
#' 12. Correlation coefficients
#' 13. Phi/chi-square value
#'
#' #### Adjusted SMD or MD (\code{measure=c("d", "g", "md")} and \code{selection_auto="adjusted"})
#' 1. User's input adjusted effect size value
#' 2. Adjusted SMD value
#' 3. Estimated marginal means from ANCOVA
#' 4. F- or t-test value from ANCOVA
#' 5. Adjusted mean difference from ANCOVA
#' 6. Estimated marginal means from ANCOVA extracted from a plot
#'
#' #### Odds Ratio (\code{measure=c("or")})
#' 1.	User's input effect size value
#' 2.	Odds ratio value
#' 3.	Contingency table
#' 4.	Risk ratio values
#' 5.	Phi/chi-square value
#' 6.	Correlation coefficients
#' 7.	(Then hierarchy as for "d" or "g" option crude)
#'
#' #### Risk Ratio (\code{measure=c("rr")})
#' 1.	User's input effect size value
#' 2.	Risk ratio values
#' 3.	Contingency table
#' 4.	Odds ratio values
#' 5.	Phi/chi-square value
#'
#' #### Incidence rate ratio (\code{measure=c("irr")})
#' 1.	User's input effect size value
#' 2.	Number of cases and time of disease free observation time
#'
#' #### Correlation (\code{measure=c("r", "z")})
#' 1. User's input effect size value
#' 2. Correlation coefficients
#' 3. Contingency table
#' 4. Odds ratio value
#' 5. Phi/chi-square value
#' 6. SMD value
#' 7. Means at post-test
#' 8. ANOVA/Student's t-test/point biserial correlation
#' 9. Linear regression estimates
#' 10. Mean difference values
#' 11 Quartiles/median/maximum values
#' 12. Post-test means extracted from a plot
#' 13. Pre-test+post-test means or mean change
#' 14. Paired ANOVA/t-test
#'
#' #### Variability ratios (\code{measure=c("vr", "cvr")})
#' 1.	User's input effect size value
#' 2. means/variability indices at post-test
#' 3. means/variability indices at post-test extracted from a plot
#'
#' #### Number needed to treat (\code{measure=c("nnt")})
#' 1.	User's input effect size value
#' 2.	Contingency table
#' 3.	Odds ratio values
#' 4.	Risk ratio values
#' 5.	Incidence rate ratio (person-time NNT, requires baseline_rate)
#' 6.	Phi/chi-square value
#'
#' NNT values should not be pooled directly (the NNT confidence interval is
#' disjoint when the risk difference crosses zero). You should pool RD/OR/RR
#' values and convert the pooled estimate to NNT (Deeks, 2002; Cochrane
#' Handbook, Chapter 15).
#'
#' #### Risk difference (\code{measure=c("rd")})
#' 1.	User's input effect size value
#' 2.	Contingency table
#' 3.	Odds ratio values
#' 4.	Risk ratio values
#' 5.	Incidence rate ratio (requires baseline_rate)
#' 6.	Phi/chi-square value
#'
#' ### Manual
#'
#' If you select a manual hierarchy, you can specify the order in which you want to
#' use each type of input data.
#' You can prioritize some types of input data by placing them at the begining of the
#' hierarchy argument, and you must separate all input data with a ">" separator.
#' For example, if you set:
#' - \code{hierarchy = "means_sd > means_se > student_t"}, the convert_df function will prioritize
#' the means + SD, then the means + SE, then the Student's t-test to estimate the main effect
#' size.
#' - \code{hierarchy = "2x2 > or_se > phi"}, the convert_df function will prioritize
#' the contingency table, then the odds ratio value + SE, then the phi coefficient to estimate
#' the main effect size.
#'
#' Importantly, if none of the types of input data indicated in the \code{hierarchy} argument
#' can be used to estimate the target effect size measure,
#' the \code{convert_df()} function will automatically try to use other types of input
#' data to estimate an effect size.
#'
#' ## Adjusted effect sizes
#' Some datasets will be composed of crude (i.e., non-adjusted) types of input data
#' (such as standard means + SD, Student's t-test, etc.) and adjusted types of input data
#' (such as means + SE from an ANCOVA model, a t-test from an ANCOVA, etc.).
#'
#' In these situations, you can decide to:
#' - treat crude and adjusted input data the same way \code{split_adjusted = FALSE}
#' - split calculations for crude and adjusted types of input data \code{split_adjusted = TRUE}
#'
#' If you want to split the calculations, you can decide to present the final dataset:
#' - in a long format (i.e., crude and adjusted effect sizes presented in separate rows \code{format_adjusted = "long"})
#' - in a wide format (i.e., crude and adjusted effect sizes presented in separate columns \code{format_adjusted = "wide"})
#'
#' @return
#' The \code{convert_df()} function returns a list of
#' more than 70 dataframes
#' (one for each function automatically applied to the dataset).
#' These dataframes systematically contain the columns described in
#' \code{\link{metaConvert-package}}.
#' The list of dataframes can be easily converted to a single,
#' calculations-ready dataframe
#' using the summary function (see \code{\link{summary.metaConvert}}).
#'
#' @md
#'
#' @export convert_df
#'
#' @examples
#' res <- convert_df(df.haza,
#'   measure = "g",
#'   split_adjusted = TRUE,
#'   es_selected = "minimum",
#'   format_adjusted = "long"
#' )
#' summary(res)
convert_df <- function(x, measure = c("d", "g", "md", "logor", "logrr", "logirr",
                                      "nnt", "rd", "r", "z", "rp", "zp", "logvr", "logcvr"),
                       main_es = TRUE,
                       es_selected = c("auto", "hierarchy", "minimum", "maximum"),
                       selection_auto = c("crude", "paired", "adjusted"),
                       split_adjusted = TRUE,
                       format_adjusted = c("wide", "long"),
                       verbose = TRUE,
                       max_asymmetry = 50,
                       hierarchy = "means_sd > means_se > means_ci",
                       table_2x2_to_cor = "tetrachoric",
                       rr_to_or = "metaumbrella",
                       or_to_rr = "metaumbrella_cases",
                       or_to_cor = "bonett",
                       smd_to_cor = "viechtbauer",
                       smd_var = "borenstein",
                       smd_denom = "pooled",
                       pre_post_to_smd = "bonett",
                       r_pre_post = 0.8,
                       cor_to_smd = "viechtbauer",
                       unit_type = "raw_scale",
                       yates_chisq = FALSE,
                       pool_sd = FALSE,
                       prop_to_es = "raw",
                       alpha_to_es = "bonett",
                       icc_to_es = "bonett",
                       correct_inputs = TRUE,
                       flag_options = list()) {
  # @param table_2x2_to_cor formula used to obtain a correlation coefficient from the contingency table.
  # table_2x2_to_cor = "tetrachoric",
  # x = dat
  # measure = "nnt";
  # main_es = TRUE;
  # es_selected = "hierarchy";
  # split_adjusted = TRUE;
  # format_adjusted = "wide";
  # verbose = TRUE;
  # hierarchy = "means_sd > means_se > means_ci";
  # rr_to_or = "metaumbrella";
  # or_to_rr = "dipietrantonj";
  # or_to_cor = "bonett";
  # table_2x2_to_cor = "lipsey";
  # smd_to_cor = "viechtbauer";
  # chisq_to_cor = "tetrachoric";
  # phi_to_cor = "tetrachoric";
  # pre_post_to_smd = "bonett";
  # cor_to_smd = "viechtbauer";
  # unit_type = "raw_scale";
  # yates_chisq = FALSE
  # max_asymmetry = 5
  # selection_auto = c("crude", "paired", "adjusted")
  measure = measure[1]
  es_selected = es_selected[1]
  selection_auto = selection_auto[1]
  format_adjusted = format_adjusted[1]

  x <- .check_data(x, split_adjusted = split_adjusted,
                   main_es = main_es, format = format_adjusted)
  # rows with defaulted r_pre_post
  .r_defaulted_exp <- is.na(x[, "r_pre_post_exp"])
  .r_defaulted_nexp <- is.na(x[, "r_pre_post_nexp"])
  x[.r_defaulted_exp, "r_pre_post_exp"] <- r_pre_post
  x[.r_defaulted_nexp, "r_pre_post_nexp"] <- r_pre_post

  # An unreported r that is silently imputed is a substantive assumption. How it
  # bites depends on the standardizer: for change-SD methods (morris_dz, cooper/
  # morris_drm) the assumed r scales the POINT estimate; for baseline-SD (bonett) and
  # average-SD (morris_dav) the point estimate is r-free and only the SE/CI move.
  # Warn once, and only for rows that actually feed an r-consuming (pre/post, mean-
  # change, paired) route -- datasets with no such data never reach the formulas.
  if (verbose) {
    .r_consuming_cols <- c(
      "mean_pre_exp", "mean_pre_nexp", "mean_pre_sd_exp", "mean_pre_sd_nexp",
      "mean_change_exp", "mean_change_nexp",
      "mean_change_sd_exp", "mean_change_sd_nexp",
      "mean_change_se_exp", "mean_change_se_nexp",
      "mean_change_pval_exp", "mean_change_pval_nexp",
      "paired_t_exp", "paired_t_nexp", "paired_f_exp", "paired_f_nexp",
      "paired_t_pval_exp", "paired_t_pval_nexp",
      "paired_f_pval_exp", "paired_f_pval_nexp"
    )
    .r_consuming_cols <- intersect(.r_consuming_cols, colnames(x))
    if (length(.r_consuming_cols) > 0) {
      .rows_with_pp <- rowSums(!is.na(x[, .r_consuming_cols, drop = FALSE])) > 0
      .n_imputed <- sum((.r_defaulted_exp | .r_defaulted_nexp) & .rows_with_pp)
      if (.n_imputed > 0) {
        # Change-SD standardizers make the point estimate itself depend on r; the
        # mean-change and paired-t/F routes are always standardized by the change SD
        # (coerced to cooper), so their point estimates depend on r regardless of the
        # requested method.
        .r_scales_point <- pre_post_to_smd %in% c("morris_dz", "morris_drm", "cooper")
        .impact <- if (.r_scales_point) {
          paste0("Under the '", pre_post_to_smd, "' standardizer the assumed\n",
                 "  correlation scales the POINT estimate, not only the SE.")
        } else {
          paste0("Under the '", pre_post_to_smd, "' standardizer the point estimate is\n",
                 "  r-free and only the SE/CI depend on the assumed correlation; note that\n",
                 "  mean-change and paired-t/F rows are standardized by the change SD, whose\n",
                 "  POINT estimate does depend on r.")
        }
        message(
          "Note: r_pre_post was not reported for ", .n_imputed, " row(s) with pre/post, ",
          "mean-change or paired data;\n  the default r_pre_post = ", r_pre_post,
          " was assumed. ", .impact,
          "\n  Supply r_pre_post_exp/r_pre_post_nexp when available, or run a ",
          "sensitivity analysis\n  over plausible values."
        )
      }
    }
  }

  r_pre_post = rep(r_pre_post, nrow(x))
  for (i in c("rr_to_or",
              "or_to_rr",
              "or_to_cor",
              # "table_2x2_to_cor",
              "smd_to_cor",
              "smd_var",
              "smd_denom",
              "pre_post_to_smd",
              "cor_to_smd",
              "unit_type")) {
    if (i %in% colnames(x)) {
      if (length(sapply(mget(i), function(x) x)) > 1) {
        stop("The length of the argument '", i, "' should be 1, or should be specified in a column of the dataset.")
      }
      x[, i][is.na(x[, i])] <- sapply(mget(i), function(x) x)
    } else {
      if (length(sapply(mget(i), function(x) x)) > 1) {
        stop("The length of the argument '", i, "' should be 1, or should be specified in a column of the dataset.")
      }
      x[, i] <- sapply(mget(i), function(x) x)
    }
  }

  # P10: smd_denom is honoured only by the endpoint means family; rows reaching
  # the SMD via any other route keep the pooled-SD standardizer. Say so once,
  # or a "Glass" analysis silently mixes estimands across methods.
  .denom_used <- .normalize_smd_denom(x[, "smd_denom"])
  if (any(!is.na(.denom_used) & .denom_used != "pooled")) {
    message(
      "Note: smd_denom is honoured by the endpoint means family only ",
      "(es_from_means_sd, es_from_means_se, es_from_means_ci).\n  Rows whose effect size ",
      "comes from any other method (t/F statistics, cohen_d/hedges_g, eta-squared,\n  ",
      "point-biserial r, medians/ranges/quartiles, plots, ANCOVA, raw mean differences) ",
      "use the pooled-SD\n  standardizer. Check 'info_used' in summary() to see which ",
      "standardizer each row received."
    )
  }

  # input validation
  enable_info <- if (!is.null(flag_options$enable_informational)) {
    flag_options$enable_informational
  } else {
    .default_flag_options()$enable_informational
  }
  sd_ratio_max_val <- if (!is.null(flag_options$sd_ratio_max)) {
    flag_options$sd_ratio_max
  } else {
    .default_flag_options()$sd_ratio_max
  }
  sd_ratio_bl_ep_min_val <- if (!is.null(flag_options$sd_ratio_bl_ep_min)) {
    flag_options$sd_ratio_bl_ep_min
  } else {
    .default_flag_options()$sd_ratio_bl_ep_min
  }
  baseline_imb_max_val <- if (!is.null(flag_options$baseline_imbalance_max)) {
    flag_options$baseline_imbalance_max
  } else {
    .default_flag_options()$baseline_imbalance_max
  }
  enable_cross_row_val <- if (!is.null(flag_options$enable_cross_row)) {
    flag_options$enable_cross_row
  } else {
    .default_flag_options()$enable_cross_row
  }
  templated_min_match_val <- if (!is.null(flag_options$templated_min_match)) {
    flag_options$templated_min_match
  } else {
    .default_flag_options()$templated_min_match
  }
  paired_as_indep_tol_val <- if (!is.null(flag_options$paired_as_indep_tol)) {
    flag_options$paired_as_indep_tol
  } else {
    .default_flag_options()$paired_as_indep_tol
  }
  validation <- .validate_input_data(x, max_asymmetry = max_asymmetry, verbose = verbose,
                                      enable_informational = enable_info,
                                      sd_ratio_max = sd_ratio_max_val,
                                      sd_ratio_bl_ep_min = sd_ratio_bl_ep_min_val,
                                      baseline_imbalance_max = baseline_imb_max_val,
                                      enable_cross_row = enable_cross_row_val,
                                      templated_min_match = templated_min_match_val,
                                      measure = measure,
                                      paired_as_indep_tol = paired_as_indep_tol_val,
                                      correct_inputs = correct_inputs)
  x <- validation$data

  # V6: Flag rows where r_pre_post used the default value AND pre-post data is present
  .pre_post_cols <- c(
    "mean_pre_exp", "mean_post_exp", "mean_pre_nexp", "mean_post_nexp",
    "mean_change_exp", "mean_change_nexp",
    "mean_pre_single_group", "mean_post_single_group",
    "mean_change_single_group", "paired_t", "paired_f"
  )
  .pre_post_cols <- intersect(.pre_post_cols, colnames(x))
  .has_pre_post <- if (length(.pre_post_cols) > 0) {
    rowSums(!is.na(x[, .pre_post_cols, drop = FALSE])) > 0
  } else {
    rep(FALSE, nrow(x))
  }
  .r_defaulted <- (.r_defaulted_exp | .r_defaulted_nexp) & .has_pre_post
  for (i in which(.r_defaulted)) {
    msg <- sprintf("[INFO] Default r_pre_post = %s used (not provided by user)", r_pre_post[1])
    if (nzchar(validation$issues[i])) {
      validation$issues[i] <- paste(validation$issues[i], msg, sep = "; ")
    } else {
      validation$issues[i] <- msg
    }
  }

  if (verbose) message("Calculations in progress, it may take up to 30 sec...")
  measure = tolower(measure)
  if (!measure %in% c("d", "g", "md", "dw", "gw", "mdw", "r", "z", "rp", "zp", "or", "rr", "irr", "hr", "logor", "logrr", "logirr", "loghr", "logvr", "logcvr", "nnt", "rd", "prop", "alpha", "icc")) {
    stop(paste0("'", measure, "' not in tolerated measures. Possible inputs are: 'md', 'd', 'g', 'dw', 'gw', 'mdw', 'or', 'rr', 'irr', 'hr', 'logor', 'logrr', 'logirr', 'loghr', 'r', 'z', 'rp', 'zp', 'logvr', 'logcvr', 'nnt', 'rd', 'prop', 'alpha', 'icc'"))
  } else if (!split_adjusted %in% c(TRUE, FALSE)) {
    stop(paste0("'", split_adjusted, "' not in tolerated values for the 'split_adjusted' argument. Should be a logical value (TRUE/FALSE)"))
  } else if (!format_adjusted %in% c("wide", "long")) {
    stop(paste0("'", format_adjusted, "' not in tolerated values for the 'format_adjusted' argument. Should be either 'long' or 'wide'."))
  } else if (!es_selected %in% c("auto", "minimum", "maximum", "hierarchy")) {
    stop(paste0("'", es_selected, "' not in tolerated values for the 'es_selected' argument. Possible inputs are: 'minimum', 'maximum', 'hierarchy'"))
  } else if (!main_es %in% c(TRUE, FALSE)) {
    stop(paste0("'", main_es, "' not in tolerated values for the 'main_es' argument. Should be a logical value (TRUE/FALSE)"))
  }

  if (measure %in% c("or", "rr", "irr", "hr")) {
    exp <- TRUE
    if (measure == "or") {
      measure <- "logor"
    } else if (measure == "rr") {
      measure <- "logrr"
    } else if (measure == "irr") {
      measure <- "logirr"
    } else if (measure == "hr") {
      measure <- "loghr"
    }
  } else {
    exp <- FALSE
  }

  # SMD ----------------------------------------------
  es_cohen_d <- with(x, es_from_cohen_d(
    cohen_d = cohen_d, n_exp = n_exp, n_nexp = n_nexp,
    smd_var = smd_var, smd_to_cor = smd_to_cor, reverse_d = reverse_d)
  )

  es_cohen_d_adj <- with(x, es_from_cohen_d_adj(
    cohen_d_adj = cohen_d_adj, n_cov_ancova = n_cov_ancova, cov_outcome_r = cov_outcome_r,
    n_exp = n_exp, n_nexp = n_nexp, smd_var = smd_var, smd_to_cor = smd_to_cor, reverse_d = reverse_d
  ))
  es_hedges_g <- with(x, es_from_hedges_g(
    hedges_g = hedges_g, n_exp = n_exp, n_nexp = n_nexp,
    smd_var = smd_var, smd_to_cor = smd_to_cor, reverse_g = reverse_g
  ))


  # OR ----------------------------------------------
  es_odds_ratio <- with(x, es_from_or(
    or = or, logor = logor, baseline_risk = baseline_risk, n_exp = n_exp, n_nexp = n_nexp,
    n_cases = n_cases, n_controls = n_controls, n_sample = n_sample, small_margin_prop = small_margin_prop,
    or_to_rr = or_to_rr, or_to_cor = or_to_cor, reverse_or = reverse_or
  ))
  es_odds_ratio_se <- with(x, es_from_or_se(
    or = or, logor = logor, logor_se = logor_se, small_margin_prop = small_margin_prop,
    baseline_risk = baseline_risk,
    n_exp = n_exp, n_nexp = n_nexp, n_sample = n_sample,
    n_cases = n_cases, n_controls = n_controls,
    or_to_rr = or_to_rr, or_to_cor = or_to_cor,
    reverse_or = reverse_or
  ))
  es_odds_ratio_ci <- with(x, es_from_or_ci(
    or = or, or_ci_lo, or_ci_up, logor = logor, small_margin_prop = small_margin_prop,
    baseline_risk = baseline_risk, n_exp = n_exp, n_nexp = n_nexp,
    logor_ci_lo = logor_ci_lo, logor_ci_up = logor_ci_up,
    n_cases = n_cases, n_controls = n_controls, n_sample = n_sample,
    or_to_rr = or_to_rr, or_to_cor = or_to_cor, reverse_or = reverse_or,
    max_asymmetry = max_asymmetry
  ))
  es_odds_ratio_pval <- with(x, es_from_or_pval(
    or = or, logor = logor, or_pval = or_pval, small_margin_prop = small_margin_prop,
    baseline_risk = baseline_risk, n_exp = n_exp, n_nexp = n_nexp,
    n_cases = n_cases, n_controls = n_controls, n_sample = n_sample,
    or_to_rr = or_to_rr, or_to_cor = or_to_cor, reverse_or_pval = reverse_or_pval
  ))
  es_logreg_t <- with(x, es_from_logreg_t(
    or = or, logor = logor, rr = rr, logrr = logrr,
    logreg_t = logreg_t, small_margin_prop = small_margin_prop,
    baseline_risk = baseline_risk, n_exp = n_exp, n_nexp = n_nexp,
    n_cases = n_cases, n_controls = n_controls, n_sample = n_sample,
    or_to_rr = or_to_rr, or_to_cor = or_to_cor,
    rr_to_or = rr_to_or,
    reverse_logreg_t = reverse_logreg_t
  ))
  # R ----------------------------------------------
  es_pearson_r <- with(x, es_from_pearson_r(
    pearson_r = pearson_r, n_sample = n_sample,
    unit_type = unit_type, sd_iv = sd_iv,
    unit_increase_iv = unit_increase_iv,
    n_exp = n_exp, n_nexp = n_nexp, cor_to_smd = cor_to_smd,
    reverse_pearson_r = reverse_pearson_r
  ))
  es_fisher_z <- with(x, es_from_fisher_z(
    fisher_z = fisher_z, n_sample = n_sample,
    unit_type = unit_type, sd_iv = sd_iv,
    unit_increase_iv = unit_increase_iv,
    n_exp = n_exp, n_nexp = n_nexp, cor_to_smd = cor_to_smd,
    reverse_fisher_z = reverse_fisher_z
  ))
  es_spearman_r <- with(x, es_from_spearman_rho(
    spearman_r = spearman_r, n_sample = n_sample,
    sd_iv = sd_iv, unit_increase_iv = unit_increase_iv,
    unit_type = unit_type,
    n_exp = n_exp, n_nexp = n_nexp, cor_to_smd = cor_to_smd,
    reverse_spearman_r = reverse_spearman_r
  ))

  # MEANS ----------------------------------------------
  es_means_sd_raw <- with(x, es_from_means_sd(
    n_exp = n_exp, n_nexp = n_nexp,
    mean_exp = mean_exp, mean_sd_exp = mean_sd_exp,
    mean_nexp = mean_nexp, mean_sd_nexp = mean_sd_nexp,
    smd_var = smd_var, smd_denom = smd_denom, smd_to_cor = smd_to_cor, reverse_means = reverse_means
  )
  )
  es_means_se_raw <- with(
    x,
    es_from_means_se(
      n_exp = n_exp, n_nexp = n_nexp,
      mean_exp = mean_exp, mean_se_exp = mean_se_exp,
      mean_nexp = mean_nexp, mean_se_nexp = mean_se_nexp,
      smd_var = smd_var, smd_denom = smd_denom, smd_to_cor = smd_to_cor, reverse_means = reverse_means
    )
  )
  es_means_ci_raw <- with(
    x,
    es_from_means_ci(
      n_exp = n_exp, n_nexp = n_nexp,
      mean_exp = mean_exp, mean_ci_lo_exp = mean_ci_lo_exp, mean_ci_up_exp = mean_ci_up_exp,
      mean_nexp = mean_nexp, mean_ci_lo_nexp = mean_ci_lo_nexp, mean_ci_up_nexp = mean_ci_up_nexp,
      smd_var = smd_var, smd_denom = smd_denom, smd_to_cor = smd_to_cor, reverse_means = reverse_means,
      max_asymmetry = max_asymmetry
    )
  )
  es_means_sd_pooled <- with(
    x,
    es_from_means_sd_pooled(
      n_exp = n_exp, n_nexp = n_nexp, mean_exp = mean_exp, mean_nexp = mean_nexp,
      mean_sd_pooled = mean_sd_pooled, smd_var = smd_var, smd_to_cor = smd_to_cor, reverse_means = reverse_means
    )
  )
  # plot
  es_plot_means_raw <- with(x, es_from_plot_means(
    n_exp = n_exp, n_nexp = n_nexp,
    plot_mean_exp = plot_mean_exp, plot_mean_nexp = plot_mean_nexp,
    plot_mean_sd_lo_exp = plot_mean_sd_lo_exp, plot_mean_sd_lo_nexp = plot_mean_sd_lo_nexp,
    plot_mean_sd_up_exp = plot_mean_sd_up_exp, plot_mean_sd_up_nexp = plot_mean_sd_up_nexp,
    plot_mean_se_lo_exp = plot_mean_se_lo_exp, plot_mean_se_lo_nexp = plot_mean_se_lo_nexp,
    plot_mean_se_up_exp = plot_mean_se_up_exp, plot_mean_se_up_nexp = plot_mean_se_up_nexp,
    plot_mean_ci_lo_exp = plot_mean_ci_lo_exp, plot_mean_ci_lo_nexp = plot_mean_ci_lo_nexp,
    plot_mean_ci_up_exp = plot_mean_ci_up_exp, plot_mean_ci_up_nexp = plot_mean_ci_up_nexp,
    smd_var = smd_var, smd_to_cor = smd_to_cor,
    reverse_plot_means = reverse_plot_means
  ))
  # PRE POST MEANS ----------------------------------------------
  es_means_sd_pre_post <- with(
    x,
    es_from_means_sd_pre_post(
      n_exp = n_exp, n_nexp = n_nexp,
      mean_pre_exp = mean_pre_exp, mean_exp = mean_exp,
      mean_pre_sd_exp = mean_pre_sd_exp, mean_sd_exp = mean_sd_exp,
      mean_pre_nexp = mean_pre_nexp, mean_nexp = mean_nexp,
      mean_pre_sd_nexp = mean_pre_sd_nexp, mean_sd_nexp = mean_sd_nexp,
      r_pre_post_exp = r_pre_post_exp, r_pre_post_nexp = r_pre_post_nexp,

      smd_to_cor = smd_to_cor, reverse_means_pre_post = reverse_means_pre_post,
      pre_post_to_smd = pre_post_to_smd,
      pool_sd = pool_sd
    )
  )
  es_means_se_pre_post <- with(
    x,
    es_from_means_se_pre_post(
      n_exp = n_exp, n_nexp = n_nexp,
      mean_pre_exp = mean_pre_exp, mean_exp = mean_exp,
      mean_pre_se_exp = mean_pre_se_exp, mean_se_exp = mean_se_exp,
      mean_pre_nexp = mean_pre_nexp, mean_nexp = mean_nexp,
      mean_pre_se_nexp = mean_pre_se_nexp, mean_se_nexp = mean_se_nexp,
      r_pre_post_exp = r_pre_post_exp, r_pre_post_nexp = r_pre_post_nexp,

      smd_to_cor = smd_to_cor, reverse_means_pre_post = reverse_means_pre_post,
      pre_post_to_smd = pre_post_to_smd,
      pool_sd = pool_sd
    )
  )
  es_means_ci_pre_post <- with(
    x,
    es_from_means_ci_pre_post(
      n_exp = n_exp, n_nexp = n_nexp,
      mean_pre_exp = mean_pre_exp, mean_exp = mean_exp,
      mean_pre_ci_lo_exp = mean_pre_ci_lo_exp, mean_pre_ci_up_exp = mean_pre_ci_up_exp,
      mean_ci_lo_exp = mean_ci_lo_exp, mean_ci_up_exp = mean_ci_up_exp,
      mean_pre_nexp = mean_pre_nexp, mean_nexp = mean_nexp,
      mean_pre_ci_lo_nexp = mean_pre_ci_lo_nexp, mean_pre_ci_up_nexp = mean_pre_ci_up_nexp,
      mean_ci_lo_nexp = mean_ci_lo_nexp, mean_ci_up_nexp = mean_ci_up_nexp,
      r_pre_post_exp = r_pre_post_exp, r_pre_post_nexp = r_pre_post_nexp,

      smd_to_cor = smd_to_cor, reverse_means_pre_post = reverse_means_pre_post,
      pre_post_to_smd = pre_post_to_smd,
      pool_sd = pool_sd,
      max_asymmetry = max_asymmetry
    )
  )

  # bonett/morris_dav need separate pre/post SDs, use cooper for mean change and paired t/f
  pre_post_to_smd_restricted <- if (pre_post_to_smd %in% c("bonett", "morris_dav")) {
    "cooper"
  } else {
    pre_post_to_smd
  }

  # The coercion above silently changes the ESTIMAND for the affected rows: they
  # are standardized by the change SD (cooper/d_rm) while means_sd_pre_post rows
  # keep the requested baseline-SD (bonett) or average-SD (morris_dav)
  # standardizer. Tell the user whenever data that actually routes through a
  # coerced method is present.
  if (verbose && !identical(pre_post_to_smd_restricted, pre_post_to_smd)) {
    .coerced_cols <- c(
      "mean_change_exp", "mean_change_nexp", "mean_change_sd_exp",
      "mean_change_sd_nexp", "mean_change_se_exp", "mean_change_se_nexp",
      "mean_change_pval_exp", "mean_change_pval_nexp",
      "paired_t_exp", "paired_t_nexp", "paired_f_exp", "paired_f_nexp",
      "paired_t_pval_exp", "paired_t_pval_nexp",
      "paired_f_pval_exp", "paired_f_pval_nexp"
    )
    .coerced_cols <- intersect(.coerced_cols, colnames(x))
    .has_coerced_data <- length(.coerced_cols) > 0 &&
      any(!is.na(x[, .coerced_cols, drop = FALSE]))
    if (.has_coerced_data) {
      message(
        "Note: pre_post_to_smd = '", pre_post_to_smd, "' requires separate pre/post SDs, ",
        "which mean-change and paired t/F data do not carry.\n",
        "  Those rows use 'cooper' (morris_drm: the change SD, rescaled by sqrt(2(1 - r)) ",
        "onto the\n  raw-score metric) instead. Both standardizers are on the raw-score ",
        "scale, so the rows\n  remain combinable, but 'morris_drm' additionally assumes ",
        "equal pre/post SDs and depends\n  on r_pre_post. Supply pre/post SDs to use '",
        pre_post_to_smd, "' throughout."
      )
    }
  }

  es_means_change_sd <- with(x, es_from_mean_change_sd(
    n_exp = n_exp, n_nexp = n_nexp,
    mean_change_exp = mean_change_exp, mean_change_sd_exp = mean_change_sd_exp,
    mean_change_nexp = mean_change_nexp, mean_change_sd_nexp = mean_change_sd_nexp,
    r_pre_post_exp = r_pre_post_exp, r_pre_post_nexp = r_pre_post_nexp,

    smd_to_cor = smd_to_cor, reverse_mean_change = reverse_mean_change,
    pre_post_to_smd = pre_post_to_smd_restricted,
    pool_sd = pool_sd
  ))
  es_mean_change_se <- with(x, es_from_mean_change_se(
    n_exp = n_exp, n_nexp = n_nexp,
    mean_change_exp = mean_change_exp, mean_change_se_exp = mean_change_se_exp,
    mean_change_nexp = mean_change_nexp, mean_change_se_nexp = mean_change_se_nexp,
    r_pre_post_exp = r_pre_post_exp, r_pre_post_nexp = r_pre_post_nexp,

    smd_to_cor = smd_to_cor, reverse_mean_change = reverse_mean_change,
    pre_post_to_smd = pre_post_to_smd_restricted,
    pool_sd = pool_sd
  ))
  es_mean_change_ci <- with(x, es_from_mean_change_ci(
    n_exp = n_exp, n_nexp = n_nexp,
    mean_change_exp = mean_change_exp,
    mean_change_ci_lo_exp = mean_change_ci_lo_exp, mean_change_ci_up_exp = mean_change_ci_up_exp,
    mean_change_nexp = mean_change_nexp,
    mean_change_ci_lo_nexp = mean_change_ci_lo_nexp, mean_change_ci_up_nexp = mean_change_ci_up_nexp,
    r_pre_post_exp = r_pre_post_exp, r_pre_post_nexp = r_pre_post_nexp,

    smd_to_cor = smd_to_cor, reverse_mean_change = reverse_mean_change,
    pre_post_to_smd = pre_post_to_smd_restricted,
    pool_sd = pool_sd,
    max_asymmetry = max_asymmetry
  ))
  es_mean_change_pval <- with(x, es_from_mean_change_pval(
    n_exp = n_exp, n_nexp = n_nexp,
    mean_change_exp = mean_change_exp, mean_change_pval_exp = mean_change_pval_exp,
    mean_change_nexp = mean_change_nexp, mean_change_pval_nexp = mean_change_pval_nexp,
    r_pre_post_exp = r_pre_post_exp, r_pre_post_nexp = r_pre_post_nexp,

    smd_to_cor = smd_to_cor, reverse_mean_change = reverse_mean_change,
    pre_post_to_smd = pre_post_to_smd_restricted,
    pool_sd = pool_sd
  ))

  # SINGLE-GROUP WITHIN-GROUP EFFECTS (experimental group only) --------------
  es_means_pp_sg <- with(x, es_from_means_sd_pre_post_single_group(
    mean_pre_exp = mean_pre_exp, mean_exp = mean_exp,
    mean_pre_sd_exp = mean_pre_sd_exp, mean_sd_exp = mean_sd_exp,
    n_exp = n_exp, r_pre_post_exp = r_pre_post_exp,
    pre_post_to_smd = pre_post_to_smd, smd_to_cor = smd_to_cor,
    reverse_means_pre_post = reverse_means_pre_post
  ))

  es_means_se_pp_sg <- with(x, es_from_means_se_pre_post_single_group(
    mean_pre_exp = mean_pre_exp, mean_exp = mean_exp,
    mean_pre_se_exp = mean_pre_se_exp, mean_se_exp = mean_se_exp,
    n_exp = n_exp, r_pre_post_exp = r_pre_post_exp,
    pre_post_to_smd = pre_post_to_smd, smd_to_cor = smd_to_cor,
    reverse_means_pre_post = reverse_means_pre_post
  ))

  es_means_ci_pp_sg <- with(x, es_from_means_ci_pre_post_single_group(
    mean_pre_exp = mean_pre_exp, mean_exp = mean_exp,
    mean_pre_ci_lo_exp = mean_pre_ci_lo_exp, mean_pre_ci_up_exp = mean_pre_ci_up_exp,
    mean_ci_lo_exp = mean_ci_lo_exp, mean_ci_up_exp = mean_ci_up_exp,
    n_exp = n_exp, r_pre_post_exp = r_pre_post_exp,
    pre_post_to_smd = pre_post_to_smd, smd_to_cor = smd_to_cor,
    max_asymmetry = max_asymmetry,
    reverse_means_pre_post = reverse_means_pre_post
  ))

  es_mean_change_sg <- with(x, es_from_mean_change_sd_single_group(
    mean_change_exp = mean_change_exp, mean_change_sd_exp = mean_change_sd_exp,
    n_exp = n_exp, r_pre_post_exp = r_pre_post_exp,
    smd_to_cor = smd_to_cor,
    pre_post_to_smd = pre_post_to_smd_restricted,
    reverse_mean_change = reverse_mean_change
  ))

  es_mean_change_se_sg <- with(x, es_from_mean_change_se_single_group(
    mean_change_exp = mean_change_exp, mean_change_se_exp = mean_change_se_exp,
    n_exp = n_exp, r_pre_post_exp = r_pre_post_exp,
    smd_to_cor = smd_to_cor,
    pre_post_to_smd = pre_post_to_smd_restricted,
    reverse_mean_change = reverse_mean_change
  ))

  es_mean_change_ci_sg <- with(x, es_from_mean_change_ci_single_group(
    mean_change_exp = mean_change_exp,
    mean_change_ci_lo_exp = mean_change_ci_lo_exp, mean_change_ci_up_exp = mean_change_ci_up_exp,
    n_exp = n_exp, r_pre_post_exp = r_pre_post_exp,
    smd_to_cor = smd_to_cor, max_asymmetry = max_asymmetry,
    pre_post_to_smd = pre_post_to_smd_restricted,
    reverse_mean_change = reverse_mean_change
  ))

  es_mean_change_pval_sg <- with(x, es_from_mean_change_pval_single_group(
    mean_change_exp = mean_change_exp, mean_change_pval_exp = mean_change_pval_exp,
    n_exp = n_exp, r_pre_post_exp = r_pre_post_exp,
    smd_to_cor = smd_to_cor,
    pre_post_to_smd = pre_post_to_smd_restricted,
    reverse_mean_change = reverse_mean_change
  ))

  es_paired_t_sg <- with(x, es_from_paired_t_single_group(
    paired_t_exp = paired_t_exp,
    n_exp = n_exp, r_pre_post_exp = r_pre_post_exp,
    pre_post_to_smd = pre_post_to_smd_restricted, smd_to_cor = smd_to_cor,
    reverse_paired_t = reverse_paired_t
  ))

  # SINGLE-GROUP PROPORTIONS --------------
  es_prop_sg <- with(x, es_from_prop_single_group(
    prop = prop,
    n_sample = n_sample,
    prop_to_es = prop_to_es,
    reverse_prop = reverse_prop
  ))

  es_prop_counts_sg <- with(x, es_from_prop_single_group_counts(
    n_cases = n_cases,
    n_sample = n_sample,
    prop_to_es = prop_to_es,
    reverse_prop = reverse_prop
  ))

  # CRONBACH'S ALPHA -----------------------------------------
  es_alpha_sg <- with(x, es_from_cronbach_alpha(
    cronbach_alpha = cronbach_alpha, n_sample = n_sample,
    n_items = n_items, alpha_to_es = alpha_to_es
  ))

  # ICC -------------------------------------------------------
  es_icc_sg <- with(x, es_from_icc(
    icc = icc, n_sample = n_sample,
    n_measurements = n_measurements, icc_type = icc_type,
    icc_to_es = icc_to_es
  ))

  es_paired_t <- with(x, es_from_paired_t(
    paired_t_exp = paired_t_exp, paired_t_nexp = paired_t_nexp,
    n_exp = n_exp, n_nexp = n_nexp,
    r_pre_post_exp = r_pre_post_exp, r_pre_post_nexp = r_pre_post_nexp,

    smd_to_cor = smd_to_cor, reverse_paired_t = reverse_paired_t,
    pre_post_to_smd = pre_post_to_smd_restricted
  ))

  es_paired_t_pval <- with(x, es_from_paired_t_pval(
    paired_t_pval_exp = paired_t_pval_exp,
    paired_t_pval_nexp = paired_t_pval_nexp,
    n_exp = n_exp, n_nexp = n_nexp,
    r_pre_post_exp = r_pre_post_exp, r_pre_post_nexp = r_pre_post_nexp,

    smd_to_cor = smd_to_cor, reverse_paired_t_pval = reverse_paired_t_pval,
    pre_post_to_smd = pre_post_to_smd_restricted
  ))

  es_paired_f <- with(x, es_from_paired_f(paired_f_exp, paired_f_nexp,
    n_exp = n_exp, n_nexp = n_nexp,
    r_pre_post_exp = r_pre_post_exp, r_pre_post_nexp = r_pre_post_nexp,

    smd_to_cor = smd_to_cor, reverse_paired_f = reverse_paired_f,
    pre_post_to_smd = pre_post_to_smd_restricted
  ))

  es_paired_f_pval <- with(x, es_from_paired_f_pval(
    paired_f_pval_exp = paired_f_pval_exp,
    paired_f_pval_nexp = paired_f_pval_nexp,
    n_exp = n_exp, n_nexp = n_nexp,
    r_pre_post_exp = r_pre_post_exp, r_pre_post_nexp = r_pre_post_nexp,

    smd_to_cor = smd_to_cor, reverse_paired_f_pval = reverse_paired_f_pval,
    pre_post_to_smd = pre_post_to_smd_restricted
  ))
  # ANOVA, Student t-test  ----------------------------------------------
  es_t_student <- with(x, es_from_student_t(
    student_t = student_t, n_exp = n_exp, n_nexp = n_nexp,
    smd_var = smd_var, smd_to_cor = smd_to_cor, reverse_student_t = reverse_student_t
  ))
  es_t_student_pval <- with(x, es_from_student_t_pval(
    student_t_pval = student_t_pval, n_exp = n_exp, n_nexp = n_nexp,
    smd_var = smd_var, smd_to_cor = smd_to_cor, reverse_student_t_pval = reverse_student_t_pval
  ))
  es_anova_f <- with(x, es_from_anova_f(
    anova_f = anova_f, n_exp = n_exp, n_nexp = n_nexp,
    smd_var = smd_var, smd_to_cor = smd_to_cor, reverse_anova_f = reverse_anova_f
  ))
  es_anova_f_pval <- with(x, es_from_anova_pval(
    anova_f_pval = anova_f_pval, n_exp = n_exp, n_nexp = n_nexp,
    smd_var = smd_var, smd_to_cor = smd_to_cor, reverse_anova_f_pval = reverse_anova_f_pval
  ))
  es_etasq <- with(x, es_from_etasq(
    etasq = etasq, n_exp = n_exp, n_nexp = n_nexp,
    smd_var = smd_var, smd_to_cor = smd_to_cor, reverse_etasq = reverse_etasq
  ))
  es_etasq_adj <- with(x, es_from_etasq_adj(
    etasq_adj = etasq_adj, n_exp = n_exp, n_nexp = n_nexp, n_cov_ancova = n_cov_ancova,
    cov_outcome_r = cov_outcome_r, smd_var = smd_var, smd_to_cor = smd_to_cor, reverse_etasq = reverse_etasq
  ))

  # MD   ----------------------------------------------
  es_md_sd <- with(x, es_from_md_sd(
    md = md, md_sd = md_sd, n_exp = n_exp, n_nexp = n_nexp,
    smd_to_cor = smd_to_cor, smd_var = smd_var, reverse_md = reverse_md
  ))
  es_md_se <- with(x, es_from_md_se(
    md = md, md_se = md_se, n_exp = n_exp, n_nexp = n_nexp,
    smd_to_cor = smd_to_cor, smd_var = smd_var, reverse_md = reverse_md
  ))
  es_md_ci <- with(x, es_from_md_ci(
    md = md, md_ci_lo = md_ci_lo, md_ci_up = md_ci_up, n_exp = n_exp, n_nexp = n_nexp,
    smd_to_cor = smd_to_cor, smd_var = smd_var, reverse_md = reverse_md,
    max_asymmetry = max_asymmetry
  ))
  es_md_pval <- with(x, es_from_md_pval(
    md = md, md_pval = md_pval, n_exp = n_exp, n_nexp = n_nexp,
    smd_to_cor = smd_to_cor, smd_var = smd_var, reverse_md = reverse_md
  ))

  # RANGE/QUARTILES  ----------------------------------------------
  es_med_quarts <- with(x, es_from_med_quarts(
    q1_exp = q1_exp, med_exp = med_exp, q3_exp = q3_exp, n_exp = n_exp,
    q1_nexp = q1_nexp, med_nexp = med_nexp, q3_nexp = q3_nexp, n_nexp = n_nexp,
    smd_var = smd_var, smd_to_cor = smd_to_cor, reverse_med = reverse_med
  ))
  es_med_min_max <- with(x, es_from_med_min_max(
    min_exp = min_exp, med_exp = med_exp, max_exp = max_exp, n_exp = n_exp,
    min_nexp = min_nexp, med_nexp = med_nexp, max_nexp = max_nexp, n_nexp = n_nexp,
    smd_var = smd_var, smd_to_cor = smd_to_cor, reverse_med = reverse_med
  ))
  es_med_min_max_quarts <- with(x, es_from_med_min_max_quarts(
    min_exp = min_exp, q1_exp = q1_exp, med_exp = med_exp, q3_exp = q3_exp, max_exp = max_exp, n_exp = n_exp,
    min_nexp = min_nexp, q1_nexp = q1_nexp, med_nexp = med_nexp, q3_nexp = q3_nexp, max_nexp = max_nexp,
    n_nexp = n_nexp, smd_var = smd_var, smd_to_cor = smd_to_cor, reverse_med = reverse_med
  ))

  # ANCOVA MEANS ------------------------------------------------------------------
  es_ancova_means_sd <- with(x, es_from_ancova_means_sd(
    ancova_mean_exp = ancova_mean_exp, ancova_mean_nexp = ancova_mean_nexp,
    ancova_mean_sd_exp = ancova_mean_sd_exp, ancova_mean_sd_nexp = ancova_mean_sd_nexp,
    cov_outcome_r = cov_outcome_r, n_cov_ancova = n_cov_ancova, n_exp = n_exp, n_nexp = n_nexp,
    smd_var = smd_var, smd_to_cor = smd_to_cor, reverse_ancova_means = reverse_ancova_means
  ))
  es_ancova_means_se <- with(x, es_from_ancova_means_se(
    ancova_mean_exp = ancova_mean_exp, ancova_mean_nexp = ancova_mean_nexp,
    ancova_mean_se_exp = ancova_mean_se_exp, ancova_mean_se_nexp = ancova_mean_se_nexp,
    cov_outcome_r = cov_outcome_r, n_cov_ancova = n_cov_ancova, n_exp = n_exp, n_nexp = n_nexp,
    smd_var = smd_var, smd_to_cor = smd_to_cor, reverse_ancova_means = reverse_ancova_means
  ))
  es_ancova_means_ci <- with(x, es_from_ancova_means_ci(
    ancova_mean_exp = ancova_mean_exp, ancova_mean_nexp = ancova_mean_nexp,
    ancova_mean_ci_lo_exp = ancova_mean_ci_lo_exp, ancova_mean_ci_up_exp = ancova_mean_ci_up_exp,
    ancova_mean_ci_lo_nexp = ancova_mean_ci_lo_nexp, ancova_mean_ci_up_nexp = ancova_mean_ci_up_nexp,
    cov_outcome_r = cov_outcome_r, n_cov_ancova = n_cov_ancova, n_exp = n_exp, n_nexp = n_nexp,
    smd_var = smd_var, smd_to_cor = smd_to_cor, reverse_ancova_means = reverse_ancova_means
  ))
  es_ancova_means_sd_pooled_adj <- with(x, es_from_ancova_means_sd_pooled_adj(
    ancova_mean_exp = ancova_mean_exp, ancova_mean_nexp = ancova_mean_nexp,
    ancova_mean_sd_pooled = ancova_mean_sd_pooled,
    n_exp = n_exp, n_nexp = n_nexp, cov_outcome_r = cov_outcome_r, n_cov_ancova = n_cov_ancova,
    smd_var = smd_var, smd_to_cor = smd_to_cor, reverse_ancova_means = reverse_ancova_means
  ))
  es_ancova_means_sd_pooled <- with(x, es_from_ancova_means_sd_pooled_crude(
    ancova_mean_exp = ancova_mean_exp, ancova_mean_nexp = ancova_mean_nexp,
    mean_sd_pooled = mean_sd_pooled, cov_outcome_r = cov_outcome_r, n_cov_ancova = n_cov_ancova,
    n_exp = n_exp, n_nexp = n_nexp, smd_var = smd_var, smd_to_cor = smd_to_cor, reverse_ancova_means = reverse_ancova_means
  ))
  # plot
  es_plot_ancova_means <- with(x, es_from_plot_ancova_means(
    n_exp = n_exp, n_nexp = n_nexp,
    plot_ancova_mean_exp = plot_ancova_mean_exp, plot_ancova_mean_nexp = plot_ancova_mean_nexp,
    plot_ancova_mean_sd_lo_exp = plot_ancova_mean_sd_lo_exp, plot_ancova_mean_sd_lo_nexp = plot_ancova_mean_sd_lo_nexp,
    plot_ancova_mean_sd_up_exp = plot_ancova_mean_sd_up_exp, plot_ancova_mean_sd_up_nexp = plot_ancova_mean_sd_up_nexp,
    plot_ancova_mean_se_lo_exp = plot_ancova_mean_se_lo_exp, plot_ancova_mean_se_lo_nexp = plot_ancova_mean_se_lo_nexp,
    plot_ancova_mean_se_up_exp = plot_ancova_mean_se_up_exp, plot_ancova_mean_se_up_nexp = plot_ancova_mean_se_up_nexp,
    plot_ancova_mean_ci_lo_exp = plot_ancova_mean_ci_lo_exp, plot_ancova_mean_ci_lo_nexp = plot_ancova_mean_ci_lo_nexp,
    plot_ancova_mean_ci_up_exp = plot_ancova_mean_ci_up_exp, plot_ancova_mean_ci_up_nexp = plot_ancova_mean_ci_up_nexp,
    cov_outcome_r = cov_outcome_r, n_cov_ancova = n_cov_ancova,
    smd_var = smd_var, smd_to_cor = smd_to_cor, reverse_plot_ancova_means = reverse_plot_ancova_means
  ))
  # ANCOVA MD ------------------------------------------------------------------
  es_ancova_md_sd <- with(x, es_from_ancova_md_sd(ancova_md = ancova_md, ancova_md_sd = ancova_md_sd,
         cov_outcome_r = cov_outcome_r, n_cov_ancova = n_cov_ancova,
         n_exp = n_exp, n_nexp = n_nexp,
         smd_var = smd_var, smd_to_cor = smd_to_cor,
         reverse_ancova_md = reverse_ancova_md))
  es_ancova_md_se <- with(x, es_from_ancova_md_se(ancova_md = ancova_md, ancova_md_se = ancova_md_se,
         cov_outcome_r = cov_outcome_r, n_cov_ancova = n_cov_ancova,
         n_exp = n_exp, n_nexp = n_nexp,
         smd_var = smd_var, smd_to_cor = smd_to_cor,
         reverse_ancova_md = reverse_ancova_md))
  es_ancova_md_ci <- with(x, es_from_ancova_md_ci(ancova_md = ancova_md,
         ancova_md_ci_lo = ancova_md_ci_lo, ancova_md_ci_up = ancova_md_ci_up,
         cov_outcome_r = cov_outcome_r, n_cov_ancova = n_cov_ancova,
         n_exp = n_exp, n_nexp = n_nexp,
         smd_var = smd_var, smd_to_cor = smd_to_cor,
         reverse_ancova_md = reverse_ancova_md))
  es_ancova_md_pval <- with(x, es_from_ancova_md_pval(
        ancova_md = ancova_md,
        ancova_md_pval = ancova_md_pval,
        cov_outcome_r = cov_outcome_r, n_cov_ancova = n_cov_ancova,
        n_exp = n_exp, n_nexp = n_nexp,
        smd_var = smd_var, smd_to_cor = smd_to_cor,
        reverse_ancova_md = reverse_ancova_md))
  # ANCOVA F, T, P ------------------------------------------------------------------
  es_ancova_t <- with(x, es_from_ancova_t(
    ancova_t = ancova_t, cov_outcome_r = cov_outcome_r, n_cov_ancova = n_cov_ancova,
    n_exp = n_exp, n_nexp = n_nexp, smd_var = smd_var, smd_to_cor = smd_to_cor, reverse_ancova_t = reverse_ancova_t
  ))
  es_ancova_f <- with(x, es_from_ancova_f(
    ancova_f = ancova_f, cov_outcome_r = cov_outcome_r, n_cov_ancova = n_cov_ancova,
    n_exp = n_exp, n_nexp = n_nexp, smd_var = smd_var, smd_to_cor = smd_to_cor, reverse_ancova_f = reverse_ancova_f
  ))
  es_ancova_t_pval <- with(x, es_from_ancova_t_pval(
    ancova_t_pval = ancova_t_pval, cov_outcome_r = cov_outcome_r, n_cov_ancova = n_cov_ancova,
    n_exp = n_exp, n_nexp = n_nexp, smd_var = smd_var, smd_to_cor = smd_to_cor, reverse_ancova_t_pval = reverse_ancova_t_pval
  ))
  es_ancova_f_pval <- with(x, es_from_ancova_f_pval(
    ancova_f_pval = ancova_f_pval, cov_outcome_r = cov_outcome_r, n_cov_ancova = n_cov_ancova,
    n_exp = n_exp, n_nexp = n_nexp, smd_var = smd_var, smd_to_cor = smd_to_cor, reverse_ancova_f_pval = reverse_ancova_f_pval
  ))

  # CHI-SQ + PHI --------------------------------------------------------
  # yates_chisq column overrides the argument
  yates_chisq_vec <- x$yates_chisq
  yates_chisq_vec[is.na(yates_chisq_vec)] <- as.logical(yates_chisq)[1]

  es_chisq <- with(x, es_from_chisq(
    chisq = chisq, n_sample = n_sample,
    n_cases = n_cases, n_exp = n_exp,
    yates_chisq = yates_chisq_vec,
    reverse_chisq = reverse_chisq
  ))

  es_chisq_pval <- with(x, es_from_chisq_pval(
    chisq_pval = chisq_pval,
    n_cases = n_cases, n_exp = n_exp,
    yates_chisq = yates_chisq_vec,
    n_sample = n_sample, reverse_chisq_pval = reverse_chisq_pval
  ))

  es_phi <- with(x, es_from_phi(
    phi = phi, n_sample = n_sample,
    n_cases = n_cases, n_exp = n_exp,
    reverse_phi = reverse_phi
  ))

  # COR-PB   --------------------------------------------------------
  es_r_point_bis <- with(x, es_from_pt_bis_r(
    pt_bis_r = pt_bis_r, n_exp = n_exp, n_nexp = n_nexp,
    smd_var = smd_var, smd_to_cor = smd_to_cor,
    reverse_pt_bis_r = reverse_pt_bis_r
  ))

  es_r_point_bis_pval <- with(x, es_from_pt_bis_r_pval(
    pt_bis_r_pval = pt_bis_r_pval,
    n_exp = n_exp, n_nexp = n_nexp,
    smd_var = smd_var, smd_to_cor = smd_to_cor,
    reverse_pt_bis_r_pval = reverse_pt_bis_r_pval
  ))

  # 2x2, PROP --------------------------------------------------------
  es_2x2 <- with(x, es_from_2x2(
    n_cases_exp = n_cases_exp, n_cases_nexp = n_cases_nexp,
    n_controls_exp = n_controls_exp, n_controls_nexp = n_controls_nexp,
    # table_2x2_to_cor = table_2x2_to_cor,
    reverse_2x2 = reverse_2x2
  ))
  es_2x2_sum <- with(x, es_from_2x2_sum(
    n_cases_exp = n_cases_exp, n_cases_nexp = n_cases_nexp, n_exp = n_exp, n_nexp = n_nexp,
    # table_2x2_to_cor = table_2x2_to_cor,
    reverse_2x2 = reverse_2x2
  ))
  es_prop <- with(x, es_from_2x2_prop(
    prop_cases_exp = prop_cases_exp, prop_cases_nexp = prop_cases_nexp, n_exp = n_exp, n_nexp = n_nexp,
    # table_2x2_to_cor = table_2x2_to_cor,
    reverse_prop = reverse_prop
  ))

  # RR --------------------------------------------------------
  es_rr_se <- with(x, es_from_rr_se(
    rr = rr, logrr = logrr, logrr_se = logrr_se,
    baseline_risk = baseline_risk, n_exp = n_exp, n_nexp = n_nexp,
    n_cases = n_cases, n_controls = n_controls,
    rr_to_or = rr_to_or, reverse_rr = reverse_rr
  ))
  es_rr_ci <- with(x, es_from_rr_ci(
    rr = rr, logrr = logrr,
    baseline_risk = baseline_risk, n_exp = n_exp, n_nexp = n_nexp,
    rr_ci_lo = rr_ci_lo, logrr_ci_lo = logrr_ci_lo, rr_ci_up = rr_ci_up, logrr_ci_up = logrr_ci_up, n_cases = n_cases, n_controls = n_controls,
    rr_to_or = rr_to_or, reverse_rr = reverse_rr,
    max_asymmetry = max_asymmetry
  ))
  es_rr_pval <- with(x, es_from_rr_pval(
    rr = rr, logrr = logrr, rr_pval = rr_pval,
    baseline_risk = baseline_risk, n_exp = n_exp, n_nexp = n_nexp,
    n_cases = n_cases, n_controls = n_controls,
    rr_to_or = rr_to_or, reverse_rr = reverse_rr_pval
  ))

  # RD (risk difference) --------------------------------------------------------
  es_rd_se <- with(x, es_from_rd_se(
    rd = rd, rd_se = rd_se,
    baseline_risk = baseline_risk,
    n_exp = n_exp, n_nexp = n_nexp,
    n_cases = n_cases, n_controls = n_controls, n_sample = n_sample,
    reverse_rd = reverse_rd
  ))
  es_rd_ci <- with(x, es_from_rd_ci(
    rd = rd,
    rd_ci_lo = rd_ci_lo, rd_ci_up = rd_ci_up,
    baseline_risk = baseline_risk,
    n_exp = n_exp, n_nexp = n_nexp,
    n_cases = n_cases, n_controls = n_controls, n_sample = n_sample,
    reverse_rd = reverse_rd
  ))
  es_rd_pval <- with(x, es_from_rd_pval(
    rd = rd, rd_pval = rd_pval,
    baseline_risk = baseline_risk,
    n_exp = n_exp, n_nexp = n_nexp,
    n_cases = n_cases, n_controls = n_controls, n_sample = n_sample,
    reverse_rd_pval = reverse_rd_pval
  ))

  # regression
  es_std_beta <- with(x, es_from_beta_std(
    beta_std = beta_std, sd_dv = sd_dv, n_exp = n_exp, n_nexp = n_nexp,
    smd_var = smd_var, smd_to_cor = smd_to_cor, reverse_beta_std = reverse_beta_std
  ))
  es_unstd_beta <- with(x, es_from_beta_unstd(
    beta_unstd = beta_unstd, sd_dv = sd_dv, n_exp = n_exp, n_nexp = n_nexp,
    smd_var = smd_var, smd_to_cor = smd_to_cor, reverse_beta_unstd = reverse_beta_unstd
  ))

  # regression t-statistic (partial correlation)
  es_linreg_t <- with(x, es_from_linreg_t(
    linreg_t = linreg_t, n_sample = n_sample, n_covariates = n_covariates,
    sd_iv = sd_iv, unit_increase_iv = unit_increase_iv, unit_type = unit_type,
    n_exp = n_exp, n_nexp = n_nexp, cor_to_smd = cor_to_smd,
    reverse_linreg_t = reverse_linreg_t
  ))

  # regression coefficient (partial correlation)
  es_linreg_b_se <- with(x, es_from_linreg_b_se(
    linreg_b = linreg_b, linreg_b_se = linreg_b_se,
    n_sample = n_sample, n_covariates = n_covariates,
    sd_iv = sd_iv, unit_increase_iv = unit_increase_iv, unit_type = unit_type,
    n_exp = n_exp, n_nexp = n_nexp, cor_to_smd = cor_to_smd,
    reverse_linreg_b = reverse_linreg_b
  ))
  es_linreg_b_ci <- with(x, es_from_linreg_b_ci(
    linreg_b = linreg_b, linreg_b_ci_lo = linreg_b_ci_lo, linreg_b_ci_up = linreg_b_ci_up,
    n_sample = n_sample, n_covariates = n_covariates,
    sd_iv = sd_iv, unit_increase_iv = unit_increase_iv, unit_type = unit_type,
    n_exp = n_exp, n_nexp = n_nexp, cor_to_smd = cor_to_smd,
    reverse_linreg_b = reverse_linreg_b
  ))
  es_linreg_b_pval <- with(x, es_from_linreg_b_pval(
    linreg_b = linreg_b, linreg_b_pval = linreg_b_pval,
    n_sample = n_sample, n_covariates = n_covariates,
    sd_iv = sd_iv, unit_increase_iv = unit_increase_iv, unit_type = unit_type,
    n_exp = n_exp, n_nexp = n_nexp, cor_to_smd = cor_to_smd,
    reverse_linreg_b_pval = reverse_linreg_b_pval
  ))

  # user input
  es_user_crude <- es_from_user_crude(
    user_es_original_measure_crude = x$user_es_original_measure_crude,
    user_es_crude = x$user_es_crude,
    user_se_crude = x$user_se_crude,
    user_ci_lo_crude = x$user_ci_lo_crude,
    user_ci_up_crude = x$user_ci_up_crude,
    user_es_target_measure_crude = measure,
    n_exp = x$n_exp, n_nexp = x$n_nexp, n_sample = x$n_sample,
    n_cases = x$n_cases, n_controls = x$n_controls,
    baseline_risk = x$baseline_risk,
    small_margin_prop = x$small_margin_prop,
    or_to_rr = or_to_rr, or_to_cor = or_to_cor,
    smd_to_cor = smd_to_cor, cor_to_smd = cor_to_smd,
    rr_to_or = rr_to_or
  )
  es_user_adj <- es_from_user_adj(
    user_es_original_measure_adj = x$user_es_original_measure_adj,
    user_es_adj = x$user_es_adj,
    user_se_adj = x$user_se_adj,
    user_ci_lo_adj = x$user_ci_lo_adj,
    user_ci_up_adj = x$user_ci_up_adj,
    user_es_target_measure_adj = measure,
    n_exp = x$n_exp, n_nexp = x$n_nexp, n_sample = x$n_sample,
    n_cases = x$n_cases, n_controls = x$n_controls,
    baseline_risk = x$baseline_risk,
    small_margin_prop = x$small_margin_prop,
    or_to_rr = or_to_rr, or_to_cor = or_to_cor,
    smd_to_cor = smd_to_cor, cor_to_smd = cor_to_smd,
    rr_to_or = rr_to_or
  )
  # survival
  es_cases_time <- with(x, es_from_cases_time(
    n_cases_exp = n_cases_exp, n_cases_nexp = n_cases_nexp,
    time_exp = time_exp, time_nexp = time_nexp,
    baseline_rate = baseline_rate,
    reverse_irr = reverse_irr
  ))
  # variability
  var_means_sd <- with(x, es_variab_from_means_sd(
    mean_exp = mean_exp, mean_nexp = mean_nexp,
    mean_sd_exp = mean_sd_exp, mean_sd_nexp = mean_sd_nexp,
    n_exp = n_exp, n_nexp = n_nexp,
    reverse_means_variability = reverse_means_variability
  ))
  var_means_se <- with(x, es_variab_from_means_se(
    mean_exp = mean_exp, mean_nexp = mean_nexp,
    mean_se_exp = mean_se_exp, mean_se_nexp = mean_se_nexp,
    n_exp = n_exp, n_nexp = n_nexp,
    reverse_means_variability = reverse_means_variability
  ))
  var_means_ci <- with(x, es_variab_from_means_ci(
    mean_exp = mean_exp, mean_nexp = mean_nexp,
    mean_ci_lo_exp = mean_ci_lo_exp, mean_ci_up_exp = mean_ci_up_exp,
    mean_ci_lo_nexp = mean_ci_lo_nexp, mean_ci_up_nexp = mean_ci_up_nexp,
    n_exp = n_exp, n_nexp = n_nexp,
    reverse_means_variability = reverse_means_variability
  ))
  # ========================================= STEP 2. SYNTHESIS OF CALCULATIONS =========================================== #

  smd_list_L1 = list(es_cohen_d = es_cohen_d,
                es_hedges_g = es_hedges_g)
  # Reported precision (a logOR SE, or a reported CI) outranks es_odds_ratio, whose SE
  # is *simulated* from the cell marginals (es_from_or) and runs ~1.5x wide. When a study
  # reports an OR together with a CI/SE AND has n_cases/n_controls, the reported interval
  # is the more reliable source and must not be discarded for the marginal reconstruction.
  # This also aligns the OR order with the RR list (es_rr_se, es_rr_ci first).
  or_list_L2 = list(es_odds_ratio_se = es_odds_ratio_se,
                 es_odds_ratio_ci = es_odds_ratio_ci,
                 es_odds_ratio = es_odds_ratio,
                 es_odds_ratio_pval = es_odds_ratio_pval,
                 es_logreg_t = es_logreg_t)
  rr_list_L3 = list(es_rr_se = es_rr_se,
                     es_rr_ci = es_rr_ci,
                     es_rr_pval = es_rr_pval)
  rd_list_L30 = list(es_rd_se = es_rd_se,
                     es_rd_ci = es_rd_ci,
                     es_rd_pval = es_rd_pval)
  cor_list_L4 = list(es_pearson_r = es_pearson_r,
                  es_fisher_z = es_fisher_z,
                  es_spearman_r = es_spearman_r)
  irr_list_L5 = list(es_cases_time = es_cases_time)

  var_list_L6 = list(var_means_sd = var_means_sd,
                     var_means_se = var_means_se,
                     var_means_ci = var_means_ci)

  contingency_list_L7 = list(es_2x2 = es_2x2,
                         es_2x2_sum = es_2x2_sum,
                         es_prop = es_prop)

  phi_chisq_list_L8 = list(es_chisq = es_chisq,
                           es_phi = es_phi,
                           es_chisq_pval = es_chisq_pval)

  means_post_list_L9 = list(es_means_sd_raw = es_means_sd_raw,
                         es_means_sd_pooled = es_means_sd_pooled,
                         es_means_se_raw = es_means_se_raw,
                         es_means_ci_raw = es_means_ci_raw)
  md_post_list_L10 = list(es_md_sd = es_md_sd,
                      es_md_se = es_md_se,
                      es_md_ci = es_md_ci,
                      es_md_pval = es_md_pval)
  anova_list_L11 = list(es_t_student = es_t_student,
                    es_anova_f = es_anova_f,
                    es_r_point_bis = es_r_point_bis,
                    es_t_student_pval = es_t_student_pval,
                    es_anova_f_pval = es_anova_f_pval,
                    es_r_point_bis_pval = es_r_point_bis_pval,
                    es_etasq = es_etasq)
  medians_list_L12 = list(es_med_quarts = es_med_quarts,
                      es_med_min_max = es_med_min_max,
                      es_med_min_max_quarts = es_med_min_max_quarts)
  regression_list_L13 = list(es_std_beta = es_std_beta,
                           es_unstd_beta = es_unstd_beta)
  partial_cor_list_L27 = list(es_linreg_t = es_linreg_t,
                               es_linreg_b_se = es_linreg_b_se,
                               es_linreg_b_ci = es_linreg_b_ci,
                               es_linreg_b_pval = es_linreg_b_pval)

  md_paired_list_L14 = list(es_means_change_sd = es_means_change_sd,
                   es_mean_change_se = es_mean_change_se,
                   es_mean_change_ci = es_mean_change_ci,
                   es_mean_change_pval = es_mean_change_pval)
  means_paired_list_L15 = list(es_means_sd_pre_post = es_means_sd_pre_post,
                           es_means_se_pre_post = es_means_se_pre_post,
                           es_means_ci_pre_post = es_means_ci_pre_post)
  anova_paired_list_L16 = list(es_paired_t = es_paired_t,
                           es_paired_t_pval = es_paired_t_pval,
                           es_paired_f = es_paired_f,
                           es_paired_f_pval = es_paired_f_pval)

  es_cohen_d_adj_L17 = list(es_cohen_d_adj = es_cohen_d_adj)
  ancova_adjusted_list_L18 = list(es_ancova_t = es_ancova_t,
                                  es_ancova_f = es_ancova_f,
                                  es_ancova_t_pval = es_ancova_t_pval,
                                  es_ancova_f_pval = es_ancova_f_pval,
                                  es_etasq_adj = es_etasq_adj)
  means_adjusted_list_L19 = list(es_ancova_means_sd = es_ancova_means_sd,
                             es_ancova_means_sd_pooled = es_ancova_means_sd_pooled,
                             es_ancova_means_se = es_ancova_means_se,
                             es_ancova_means_ci = es_ancova_means_ci,
                             es_ancova_means_sd_pooled_adj = es_ancova_means_sd_pooled_adj)

  md_adjusted_list_L20 = list(es_ancova_md_sd = es_ancova_md_sd,
                              es_ancova_md_se = es_ancova_md_se,
                              es_ancova_md_ci = es_ancova_md_ci,
                              es_ancova_md_pval = es_ancova_md_pval)


  plot_raw_L21 = list(es_plot_means_raw)
  plot_adjusted_L22 = list(es_plot_ancova_means)
  user_raw_L23 = list(es_user_crude)
  es_user_adj_L24 = list(es_user_adj)

  within_group_list_L25 = list(es_means_pp_sg = es_means_pp_sg,
                               es_means_se_pp_sg = es_means_se_pp_sg,
                               es_means_ci_pp_sg = es_means_ci_pp_sg,
                               es_mean_change_sg = es_mean_change_sg,
                               es_mean_change_se_sg = es_mean_change_se_sg,
                               es_mean_change_ci_sg = es_mean_change_ci_sg,
                               es_mean_change_pval_sg = es_mean_change_pval_sg,
                               es_paired_t_sg = es_paired_t_sg)

  prop_list_L26 = list(es_prop_sg = es_prop_sg,
                       es_prop_counts_sg = es_prop_counts_sg)

  alpha_list_L28 = list(es_alpha_sg = es_alpha_sg)

  icc_list_L29 = list(es_icc_sg = es_icc_sg)

  USER_crude = user_raw_L23
  USER_adjusted = es_user_adj_L24

  SMD_post = c(smd_list_L1, means_post_list_L9,
               anova_list_L11, regression_list_L13, md_post_list_L10,
               medians_list_L12, plot_raw_L21)

  SMD_paired = c(means_paired_list_L15, anova_paired_list_L16, md_paired_list_L14)

  SMD_adjusted = c(es_cohen_d_adj_L17, means_adjusted_list_L19, ancova_adjusted_list_L18,
                   md_adjusted_list_L20,
                   plot_adjusted_L22)

  OR = c(or_list_L2)

  CONT = contingency_list_L7

  RR = rr_list_L3

  RD_stand = rd_list_L30

  PHI = phi_chisq_list_L8

  COR = cor_list_L4

  IRR = irr_list_L5

  VAR = var_list_L6


  if (es_selected == "auto") {
    if (measure %in% c("d", "g", "md")) {
      # OR and the raw 2x2 table (CONT) are the sole gateway into the SMD
      # family via the Cox transform d = log(OR) * sqrt(3)/pi. RD_stand is
      # NOT used here: RD -> SMD is not identified without an assumed
      # baseline_risk and its SE is anti-conservative (baseline_risk treated
      # as a fixed constant). RD keeps its legitimate ratio conversions below.

      if (selection_auto == "crude") {
        res = c(USER_crude, SMD_post, SMD_paired, OR,
                CONT, COR, PHI, SMD_adjusted,
                USER_adjusted,
                #not used
                RR, RD_stand, IRR, VAR)

      } else if (selection_auto == "adjusted") {
        res = c(USER_adjusted, SMD_adjusted,
                USER_crude, SMD_post,
                SMD_paired, OR, CONT, COR,
                PHI,
                #not used
                RR, RD_stand, IRR, VAR)
      } else if (selection_auto == "paired") {
        res = c(USER_crude, SMD_paired, SMD_post, SMD_adjusted,
                USER_adjusted, OR, CONT, COR,
                PHI,
                #not used
                RR, RD_stand, IRR, VAR)
      } else {
        stop("selection_auto should be one of 'crude', 'adjusted' or 'paired'")
      }

    } else if (measure %in% c("logor", "or")) {
      res = c(USER_crude, OR, CONT, RR, RD_stand, PHI, COR, SMD_post, USER_adjusted,
              #not used
              SMD_paired, SMD_adjusted, IRR, VAR)

    } else if (measure %in% c("logrr", "rr")) {
      res = c(USER_crude, RR, CONT, OR, RD_stand, PHI, USER_adjusted,
              #not used
              SMD_post, SMD_paired,
              COR, SMD_adjusted,
              IRR, VAR)

    } else if (measure %in% c("logirr", "irr")) {
      res = c(USER_crude, IRR, USER_adjusted,
              #not used
              SMD_post, SMD_paired, OR,
              CONT, COR, PHI, SMD_adjusted,
              RR, RD_stand, VAR)

    } else if (measure %in% c("loghr", "hr")) {
      # HR: user input only
      res = c(USER_crude, USER_adjusted)

    } else if (measure %in% c("nnt")) {
      res = c(USER_crude, RD_stand, CONT, OR, RR, IRR, PHI, USER_adjusted,
              #not used
              SMD_post, SMD_paired, COR, SMD_adjusted,
              VAR)

    } else if (measure %in% c("rd")) {
      res = c(USER_crude, RD_stand, CONT, OR, RR, IRR, PHI, USER_adjusted,
              #not used
              SMD_post, SMD_paired, COR, SMD_adjusted,
              VAR)

    } else if (measure %in% c("r", "z")) {
      # As for the SMD family, OR and the raw 2x2 (CONT) are the sole gateway
      # into the correlation family (r/z are derived downstream of d_se).
      # RD_stand stays #not used for the same identification/SE reason.
      res = c(USER_crude, COR, CONT, OR, PHI, SMD_post,
              SMD_paired, USER_adjusted,
              #not used
              SMD_adjusted,
              RR, RD_stand, IRR, VAR)

    } else if (measure %in% c("logvr", "logcvr")) {
      res = c(USER_crude, VAR, SMD_post, SMD_paired, USER_adjusted,
              #not used
              OR, CONT, RD_stand, COR, PHI, SMD_adjusted,
              RR, IRR)

    } else if (measure %in% c("dw", "gw", "mdw")) {
      res = c(USER_crude, within_group_list_L25)

    } else if (measure == "prop") {
      res = c(USER_crude, prop_list_L26)

    } else if (measure == "alpha") {
      res = c(USER_crude, alpha_list_L28)

    } else if (measure == "icc") {
      res = c(USER_crude, icc_list_L29)

    } else if (measure %in% c("rp", "zp")) {
      res = c(USER_crude, partial_cor_list_L27)

    }
  } else {
    if (measure %in% c("dw", "gw", "mdw")) {
      res = c(USER_crude, within_group_list_L25)
    } else if (measure == "prop") {
      res = c(USER_crude, prop_list_L26)
    } else if (measure == "alpha") {
      res = c(USER_crude, alpha_list_L28)
    } else if (measure == "icc") {
      res = c(USER_crude, icc_list_L29)
    } else if (measure %in% c("rp", "zp")) {
      res = c(USER_crude, partial_cor_list_L27)
    } else if (measure %in% c("loghr", "hr")) {
      res = c(USER_crude, USER_adjusted)
    } else if (measure %in% c("logor", "or")) {
      # Non-auto modes mirror the auto-mode ordering for every measure, so the
      # fallback selection (e.g. hierarchy mode with a default hierarchy) agrees
      # with es_selected = "auto" -- here, preferring the directly reported OR
      # over a reconstructed 2x2.
      res = c(USER_crude, OR, CONT, RR, RD_stand, PHI, COR, SMD_post, USER_adjusted,
              #not used
              SMD_paired, SMD_adjusted, IRR, VAR)
    } else if (measure %in% c("logrr", "rr")) {
      # Prefer the directly reported RR over a reconstructed 2x2/OR (mirrors auto).
      res = c(USER_crude, RR, CONT, OR, RD_stand, PHI, USER_adjusted,
              #not used
              SMD_post, SMD_paired,
              COR, SMD_adjusted,
              IRR, VAR)
    } else if (measure %in% c("logirr", "irr")) {
      res = c(USER_crude, IRR, USER_adjusted,
              #not used
              SMD_post, SMD_paired, OR,
              CONT, COR, PHI, SMD_adjusted,
              RR, RD_stand, VAR)
    } else if (measure %in% c("nnt", "rd")) {
      # Ratio-family targets: RD_stand is a legitimate source (RD -> OR/RR/NNT).
      res = c(USER_crude, RD_stand, CONT, OR, RR, IRR, PHI, USER_adjusted,
              #not used
              SMD_post, SMD_paired, COR, SMD_adjusted,
              VAR)
    } else if (measure %in% c("r", "z")) {
      # OR and the raw 2x2 are the sole gateway into the correlation family;
      # RD_stand stays #not used (RD -> r/z not identified without an assumed
      # baseline_risk; anti-conservative SE). Mirrors auto.
      res = c(USER_crude, COR, CONT, OR, PHI, SMD_post,
              SMD_paired, USER_adjusted,
              #not used
              SMD_adjusted,
              RR, RD_stand, IRR, VAR)
    } else if (measure %in% c("logvr", "logcvr")) {
      res = c(USER_crude, VAR, SMD_post, SMD_paired, USER_adjusted,
              #not used
              OR, CONT, RD_stand, COR, PHI, SMD_adjusted,
              RR, IRR)
    } else {
      # d/g/md targets. selection_auto (crude/adjusted/paired) does not apply
      # outside es_selected = "auto", so the crude ordering is used. OR and the
      # raw 2x2 are the sole gateway into the SMD family; RD_stand is demoted
      # (RD -> SMD is not identified without an assumed baseline_risk and yields
      # anti-conservative SEs).
      res = c(USER_crude, SMD_post, SMD_paired, OR,
              CONT, COR, PHI, SMD_adjusted,
              USER_adjusted,
              #not used
              RR, RD_stand, IRR, VAR)
    }
  }

  df_sq_list = list(
    es_odds_ratio_pval,  es_rr_pval, es_chisq, es_chisq_pval,
    es_md_pval, es_anova_f,  es_anova_f_pval, es_r_point_bis_pval,
    es_etasq, es_mean_change_pval, es_paired_t_pval, es_paired_f,
    es_paired_f_pval, es_ancova_f, es_ancova_f_pval, es_etasq_adj, es_ancova_md_pval
  )
  warning_reverse = FALSE
  for (i in seq_along(df_sq_list)) {
    df <- df_sq_list[[i]]
    ci_lo_cols <- grep("ci_lo", names(df), value = TRUE)

    if (length(ci_lo_cols) > 0) {
      for (col in ci_lo_cols) {
        if (any(!is.na(df[[col]]))) {
          warning_reverse = TRUE
          break
        }
      }
    }
  }

  if (warning_reverse & verbose) {
    warning("When you enter input data that cannot be negative (F-test, eta-squared, p-value, or chi-square values), do not forget to properly set up the direction of the generated effect size using corresponding reverse_* argument!")
  }
  expected_length <- if (measure %in% c("dw", "gw", "mdw")) {
    9
  } else if (measure == "prop") {
    3
  } else if (measure %in% c("alpha", "icc", "loghr", "hr")) {
    2
  } else if (measure %in% c("rp", "zp")) {
    5
  } else {
    76
  }
  if (length(res) != expected_length) {
    stop ("failure to estimate all effect sizes (not expected, you trigerred a bug, please contact cgosling@parisnanterre.fr). Expected ", expected_length, ", got ", length(res))
  }
  class(res) <- "metaConvert"
  attr(res, "raw_data") <- x
  attr(res, "exp") <- exp
  attr(res, "measure") <- measure
  attr(res, "split_adjusted") <- split_adjusted
  attr(res, "es_selected") <- es_selected
  attr(res, "format_adjusted") <- format_adjusted
  attr(res, "hierarchy") <- hierarchy
  attr(res, "main_es") <- main_es
  flag_opts <- .default_flag_options()
  flag_opts[names(flag_options)] <- flag_options
  attr(res, "flag_options") <- flag_opts
  attr(res, "input_validation") <- validation$issues
  # rows where r_pre_post was defaulted (used by the summary flags)
  attr(res, "r_defaulted") <- .r_defaulted
  attr(res, "alpha_to_es") <- alpha_to_es
  attr(res, "icc_to_es") <- icc_to_es
  attr(res, "prop_to_es") <- prop_to_es
  # standardizer used by the pre/post routes (drives the E6/E7 mixing flags)
  attr(res, "pre_post_to_smd") <- pre_post_to_smd
  attr(res, "pool_sd") <- pool_sd
  # per-row endpoint-SMD standardizer ("pooled"/"control"/"control_robust"),
  # used by the summary A6 CI-width check (Glass CIs are on qt(.975, n_nexp-1))
  attr(res, "smd_denom_used") <- .normalize_smd_denom(x[, "smd_denom"])
  return(res)
}
# x_save2 = x; list_df = df_es; ordering = ordering_crude; digits = digits;
# suffix = "_crude"; measure = measure
#
# x = dat; exp = TRUE; es_selected = "hierarchy"
# split_adjusted = TRUE; measure = "d_to_or, smd_to
# hierarchy = "means_sd"#hierarchy#"user_input_crude"
# digits = 3
# x$user_es_measure_crude = "ROR"
# x$user_es_measure_adj = "MOR"
# x$split_adjustedusted = x$se_adjusted = 2
# load_all(); View(convert_df(df.haza));


# x = dat
# measure = "d"
# main_es = TRUE
# split_adjusted = TRUE
# format_adjusted = "wide"
# verbose = TRUE
# es_selected = "hierarchy"
# phi_to_cor = "tetrachoric";
# chisq_to_cor = "tetrachoric";
# rr_to_or = "metaumbrella"
# or_to_rr = "metaumbrella_cases"
# or_to_cor = "bonett"
# cor_to_smd = "cooper"
# table_2x2_to_cor = "lipsey"
# yates_chisq = FALSE
# smd_to_cor = "viechtbauer"
# pre_post_to_smd = "morris"
# unit_type = "raw_scale"
# hierarchy = "rr_se"
# start_time <- Sys.time()
# end_time <- Sys.time()
# end_time - start_time
