update.distfreereg <- function(object, ..., smart = TRUE){
  override_supplied <- as.list(match.call(expand.dots = FALSE)$...$override)
  # Sometimes (only sometimes; why?), the first element is "list" as a language
  # object. This should be removed.
  if(length(override_supplied) >0 && identical(quote(list), override_supplied[[1]])){
    override_supplied <- override_supplied[-1]
  }
  override_supplied <- lapply(override_supplied, deparse)
  extras <- eval(match.call(expand.dots = FALSE)$...)
  if("override" %in% names(extras)) extras[["override"]] <- NULL
  
  old_arg_list <- as.list(getCall(object))[-1]
  
  if(!isTRUE(smart)){
    update.default(object = object, ...)
  } else {
    # First prepare for dealing with "override".
    
    n_old <- nrow(object[["J"]])
    p_old <- ncol(object[["J"]])
    
    dots <- list(...)
    n_all <- sapply(list(Y = dots[["Y"]], X = dots[["X"]],
                         data = dots[["data"]], J =
                             dots[["override"]][["J"]],
                         fitted_values =
                             dots[["override"]][["fitted_values"]]),
                    NROW)
    n_new <- setdiff(unique(n_all), 0)
    p_all <- as.numeric(c(sapply(dots[["override"]]["J"], NCOL),
                          sapply(c(dots["theta_init"],
                                   dots[["override"]][["theta_hat"]]),
                                 length)))
    p_new <- setdiff(unique(p_all), 0)
    
    if(length(n_new) == 0) n_new <- NULL
    if(length(p_new) == 0) p_new <- NULL
    if(length(n_new) > 1)
        stop("Sample size mismatch in update() arguments:\n",
             paste(names(n_all), n_all, sep = ": ", collapse = "\n"))
    if(length(p_new) > 1)
        stop("Parameter space dimension mismatch in arguments to update()")
    
    # Create a list of user inputs that should trigger recomputation of
    # values. For example, if user calls "update(object, X = X1)", then
    # object[["res_order"]] should not be used as an override. If none of X,
    # data, and ordering are specified in the call to update(), then it
    # should. The argument "verbose" is omitted from consideration here. Note
    # that the handling of "data" is not optimal, since "data" might change
    # without changing covariate values, which would not require updating
    # res_order.
    var_list <- list(
      theta_hat     = c("covariance", "data", "test_mean", "theta_init", "X",
                        "Y", "method", "method_args"),
      fitted_values = c("covariance", "data", "test_mean", "theta_init", "X",
                        "Y", "method", "method_args"),
      J             = c("covariance", "data", "test_mean", "theta_init", "X",
                        "Y", "method", "method_args"),
      res_order     = c("data", "X", "ordering", "group"),
      r             = c("data", "X", "ordering", "test_mean"),
      theoretical_stats   = c("B", "group", "ordering", "stat")
    )
    # Since some values are hidden within the control list, there is a
    # separate list analogous to var_list that contains the names of the
    # control list elements that should trigger recomputation.
    control_list <- list(
      theta_hat = c("solve_tol", "optimization_fun", "optimization_args"),
      J = c("jacobian_args"),
      r = c("qr_tol", "orth_tol")
    )
    # Next, since some values are hidden within the override list, there is a
    # separate list analogous to control_list. Note that it is not named
    # "override_supplied", since that name is used elsewhere.
    or_list <- list(
      J = c("theta_hat"),
      fitted_values = c("theta_hat"),
      r = c("res_order"),
      theoretical_stats = c("res_order", "r")
    )
    
    # Finally, define a list of custom tests for updating each variable. This
    # is important, for example, to avoid updating r when Y changes but
    # length(Y) does not. Each expression should evaluate to a single logical
    # value, and should be TRUE when var needs to be recalculated (that is,
    # not included in the override argument).
    custom_list <- list(
      res_order =
        isTRUE(!is.null(n_new) && n_new != n_old) ||
        isTRUE(isModel(eval(extras[["test_mean"]], envir = parent.frame()))),
      theta_hat = 
        isTRUE(is(object[["test_mean"]], "formula") || isModel(object[["test_mean"]])),
      r =
        isTRUE(!is.null(n_new) && n_new != n_old) ||
        isTRUE(!is.null(p_new) && p_new != p_old),
      theoretical_stats =
        isTRUE(!is.null(n_new) && n_new != n_old) ||
        isTRUE(!is.null(p_new) && p_new != p_old) ||
        isTRUE((isModel(object[["test_mean"]]) || is(object[["test_mean"]], "formula")) &&
                 !is.null(extras[["test_mean"]])) ||
        isTRUE(isTRUE(old_arg_list[["group"]]) && !is.null(extras[["ordering"]])) ||
        isTRUE(isTRUE(old_arg_list[["group"]]) && !is.null(extras[["X"]])) ||
        isTRUE(isTRUE(old_arg_list[["group"]]) && !is.null(extras[["data"]]))
    )
    
    # Now, for each variable in any of the lists above, set override_supplied[[var]]
    # to appropriate value from object unless a new argument should prevent it.
    override_candidates <- unique(c(names(var_list), names(control_list), names(or_list), names(custom_list)))
    include_in_override <- rep(FALSE, length(override_candidates))
    names(include_in_override) <- override_candidates
    for(var in names(include_in_override)){
      if(is.null(override_supplied[[var]]) &&
         !isTRUE(any(any(var_list[[var]] %in% names(extras)),
                     any(control_list[[var]] %in% names(extras[["control"]])),
                     any(or_list[[var]] %in% names(override_supplied)),
                     custom_list[[var]]))){
        # override_supplied[[var]] <- object[[var]]
        include_in_override[[var]] <- TRUE
      }
    }
    
    include_in_override <- names(include_in_override)[which(sapply(include_in_override, isTRUE))]
    
    if(length(include_in_override) > 0){
      override_smart <- paste0(deparse1(substitute(object)), "[['", include_in_override, "']]")
      names(override_smart) <- include_in_override
    } else {
      override_smart <- NULL
    }
    
    orig_call <- as.list(getCall(object))
    orig_func <- orig_call[[1]]
    
    if((is.null(orig_call[["test_mean"]]) && !("test_mean" %in% names(extras))) ||
       ("test_mean" %in% names(extras) && is.null(extras[["test_mean"]]))){
      # This accounts for the possibility that we need to reuse override values
      # when no test_mean value is provided.
      override_orig_list <- as.list(orig_call[["override"]])[c("fitted_values", "J")]
    } else {
      override_orig_list <- NULL
    }
    
    override_supplied_list <- lapply(override_supplied, str2lang)
    override_smart_list <- lapply(override_smart, str2lang)
    override_combined_list <- combine_lists(
      override_supplied_list,
      override_orig_list,
      override_smart_list
    )
    override_list <- as.call(c(quote(list), override_combined_list))
    override_list <- list(override = override_list)
    
    # Second, deal with covariance, which is not part of the "override"
    # paradigm.
    
    if(is.null(extras[["covariance"]]) &&
       (is(object[["test_mean"]], "function") || is.null(object[["test_mean"]]))){
      # extras[["covariance"]] <- object[["covariance"]]
      extras[["covariance"]] <- str2lang(paste0(deparse1(substitute(object)), "[['covariance']]"))
    }
    
    orig_call[[1]] <- orig_call[["override"]] <- orig_call[["covariance"]] <- NULL
    
    cl <- as.call(combine_lists(list(orig_func), extras, orig_call, override_list))
    
    eval(cl, envir = parent.frame())
  }
}
