Perform the actual THn -> THn or TH1 projection.

        This projection could be to 1D, 2D, 3D, or ND.

        Args:
            hist (ROOT.THnBase): Histogram from which the projections should be performed.
        Returns:
            ROOT.THnBase or ROOT.TH1: The projected histogram.

def _project_THn(self, hist: Hist) -> Any:
        """ Perform the actual THn -> THn or TH1 projection.

        This projection could be to 1D, 2D, 3D, or ND.

        Args:
            hist (ROOT.THnBase): Histogram from which the projections should be performed.
        Returns:
            ROOT.THnBase or ROOT.TH1: The projected histogram.
        """
        # THnBase projections args are given as a list of axes, followed by any possible options.
        projection_axes = [axis.axis_type.value for axis in self.projection_axes]

        # Handle ROOT THnBase quirk...
        # 2D projection are called as (y, x, options), so we should reverse the order so it performs
        # as expected
        if len(projection_axes) == 2:
            # Reverses in place
            projection_axes.reverse()

        # Test calculating errors
        # Add "E" to ensure that errors will be calculated
        args = projection_axes + ["E"]
        # Do the actual projection
        logger.debug(f"hist: {hist.GetName()} args: {args}")

        if len(projection_axes) > 3:
            # Project into a THnBase object.
            projected_hist = hist.ProjectionND(*args)
        else:
            # Project a TH1 derived object.
            projected_hist = hist.Projection(*args)

        return projected_hist