code: def ets_f(
      y,
      m,
      model="ZZZ",
      damped=None,
      alpha=None,
      beta=None,
      gamma=None,
      phi=None,
      additive_only=None,
      blambda=None,
      biasadj=None,
      lower=None,
      upper=None,
      opt_crit="lik",
      nmse=3,
      bounds="both",
      ic="aicc",
      restrict=True,
      allow_multiplicative_trend=False,
      use_initial_values=False,
      maxit=2_000,
  ):
      if y.dtype not in (np.float32, np.float64):
          y = y.astype(np.float32)
      # converting params to floats
      # to improve numba compilation
      if alpha is None:
          alpha = np.nan
      if beta is None:
          beta = np.nan
      if gamma is None:
          gamma = np.nan
      if phi is None:
          phi = np.nan
      if blambda is not None:
          raise NotImplementedError("`blambda` not None")
      if nmse < 1 or nmse > 30:
          raise ValueError("nmse out of range")
      if lower is None:
          lower = np.array([0.0001, 0.0001, 0.0001, _PHI_LOWER])
      if upper is None:
          upper = np.array([0.9999, 0.9999, 0.9999, _PHI_UPPER])
      if any(upper < lower):
          raise ValueError(
              "Lower limits must be less than upper limits"
          )
      # check if y is contant
      if is_constant(y):
          return etsmodel(
              y=y,
              m=m,
              errortype="A",
              trendtype="N",
              seasontype="N",
              alpha=0.9999,
              beta=beta,
              gamma=gamma,
              phi=phi,
              damped=False,
              lower=lower,
              upper=upper,
              opt_crit=opt_crit,
              nmse=nmse,
              bounds=bounds,
              maxit=maxit,
          )
  
      if isinstance(model, dict):
          m = model["m"]
          errortype, trendtype, seasontype = model["components"][:3]
          damped = model["components"][3] != "N"
          alpha, beta, gamma, phi = model["par"][:4]
          init_state = model["par"][4:]
          # Recompute errors from pegelsresid.C
          amse, e, states, lik = pegelsresid_C(
              y=y,
              m=m,
              init_state=init_state,
              errortype=errortype,
              trendtype=trendtype,
              seasontype=seasontype,
              damped=damped,
              alpha=alpha,
              beta=beta,
              gamma=gamma,
              phi=phi,
              nmse=nmse,
          )
          fred = model["fit"]
          nstate = len(init_state)
          np_ = model["n_params"] - 1
          np_ = np_ + 1
          ny = len(y)
          aic = lik + 2 * np_
          bic = lik + np.log(ny) * np_
          if ny - np_ - 1 != 0.0:
              aicc = aic + 2 * np_ * (np_ + 1) / (ny - np_ - 1)
          else:
              aicc = np.inf
  
          mse = amse[0]
          amse = np.mean(amse)
  
          fit_par = np.concatenate(
              [[alpha, beta, gamma, phi], init_state]
          )
          if errortype == "A":
              fits = y - e
          else:
              # protect e == -1
              aux_e = np.copy(e)
              aux_e[aux_e == -1.0] = -1 + 1e-3
              fits = y / (1 + aux_e)
          sq_e = e**2
  
          sigma2 = sq_e[~np.isinf(sq_e)].sum() / (ny - np_ - 1)
  
          return dict(
              loglik=-0.5 * lik,
              aic=aic,
              bic=bic,
              aicc=aicc,
              mse=mse,
              amse=amse,
              fit=fred,
              residuals=e,
              components=f"{errortype}{trendtype}{seasontype}{'D' if damped else 'N'}",
              m=m,
              nstate=nstate,
              fitted=fits,
              states=states,
              par=fit_par,
              sigma2=sigma2,
              n_params=np_,
          )
  
      errortype, trendtype, seasontype = model
      if errortype not in ["M", "A", "Z"]:
          raise ValueError("Invalid error type")
      if trendtype not in ["N", "A", "M", "Z"]:
          raise ValueError("Invalid trend type")
      if seasontype not in ["N", "A", "M", "Z"]:
          raise ValueError("Invalid season type")
      if m < 1 or len(y) <= m:
          seasontype = "N"
      if m == 1:
          if seasontype == "A" or seasontype == "M":
              raise ValueError("Nonseasonal data")
          else:
              # model[3] = 'N'
              seasontype = "N"
      if restrict:
          if (
              (
                  errortype == "A"
                  and (trendtype == "M" or seasontype == "M")
              )
              or (
                  errortype == "M"
                  and trendtype == "M"
                  and seasontype == "A"
              )
              or (
                  additive_only
                  and (
                      errortype == "M"
                      or trendtype == "M"
                      or seasontype == "M"
                  )
              )
          ):
              raise ValueError("Forbidden model combination")
      data_positive = min(y) > 0
      if (not data_positive) and errortype == "M":
          raise ValueError(
              "Inappropriate model for data with negative or zero values"
          )
      if damped is not None:
          if damped and trendtype == "N":
              ValueError("Forbidden model combination")
      n = len(y)
      npars = 2  # alpha + l0
      if trendtype in ["A", "M"]:
          npars += 2  # beta + b0
      if seasontype in ["A", "M"]:
          npars += 2  # gamma + s
      if damped is not None:
          npars += damped
      # ses for non-optimized tiny datasets
      if n <= npars + 4:
          # we need HoltWintersZZ function
          raise NotImplementedError("tiny datasets")
      # fit model (assuming only one nonseasonal model)
      if errortype == "Z":
          errortype = ["A", "M"]
      if trendtype == "Z":
          trendtype = ["N", "A"]
          if allow_multiplicative_trend:
              trendtype += ["M"]
      if seasontype == "Z":
          seasontype = ["N", "A", "M"]
      if damped is None:
          damped = [True, False]
      else:
          damped = [damped]
      best_ic = np.inf
      for etype in errortype:
          for ttype in trendtype:
              for stype in seasontype:
                  for dtype in damped:
                      if ttype == "N" and dtype:
                          continue
                      if restrict:
                          if etype == "A" and (
                              ttype == "M" or stype == "M"
                          ):
                              continue
                          if (
                              etype == "M"
                              and ttype == "M"
                              and stype == "A"
                          ):
                              continue
                          if additive_only and (
                              etype == "M"
                              or ttype == "M"
                              or stype == "M"
                          ):
                              continue
                      if (not data_positive) and etype == "M":
                          continue
                      if (not data_positive) and stype == "M":
                          # see https://github.com/statsmodels/statsmodels/blob/46116c493697b5456e960b1dc2932264703b6c59/statsmodels/tsa/seasonal.py#L157
                          continue
                      if stype != "N" and m == 1:
                          continue
                      fit = etsmodel(
                          y,
                          m,
                          etype,
                          ttype,
                          stype,
                          dtype,
                          alpha,
                          beta,
                          gamma,
                          phi,
                          lower=lower,
                          upper=upper,
                          opt_crit=opt_crit,
                          nmse=nmse,
                          bounds=bounds,
                          maxit=maxit,
                      )
                      fit_ic = fit[ic]
                      if not np.isnan(fit_ic):
                          if fit_ic < best_ic:
                              model = fit
                              best_ic = fit_ic
                              best_e = etype
                              best_t = ttype
                              best_s = stype
                              best_d = dtype
      if np.isinf(best_ic):
          raise Exception("no model able to be fitted")
      model[
          "method"
      ] = f"ETS({best_e},{best_t}{'d' if best_d else ''},{best_s})"
      return model
  
imports: import math
  from collections import namedtuple
  from typing import Tuple
  import numpy
  from numba import njit
  from numba.typed import List
  from statsmodels.tsa.seasonal import seasonal_decompose
  from .utils import _calculate_intervals, CACHE, NOGIL
label_desc: Use image node_id 21 for calling a global function with example usage: ets_f(y, m, model, damped, alpha, beta, gamma, phi, additive_only, blambda, biasadj, lower, upper, opt_crit, nmse, bounds, ic, restrict, allow_multiplicative_trend, use_initial_values, maxit) and returns: model, etsmodel, dict