compare <-
  function(true_mean,
           true_method = NULL,
           true_method_args = NULL,
           true_covariance = NULL,
           true_X = NULL,
           true_data = NULL,
           theta = NULL,
           n = NULL,
           reps = 1e3,
           prog = reps/10,
           simulate_args = NULL,
           err_dist_fun = NULL,
           err_dist_args = NULL,
           keep = NULL,
           manual = NULL,
           update_args = NULL,
           ...){
    cl <- match.call()
    supplied_dfr_arg_list <- as.list(match.call(expand.dots = FALSE)$...)
    arg_list <- list(...)
    
    vargs <- validate_args_compare(true_mean = true_mean,
                                   true_method = true_method,
                                   true_method_args = true_method_args,
                                   true_covariance = true_covariance,
                                   true_X = true_X,
                                   true_data = true_data,
                                   theta = theta,
                                   n = n,
                                   reps = reps,
                                   prog = prog,
                                   err_dist_fun = err_dist_fun,
                                   err_dist_args = err_dist_args,
                                   keep = keep,
                                   manual = manual,
                                   update_args = update_args,
                                   arg_list = arg_list)
    
    test_mean <- vargs[["test_mean"]]; err_dist_fun <- vargs[["err_dist_fun"]]
    true_X <- vargs[["true_X"]];  X <- vargs[["X"]];
    test_Y <- vargs[["test_Y"]]; data <- vargs[["data"]];
    n <- vargs[["n"]]; reps <- vargs[["reps"]]; prog <- vargs[["prog"]]
    keep <- vargs[["keep"]]
    matsqrt_tol <- vargs[["matsqrt_tol"]]; solve_tol <- vargs[["solve_tol"]]
    covariance <- supplied_dfr_arg_list[["covariance"]]
    
    if(isModel(test_mean)){
      data <- get_data(test_mean)
    }
    
    if(is.function(true_mean) || is(true_mean, "nls") || identical(true_method, "nls")){
      message("Calculating required true covariance specification matrices...")
      err_dist_fun_formals <- names(formals(err_dist_fun))
      possible_cov_args <- c("Sigma", "SqrtSigma", "P", "Q")
      cov_args <- possible_cov_args[which(possible_cov_args %in% err_dist_fun_formals)]
      true_covariance <- fill_covariance_list(need = intersect(cov_args, possible_cov_args),
                                              covariance_list = true_covariance,
                                              matsqrt_tol = matsqrt_tol,
                                              solve_tol = solve_tol)
    } else {
      err_dist_fun_formals <- NULL
      cov_args <- NULL
      true_covariance <- NULL
    }
    
    Y <- simulate_response(true_mean = true_mean, true_method = true_method,
                           true_method_args = true_method_args,
                           true_covariance = true_covariance, true_X = true_X,
                           true_data = true_data, simulate_args = simulate_args,
                           err_dist_fun = err_dist_fun, err_dist_args = err_dist_args,
                           err_dist_fun_formals = err_dist_fun_formals,
                           cov_args = cov_args, solve_tol = solve_tol,
                           reps = reps, n = n, theta = theta)
    
    if(!is.null(manual)) manual_output <- vector(mode = "list", length = reps)
    if(identical(keep, "all")) keep <- 1:reps
    if(!is.null(keep)) dfrs <- vector("list", length = 0L)
    
    output_covariance <- theoretical_stats <- obs_stats <- p_values <- NULL
    
    message("Running simulation...")
    
    if(!is.null(data)) new_data <- data
    
    for(i in seq_len(reps)){
      if(i %% prog == 0) message("Repetition ", i, " of ", reps)
      new_Y <- Y[,i]
      new_call <- list(quote(distfreereg))
      if(isModel(test_mean)){
        new_data[rownames(Y), test_Y] <- new_Y
        if(all(c("x", "y") %in% names(formals(get(as.list(getCall(test_mean))[[1]]))))){
          new_test_mean <- update(test_mean, data = new_data, x = TRUE, y = TRUE)
        } else {
          new_test_mean <- update(test_mean, data = new_data)
        }
        stopifnot(identical(length(residuals(new_test_mean)), n))
        new_call[["test_mean"]] <- quote(new_test_mean)
      } else {
        if(is.null(data)){
          new_call[["X"]] <- quote(X)
          new_call[["Y"]] <- quote(new_Y)
        } else {
          new_data[rownames(Y), test_Y] <- new_Y
          new_call[["data"]] <- quote(new_data)
        }
      }
      
      # if(!is.null(global_override)) dfr_args[["override"]] <- global_override
      
      if(i == 1){
        new_call <- combine_lists(new_call, supplied_dfr_arg_list, 
                                  list(verbose = FALSE))
        dfr <- eval(as.call(new_call))
        output_covariance <- dfr[["covariance"]]
        theoretical_stats <- dfr[["theoretical_stats"]]
        
        # On first repetition, create some output data frames with the correct
        # dimensions. Doing it here guarantees that the data frames have the
        # correct dimensions and, in particular, the correct column names when no
        # value of "stat" is specified in "...".
        obs_stats <- p_values <- as.data.frame(matrix(NA, nrow = reps,
                                                      ncol = length(dfr[["observed_stats"]])))
        colnames(obs_stats) <- colnames(p_values) <- names(dfr[["observed_stats"]])
      } else {
        # Include res_order, r, and theoretical_stats in override list to prevent
        # updating those because "X" or "data" is always in dfr_args.
        new_call[[1]] <- quote(update.distfreereg)
        new_call[["object"]] <- quote(dfr)
        new_call[["override"]] <-
          combine_lists(as.list(supplied_dfr_arg_list[["override"]])[-1],
                        list(res_order = quote(dfr[["res_order"]]),
                             r = quote(dfr[["r"]]),
                             theoretical_stats = quote(dfr[["theoretical_stats"]]))
          )
        dfr <- eval(as.call(new_call))
      }
      if(isTRUE(i %in% keep)) dfrs[[paste0("dfr_", i)]] <- dfr
      if(is(dfr[[length(dfr)]], "error")){
        warning("Error in repetition ", i, "; exiting loop")
        break
      }
      obs_stats[i,] <- unlist(dfr[["observed_stats"]])
      p_values[i,] <- unlist(dfr[["p"]][["value"]])
      if(!is.null(manual)) manual_output[[i]] <- manual(dfr)
    }
    
    
    output <- list(call = cl,
                   Y = Y,
                   theta = theta,
                   true_mean = true_mean,
                   true_covariance = true_covariance,
                   true_X = true_X,
                   true_data = true_data,
                   test_mean = test_mean,
                   covariance = output_covariance,
                   X = X,
                   data = data,
                   empirical_stats = obs_stats,
                   theoretical_stats = theoretical_stats,
                   p = p_values)
    
    if(!is.null(keep)) output[["dfrs"]] <- dfrs
    if(!is.null(manual)) output[["manual"]] <- manual_output
    class(output) <- "compare"
    return(output)
  }
