docs: def calc_and_store_post_estimation_results(results_dict,
                                             estimator):
      """
      Calculates and stores post-estimation results that require the use of the
      systematic utility transformation functions or the various derivative
      functions. Note that this function is only valid for logit-type models.
  
      Parameters
      ----------
      results_dict : dict.
          This dictionary should be the dictionary returned from
          scipy.optimize.minimize. In particular, it should have the following
          keys: `["fun", "x", "log_likelihood_null"]`.
      estimator : an instance of the EstimationObj class.
          Should contain the following attributes or methods:
  
            - convenience_split_params
            - convenience_calc_probs
            - convenience_calc_gradient
            - convenience_calc_hessian
            - convenience_calc_fisher_approx
            - choice_vector
            - rows_to_obs
  
      Returns
      -------
      results_dict : dict.
          The following keys will have been entered into `results_dict`:
  
            - final_log_likelihood
            - utility_coefs
            - intercept_params
            - shape_params
            - nest_params
            - chosen_probs
            - long_probs
            - residuals
            - ind_chi_squareds
            - rho_squared
            - rho_bar_squared
            - final_gradient
            - final_hessian
            - fisher_info
      """
      # Store the final log-likelihood
      final_log_likelihood = -1 * results_dict["fun"]
      results_dict["final_log_likelihood"] = final_log_likelihood
  
      # Get the final array of estimated parameters
      final_params = results_dict["x"]
  
      # Add the estimated parameters to the results dictionary
      split_res = estimator.convenience_split_params(final_params,
                                                     return_all_types=True)
      results_dict["nest_params"] = split_res[0]
      results_dict["shape_params"] = split_res[1]
      results_dict["intercept_params"] = split_res[2]
      results_dict["utility_coefs"] = split_res[3]
  
      # Get the probability of the chosen alternative and long_form probabilities
      chosen_probs, long_probs = estimator.convenience_calc_probs(final_params)
      results_dict["chosen_probs"] = chosen_probs
      results_dict["long_probs"] = long_probs
  
      #####
      # Calculate the residuals and individual chi-square values
      #####
      # Calculate the residual vector
      if len(long_probs.shape) == 1:
          residuals = estimator.choice_vector - long_probs
      else:
          residuals = estimator.choice_vector[:, None] - long_probs
      results_dict["residuals"] = residuals
  
      # Calculate the observation specific chi-squared components
      args = [residuals, long_probs, estimator.rows_to_obs]
      results_dict["ind_chi_squareds"] = calc_individual_chi_squares(*args)
  
      # Calculate and store the rho-squared and rho-bar-squared
      log_likelihood_null = results_dict["log_likelihood_null"]
      rho_results = calc_rho_and_rho_bar_squared(final_log_likelihood,
                                                 log_likelihood_null,
                                                 final_params.shape[0])
      results_dict["rho_squared"] = rho_results[0]
      results_dict["rho_bar_squared"] = rho_results[1]
  
      #####
      # Calculate the gradient, hessian, and BHHH approximation to the fisher
      # info matrix
      #####
      results_dict["final_gradient"] =\
          estimator.convenience_calc_gradient(final_params)
      results_dict["final_hessian"] =\
          estimator.convenience_calc_hessian(final_params)
      results_dict["fisher_info"] =\
          estimator.convenience_calc_fisher_approx(final_params)
  
      # Store the constrained positions that was used in this estimation process
      results_dict["constrained_pos"] = estimator.constrained_pos
  
      return results_dict