validate_args_distfreereg <-
  function(test_mean,
           ordering,
           group,
           stat,
           B,
           control,
           override,
           verbose,
           method,
           method_args,
           theta_init,
           Y,
           X,
           data,
           covariance,
           J,
           fitted_values){
    
    output <- list()
    
    if(!is.null(test_mean) && !is(test_mean, "formula") &&
       !is.function(test_mean) && !isModel(test_mean))
      stop("'test_mean' must be NULL, a function, a formula, or an acceptable model (see documentation).")

    if(!is.null(control)){
      control_names <- c("matsqrt_tol", "solve_tol", "qr_tol", "orth_tol",
                         "trans_tol", "sym_tol", "sym_tol1", "return_on_error")
      if(is.function(test_mean)){
        control_names <- c(control_names, "jacobian_args", "optimization_fun",
                           "fun_to_optimize_arg", "optimization_args",
                           "theta_hat_name", "theta_init_arg")
      }
      validate_named_list(control, valid_names = control_names)
    }
    
    control <- combine_lists(control,
                             list(return_on_error = TRUE),
                             default_distfreereg_tol())
    validate_control_tols(control)
    
    output[["control"]] <- control
    
    return_on_error <- control[["return_on_error"]]
    if(!isTRUE(return_on_error) && !isFALSE(return_on_error))
      stop("control[['return_on_error']] must be TRUE or FALSE")
    

    # weights validated in distfreereg() after converting formula to model.
        
    if(is(test_mean, "formula")){
      validate_single_response_term(form = test_mean)
      strict_match(arg = method, choices = valid_model_methods())
      
      if(!is.data.frame(data)) stop("'data' must be a data frame")
      n <- nrow(data)
      if(n == 0) stop("'data' must have at least one row")
      
      if(!is.null(method_args) && !is.list(method_args))
        stop("method_args must be a list")
    } else {
      if(!is.null(data))
        stop("'data' argument must be NULL when 'test_mean' is not a formula.")
      if(!is.null(method) || !is.null(method_args))
        stop("'method' and 'method_args' must be NULL when 'test_mean' is not a formula.")
    }
    
    if(isModel(test_mean)){
      n <- length(fitted(test_mean))
      validate_single_response_term(form = test_mean)
    }
    
    if(is(test_mean, "formula") || isModel(test_mean)){
      if(!is.null(X) || !is.null(Y)){
        stop("'X' and 'Y' must be NULL when 'test_mean' is a formula or a model")
      }
    } else {
      Y <- validate_Y(Y = Y, verbose = verbose)
      X <- validate_X(X = X, verbose = verbose)
      n <- length(Y)
      # Verify that X and Y dimensions match.
      if(!is.null(X) && nrow(X) != n)
        stop("The number of rows in X must be the length of Y: nrow(X) is ",
             nrow(X), ", but length(Y) is ", n)
      output[["X"]] <- X
      output[["Y"]] <- Y
    }
    
    output[["n"]] <- n
    
    # Verify override.
    if(!is.null(override)){
      override_names <- c("res_order", "r", "theoretical_stats", "J", "fitted_values",
                          "theta_hat")
      validate_named_list(override, valid_names = override_names)
      
      if(!is.null(override[["theta_hat"]]))
        validate_numeric(override[["theta_hat"]],
                         message = "override[['theta_hat']] failed numeric validation: ")
      
      if(!is.null(override[["J"]])){
        validate_numeric(override[["J"]], message = "override[['J']] failed numeric validation: ")
        override[["J"]] <- tryCatch(as.matrix(override[["J"]]),
                                    error = function(e) stop("Error converting override[['J']] to a matrix: ", e))
        if(nrow(as.matrix(override[["J"]])) != n) stop("nrow(as.matrix(override[['J']])) must be equal to length(Y)") 
      }
      
      if(!is.null(override[["J"]]) && !is.null(override[["r"]]) &&
         !all(dim(as.matrix(override[["J"]])) == dim(as.matrix(override[["r"]]))))
        stop("as.matrix(override[['J']]) and as.matrix(override[['r']]) must have the same dimensions")
      
      if(!is.null(override[["res_order"]])){
        if(!isOrdering(override[["res_order"]], len = n)) stop("override[['res_order']] must be an ordering")
      }
      
      if(!is.null(override[["r"]])){
        validate_numeric(override[["r"]], message = "override[['r']] failed numeric validation: ")
        if(nrow(as.matrix(override[["r"]])) != n) stop("nrow(as.matrix(override[['r']])) must be equal to length(Y)") 
      }
      
      if(!is.null(override[["fitted_values"]])){
        validate_numeric(override[["fitted_values"]], len = n,
                         message = "override[['fitted_values']] failed numeric validation: ")
      }
    }
    
    output[["override"]] <- override
    
    if(is.function(test_mean)){
      if(is.null(override[["theta_hat"]])){
        validate_numeric(x = theta_init, min_len = 1, message = "theta_init failed numeric validation: ")
      } else {
        # This is just so function output is verified at theta_hat below.
        theta_init <- override[["theta_hat"]]
      }
    } else {
      if(!is.null(theta_init))
        stop("'theta_init' must be NULL when 'theta_hat' is not a function.")
    }
    
    
    
    # Verify covariance list.
    if(is.function(test_mean) || is.null(test_mean)){
      validate_covariance_list(covariance, n = n, sym_tol = control[["sym_tol"]],
                               sym_tol1 = control[["sym_tol1"]])
    } else {
        if(!is.null(covariance))
            stop("'covariance' must be NULL when 'test_mean' is a model object")
    }
    
    
    # Deal with optimization arguments.
    if(is.function(test_mean)){
      optimization_args <- control[["optimization_args"]]
      if(!is.null(optimization_args) && !is.list(optimization_args))
        stop("control[['optimization_args']] must be a list.")
      if(is.null(control[["optimization_fun"]])){
        if(any(!is.null(control[["fun_to_optimize_arg"]]),
               !is.null(control[["theta_init_arg"]]),
               !is.null(control[["theta_hat_name"]])))
          warning("No alternative optimization function specified; using optim()",
                  " by default, ignoring 'fun_to_optimize_arg', 'theta_init_arg', and 'theta_hat_name'.")
        control[["optimization_fun"]] <- optim
        control[["optimization_args"]] <-
          combine_lists(control[["optimization_args"]], list(method = "BFGS"))
        control[["theta_init_arg"]] <- "par"
        control[["theta_hat_name"]] <- "par"
        control[["fun_to_optimize_arg"]] <- "fn"
      }
      if(!is.function(control[["optimization_fun"]]))
        stop("control[['optimization_fun']] must be a function")
      if(is.character(control[["theta_init_arg"]])){
        if(length(control[["theta_init_arg"]]) != 1)
          stop("control[['theta_init_arg']] must have length 1.")
        if(!(control[["theta_init_arg"]] %in% names(formals(control[["optimization_fun"]]))))
          stop("control[['theta_init_arg']] must be the name of an argument of",
               " control[['optimization_fun']].")
      } else {
        stop("control[['theta_init_arg']] must be a character string.")
      }
      if(is.character(control[["fun_to_optimize_arg"]])){
        if(length(control[["fun_to_optimize_arg"]]) != 1)
          stop("control[['fun_to_optimize_arg']] must have length 1")
        if(!(control[["fun_to_optimize_arg"]] %in% names(formals(control[["optimization_fun"]]))))
          stop("control[['fun_to_optimize_arg']] must be the name of an",
               " argument of control[['optimization_fun']].")
      } else {
        stop("control[['fun_to_optimize_arg']] must be a character string.")
      }
      if(!is.character(control[["theta_hat_name"]])){
        stop("control[['theta_hat_name']] must be a character vector of length 1.")
      } else {
        if(length(control[["theta_hat_name"]]) != 1)
          stop("control[['theta_hat_name']] must have length 1.")
      }
      
      control[["optimization_args"]][[control[["theta_init_arg"]]]] <- theta_init
      
      # Verification Jacobian arguments.
      jacobian_args <- control[["jacobian_args"]]
      if(!is.null(jacobian_args) && !is.list(jacobian_args))
        stop("jacobian_args must be a list")
    }
    
    output[["control"]] <- control
    
    if(is.function(test_mean)){
      if(is.null(names(theta_init))) names(theta_init) <- paste0("theta", seq_along(theta_init))
      
      validate_mean_function_arg_names(f = test_mean)
      if(is.null(X)){
        if(any(c("x", "X") %in% names(formals(test_mean))))
          stop("X must be supplied if 'x' or 'X' is an argument of test_mean")
        if(length(theta_init) != 1) stop("theta_init must have length 1 when X is NULL")
      } else {
        if(!any(c("x", "X") %in% names(formals(test_mean))))
          stop("'x' or 'X' must be an argument of test_mean when X is not NULL")
      }
      
      validate_function_behavior(f = test_mean, X = X, n = n, theta = theta_init)
      
      if(!is.null(override[["theta_hat"]])) theta_init <- NULL
    }
    
    
    # Validate B
    validate_numeric(x = B, pos_int = TRUE, len = 1)
    output[["B"]] <- as.integer(B)
    
    
    # Validate the observation ordering specification.
    if(is.null(override[["res_order"]])){
      if(is.list(ordering)){
        if(isModel(test_mean)) data <- get_data(test_mean)
        if(!is.null(data)){
          validate_order_columns(data, ordering)
        } else {
          validate_order_columns(X, ordering)
        }
      } else {
        if(length(ordering) > 1) stop("ordering method specification must have length one, ",
                                      "or be a list of column specifications")
        strict_match(ordering, c("asis", "optimal", "simplex", "natural"))
      }
    }
    
    # Validate 'group'
    if(!isTRUE(group) && !isFALSE(group)) stop("'group' must be TRUE or FALSE")
    
    # Check stat functions for egregious errors.
    v <- vector(mode = "list", length = length(stat))
    for(i in seq_along(stat)){
      v[[i]] <- tryCatch(get(stat[i])(rnorm(100)),
                         error = function(e) stop("Unable to evaluate ",
                                                  deparse1(substitute(stat[i])),
                                                  "(rnorm(100)): ", e))
    }
    bad_ind <- which(sapply(v, function(x) isFALSE(is.numeric(x)) ||
                              isFALSE(length(x) == 1)))
    if(length(bad_ind) > 0){
      stop("The following stat functions are invalid: ",
           paste(stat[bad_ind], collapse = ", "), ".\nEach failed either ",
           "because it did not return a (non-NA) numeric value or because its ",
           "output is not a single number.")
    }
    
    return(output)
  }
