///|
const NUMERIC_RECURSION_BASE = 0.0

///|
const NUMERIC_RECURSION_STEP = 1.0

///|
const NUMERIC_RECURSION_INITIAL_ARGUMENT = 256.0

///|
#warnings("-unused_field")
priv struct NumericRecursionExpressionRecipe {
  base_condition_loc : @token.Loc
  recursive_add_loc : @token.Loc
  recursive_call_loc : @token.Loc
  recursive_subtract_loc : @token.Loc
}

///|
/// Closed, effect-free numeric argument admitted from one normal-form Number
/// literal or one grouping around a comma tree of normal-form Number literals.
/// The shell accounts for every represented AST node through this budget.
#warnings("-unused_field")
priv struct NumericRecursionClosedArgumentRecipe {
  value : Double
  observation_count : Int
  root_loc : @token.Loc
  final_loc : @token.Loc
}

///|
fn NumericRecursionClosedArgumentRecipe::NumericRecursionClosedArgumentRecipe(
  value~ : Double,
  observation_count~ : Int,
  root_loc~ : @token.Loc,
  final_loc~ : @token.Loc,
) -> NumericRecursionClosedArgumentRecipe {
  { value, observation_count, root_loc, final_loc }
}

///|
fn NumericRecursionExpressionRecipe::NumericRecursionExpressionRecipe(
  base_condition_loc~ : @token.Loc,
  recursive_add_loc~ : @token.Loc,
  recursive_call_loc~ : @token.Loc,
  recursive_subtract_loc~ : @token.Loc,
) -> NumericRecursionExpressionRecipe {
  {
    base_condition_loc,
    recursive_add_loc,
    recursive_call_loc,
    recursive_subtract_loc,
  }
}

///|
#warnings("-unused_constructor-unused_field")
priv enum NumericRecursionInvocationRecipe {
  NumericRecursionDirectCall
  NumericRecursionIntrinsicApplyArray
  NumericRecursionIntrinsicApplyMappedArguments
} derive(Eq)

///|
#warnings("-unused_field")
priv struct NumericRecursionFunctionSyntax {
  name : String
  parameter : String
  retained_parameter : String?
  recursive_callee : String
  invocation_recipe : NumericRecursionInvocationRecipe
  expression_recipe : NumericRecursionExpressionRecipe
  return_recipe : NumericRecursionFunctionReturnRecipe
}

///|
fn NumericRecursionFunctionSyntax::NumericRecursionFunctionSyntax(
  name~ : String,
  parameter~ : String,
  retained_parameter? : String? = None,
  recursive_callee~ : String,
  invocation_recipe? : NumericRecursionInvocationRecipe = NumericRecursionDirectCall,
  expression_recipe~ : NumericRecursionExpressionRecipe,
  return_recipe? : NumericRecursionFunctionReturnRecipe = NumericRecursionDirectReturn,
) -> NumericRecursionFunctionSyntax {
  {
    name,
    parameter,
    retained_parameter,
    recursive_callee,
    invocation_recipe,
    expression_recipe,
    return_recipe,
  }
}

///|
#warnings("-unused_field")
priv struct NumericRecursionFunctionPlan {
  syntax : NumericRecursionFunctionSyntax
  declaration_index : Int
}

///|
fn NumericRecursionFunctionPlan::NumericRecursionFunctionPlan(
  syntax~ : NumericRecursionFunctionSyntax,
  declaration_index~ : Int,
) -> NumericRecursionFunctionPlan {
  { syntax, declaration_index }
}

///|
#warnings("-unused_constructor")
priv enum NumericRecursionRootCallCompletion {
  NumericRecursionRootCallValue
  NumericRecursionRootCallThrow
}

///|
#warnings("-unused_constructor-unused_field")
priv enum NumericRecursionClosedBodyRecipe {
  NumericRecursionClosedBodyEmpty
  NumericRecursionClosedNumberValue(Double)
  NumericRecursionClosedNumberThrow(Double)
  NumericRecursionClosedBindingValue
  NumericRecursionClosedBindingThrow
}

///|
#warnings("-unused_constructor-unused_field")
priv enum NumericRecursionFunctionReturnRecipe {
  NumericRecursionDirectReturn
  NumericRecursionProtectedReturn(NumericRecursionClosedBodyRecipe)
}

///|
#warnings("-unused_constructor-unused_field")
priv enum NumericRecursionCatchRecipe {
  NumericRecursionNoCatch
  NumericRecursionCatch(String?, NumericRecursionClosedBodyRecipe)
}

///|
#warnings("-unused_constructor-unused_field")
priv enum NumericRecursionFinalizerRecipe {
  NumericRecursionNoFinalizer
  NumericRecursionFinalizer(NumericRecursionClosedBodyRecipe)
}

///|
#warnings("-unused_field")
priv struct NumericRecursionProtectedRootRecipe {
  try_completion : NumericRecursionRootCallCompletion
  catch_recipe : NumericRecursionCatchRecipe
  finalizer_recipe : NumericRecursionFinalizerRecipe
}

///|
fn NumericRecursionProtectedRootRecipe::NumericRecursionProtectedRootRecipe(
  try_completion~ : NumericRecursionRootCallCompletion,
  catch_recipe~ : NumericRecursionCatchRecipe,
  finalizer_recipe~ : NumericRecursionFinalizerRecipe,
) -> NumericRecursionProtectedRootRecipe {
  { try_completion, catch_recipe, finalizer_recipe }
}

///|
#warnings("-unused_constructor-unused_field")
priv enum NumericRecursionRootControlRecipe {
  NumericRecursionDirectRoot
  NumericRecursionProtectedRoot(NumericRecursionProtectedRootRecipe)
}

///|
#warnings("-unused_field")
priv struct NumericRecursionPlan {
  entry : NumericRecursionFunctionPlan
  peer : NumericRecursionFunctionPlan?
  initial_argument : Double
  retained_argument : NumericRecursionClosedArgumentRecipe?
  root_call_loc : @token.Loc
  root_control : NumericRecursionRootControlRecipe
}

///|
fn NumericRecursionPlan::NumericRecursionPlan(
  entry~ : NumericRecursionFunctionPlan,
  peer~ : NumericRecursionFunctionPlan?,
  initial_argument~ : Double,
  retained_argument? : NumericRecursionClosedArgumentRecipe? = None,
  root_call_loc~ : @token.Loc,
  root_control~ : NumericRecursionRootControlRecipe,
) -> NumericRecursionPlan {
  {
    entry,
    peer,
    initial_argument,
    retained_argument,
    root_call_loc,
    root_control,
  }
}

///|
fn exact_numeric_recursion_number(expr : @ast.Expr, expected : Double) -> Bool {
  match expr {
    @ast.NumberLit(value, lex_form, _) =>
      value == expected && lex_form == @token.LexForm::LexNormal
    _ => false
  }
}

///|
fn numeric_recursion_identifier_is_safe(name : String) -> Bool {
  name != "eval" && name != "arguments"
}

///|
fn numeric_recursion_base_condition_loc(
  stmt : @ast.Stmt,
  parameter : String,
  retained_parameter? : String? = None,
) -> @token.Loc? {
  match stmt {
    @ast.IfStmt(
      @ast.Binary(
        @ast.EqEqEq,
        @ast.Ident(condition_parameter, _),
        base_condition,
        base_condition_loc
      ),
      @ast.ReturnStmt(Some(base_result), _),
      None,
      _
    ) => {
      let base_result_matches = match retained_parameter {
        None =>
          exact_numeric_recursion_number(base_result, NUMERIC_RECURSION_BASE)
        Some(expected) =>
          match base_result {
            @ast.Ident(actual, _) => actual == expected
            _ => false
          }
      }
      if condition_parameter == parameter &&
        exact_numeric_recursion_number(base_condition, NUMERIC_RECURSION_BASE) &&
        base_result_matches {
        Some(base_condition_loc)
      } else {
        None
      }
    }
    _ => None
  }
}

///|
#warnings("-unused_field")
priv struct NumericRecursionStepClassification {
  recursive_callee : String
  invocation_recipe : NumericRecursionInvocationRecipe
  recursive_add_loc : @token.Loc
  recursive_call_loc : @token.Loc
  recursive_subtract_loc : @token.Loc
}

///|
fn NumericRecursionStepClassification::NumericRecursionStepClassification(
  recursive_callee~ : String,
  invocation_recipe? : NumericRecursionInvocationRecipe = NumericRecursionDirectCall,
  recursive_add_loc~ : @token.Loc,
  recursive_call_loc~ : @token.Loc,
  recursive_subtract_loc~ : @token.Loc,
) -> NumericRecursionStepClassification {
  {
    recursive_callee,
    invocation_recipe,
    recursive_add_loc,
    recursive_call_loc,
    recursive_subtract_loc,
  }
}

///|
fn classify_numeric_recursion_step_detail(
  stmt : @ast.Stmt,
  parameter : String,
  retained_parameter? : String? = None,
) -> NumericRecursionStepClassification? {
  match stmt {
    @ast.ReturnStmt(
      Some(@ast.Binary(@ast.Add, increment, call_expr, recursive_add_loc)),
      _
    ) => {
      guard exact_numeric_recursion_number(increment, NUMERIC_RECURSION_STEP) else {
        return None
      }
      match call_expr {
        @ast.Call(@ast.Ident(recursive_callee, _), args, recursive_call_loc) => {
          let expected_argument_count = match retained_parameter {
            None => 1
            Some(_) => 2
          }
          guard args.length() == expected_argument_count else { return None }
          let retained_argument_matches = match retained_parameter {
            None => true
            Some(expected) =>
              match args[1] {
                @ast.Ident(actual, _) => actual == expected
                _ => false
              }
          }
          guard retained_argument_matches else { return None }
          match args[0] {
            @ast.Binary(
              @ast.Sub,
              @ast.Ident(step_parameter, _),
              decrement,
              recursive_subtract_loc
            ) if step_parameter == parameter &&
              exact_numeric_recursion_number(decrement, NUMERIC_RECURSION_STEP) =>
              Some(
                NumericRecursionStepClassification(
                  recursive_callee~,
                  invocation_recipe=NumericRecursionDirectCall,
                  recursive_add_loc~,
                  recursive_call_loc~,
                  recursive_subtract_loc~,
                ),
              )
            _ => None
          }
        }
        @ast.Call(
          @ast.Member(@ast.Ident(recursive_callee, _), "apply", _),
          args,
          recursive_call_loc
        ) => {
          guard retained_parameter is None && args.length() == 2 else {
            return None
          }
          guard args[0] is @ast.Ident("undefined", _) else { return None }
          match args[1] {
            @ast.ArrayLit(elements, _) if elements.length() == 1 =>
              match elements[0] {
                @ast.Binary(
                  @ast.Sub,
                  @ast.Ident(step_parameter, _),
                  decrement,
                  recursive_subtract_loc
                ) if step_parameter == parameter &&
                  exact_numeric_recursion_number(
                    decrement,
                    NUMERIC_RECURSION_STEP,
                  ) =>
                  Some(
                    NumericRecursionStepClassification(
                      recursive_callee~,
                      invocation_recipe=NumericRecursionIntrinsicApplyArray,
                      recursive_add_loc~,
                      recursive_call_loc~,
                      recursive_subtract_loc~,
                    ),
                  )
                _ => None
              }
            _ => None
          }
        }
        _ => None
      }
    }
    _ => None
  }
}

///|
fn classify_numeric_recursion_function_return(
  stmt : @ast.Stmt,
  parameter : String,
  retained_parameter? : String? = None,
) -> (NumericRecursionStepClassification, NumericRecursionFunctionReturnRecipe)? {
  match
    classify_numeric_recursion_step_detail(stmt, parameter, retained_parameter~) {
    Some(step) => Some((step, NumericRecursionDirectReturn))
    None =>
      match stmt {
        @ast.TryCatchStmt(try_body, None, None, Some(finalizer_body), _) => {
          guard try_body.length() == 1 &&
            classify_numeric_recursion_step_detail(
              try_body[0],
              parameter,
              retained_parameter~,
            )
            is Some(step) else {
            return None
          }
          match classify_numeric_recursion_closed_body(finalizer_body, None) {
            Some(
              NumericRecursionClosedBodyEmpty
              | NumericRecursionClosedNumberValue(_)
              | NumericRecursionClosedNumberThrow(_) as finalizer
            ) => Some((step, NumericRecursionProtectedReturn(finalizer)))
            _ => None
          }
        }
        _ => None
      }
  }
}

///|
fn classify_numeric_recursion_mapped_apply_assignment(
  stmt : @ast.Stmt,
  parameter : String,
) -> @token.Loc? {
  match stmt {
    @ast.ExprStmt(
      @ast.ComputedAssign(
        @ast.Ident("arguments", _),
        @ast.NumberLit(0.0, @token.LexForm::LexNormal, _),
        @ast.Binary(
          @ast.Sub,
          @ast.Ident(step_parameter, _),
          decrement,
          subtract_loc
        ),
        _
      ),
      _
    ) if step_parameter == parameter &&
      exact_numeric_recursion_number(decrement, NUMERIC_RECURSION_STEP) =>
      Some(subtract_loc)
    _ => None
  }
}

///|
fn classify_numeric_recursion_mapped_apply_step(
  stmt : @ast.Stmt,
  recursive_subtract_loc : @token.Loc,
) -> NumericRecursionStepClassification? {
  match stmt {
    @ast.ReturnStmt(
      Some(
        @ast.Binary(
          @ast.Add,
          increment,
          @ast.Call(
            @ast.Member(@ast.Ident(recursive_callee, _), "apply", _),
            args,
            recursive_call_loc
          ),
          recursive_add_loc
        )
      ),
      _
    ) => {
      guard exact_numeric_recursion_number(increment, NUMERIC_RECURSION_STEP) &&
        args.length() == 2 else {
        return None
      }
      guard args[0] is @ast.Ident("undefined", _) else { return None }
      guard args[1] is @ast.Ident("arguments", _) else { return None }
      Some(
        NumericRecursionStepClassification(
          recursive_callee~,
          invocation_recipe=NumericRecursionIntrinsicApplyMappedArguments,
          recursive_add_loc~,
          recursive_call_loc~,
          recursive_subtract_loc~,
        ),
      )
    }
    _ => None
  }
}

///|
fn classify_numeric_recursion_function_syntax(
  name : String,
  params : Array[String],
  body : Array[@ast.Stmt],
) -> NumericRecursionFunctionSyntax? {
  guard numeric_recursion_identifier_is_safe(name) &&
    (params.length() == 1 || params.length() == 2) &&
    (body.length() == 2 || body.length() == 3) else {
    return None
  }
  let parameter = params[0]
  let retained_parameter = if params.length() == 2 {
    Some(params[1])
  } else {
    None
  }
  guard numeric_recursion_identifier_is_safe(parameter) && parameter != name else {
    return None
  }
  guard (match retained_parameter {
    None => true
    Some(retained) =>
      numeric_recursion_identifier_is_safe(retained) &&
      retained != name &&
      retained != parameter
  }) else {
    return None
  }
  guard numeric_recursion_base_condition_loc(
      body[0],
      parameter,
      retained_parameter~,
    )
    is Some(base_condition_loc) else {
    return None
  }
  let (step, return_recipe) = if body.length() == 2 {
    match
      classify_numeric_recursion_function_return(
        body[1],
        parameter,
        retained_parameter~,
      ) {
      Some(result) => result
      None => return None
    }
  } else {
    guard params.length() == 1 && retained_parameter is None else {
      return None
    }
    let recursive_subtract_loc = match
      classify_numeric_recursion_mapped_apply_assignment(body[1], parameter) {
      Some(loc) => loc
      None => return None
    }
    match
      classify_numeric_recursion_mapped_apply_step(
        body[2],
        recursive_subtract_loc,
      ) {
      Some(step) => (step, NumericRecursionDirectReturn)
      None => return None
    }
  }
  Some(
    NumericRecursionFunctionSyntax(
      name~,
      parameter~,
      retained_parameter~,
      recursive_callee=step.recursive_callee,
      invocation_recipe=step.invocation_recipe,
      expression_recipe=NumericRecursionExpressionRecipe(
        base_condition_loc~,
        recursive_add_loc=step.recursive_add_loc,
        recursive_call_loc=step.recursive_call_loc,
        recursive_subtract_loc=step.recursive_subtract_loc,
      ),
      return_recipe~,
    ),
  )
}

///|
fn classify_numeric_recursion_function(
  stmt : @ast.Stmt,
  declaration_index : Int,
) -> NumericRecursionFunctionPlan? {
  match stmt {
    @ast.FuncDecl(name, params, body, _, _) =>
      match classify_numeric_recursion_function_syntax(name, params, body) {
        Some(syntax) =>
          Some(NumericRecursionFunctionPlan(syntax~, declaration_index~))
        None => None
      }
    _ => None
  }
}

///|
fn classify_numeric_recursion_root(
  stmt : @ast.Stmt,
) -> (
  String,
  Double,
  NumericRecursionClosedArgumentRecipe?,
  @token.Loc,
  NumericRecursionRootControlRecipe,
)? {
  match stmt {
    @ast.ExprStmt(@ast.Call(@ast.Ident(entry_name, _), args, root_call_loc), _) =>
      match classify_numeric_recursion_root_call_arguments(args) {
        Some((initial_argument, retained_argument)) =>
          Some(
            (
              entry_name,
              initial_argument,
              retained_argument,
              root_call_loc,
              NumericRecursionDirectRoot,
            ),
          )
        None => None
      }
    @ast.TryCatchStmt(try_body, catch_parameter, catch_body, finalizer_body, _) =>
      classify_numeric_recursion_protected_root(
        try_body, catch_parameter, catch_body, finalizer_body,
      )
    _ => None
  }
}

///|
fn classify_numeric_recursion_root_call(
  expr : @ast.Expr,
) -> (String, Double, NumericRecursionClosedArgumentRecipe?, @token.Loc)? {
  match expr {
    @ast.Call(@ast.Ident(entry_name, _), args, root_call_loc) =>
      match classify_numeric_recursion_root_call_arguments(args) {
        Some((initial_argument, retained_argument)) =>
          Some((entry_name, initial_argument, retained_argument, root_call_loc))
        None => None
      }
    _ => None
  }
}

///|
fn classify_numeric_recursion_root_call_arguments(
  args : Array[@ast.Expr],
) -> (Double, NumericRecursionClosedArgumentRecipe?)? {
  guard args.length() == 1 || args.length() == 2 else { return None }
  let initial_argument = match args[0] {
    @ast.NumberLit(value, lex_form, _) if value ==
      NUMERIC_RECURSION_INITIAL_ARGUMENT &&
      lex_form == @token.LexForm::LexNormal => value
    _ => return None
  }
  let retained_argument : NumericRecursionClosedArgumentRecipe? = if args.length() ==
    2 {
    match classify_numeric_recursion_closed_argument(args[1]) {
      Some(recipe) => Some(recipe)
      None => return None
    }
  } else {
    None
  }
  Some((initial_argument, retained_argument))
}

///|
/// Classify the closed retained argument with an explicit AST worklist. The
/// only accepted shapes are one normal-form Number literal or one Grouping
/// around a comma tree of normal-form Number literals.
fn classify_numeric_recursion_closed_argument(
  expr : @ast.Expr,
) -> NumericRecursionClosedArgumentRecipe? {
  let root_loc = match expr {
    @ast.NumberLit(_, _, loc) | @ast.Grouping(_, loc) => loc
    _ => return None
  }
  let root_is_grouping = expr is @ast.Grouping(_, _)
  let work : Array[@ast.Expr] = [expr]
  let mut grouping_count = 0
  let mut comma_count = 0
  let mut observation_count = 0
  let mut final_value = 0.0
  let mut final_loc = root_loc
  while work.pop() is Some(item) {
    observation_count = observation_count + 1
    match item {
      @ast.Grouping(inner, _) => {
        guard grouping_count == 0 && root_is_grouping else { return None }
        grouping_count = grouping_count + 1
        work.push(inner)
      }
      @ast.Comma(left, right, _) => {
        comma_count = comma_count + 1
        work.push(right)
        work.push(left)
      }
      @ast.NumberLit(value, lex_form, loc) => {
        guard lex_form == @token.LexForm::LexNormal else { return None }
        final_value = value
        final_loc = loc
      }
      _ => return None
    }
  }
  guard root_is_grouping || comma_count == 0 else { return None }
  guard !root_is_grouping || grouping_count == 1 else { return None }
  Some(
    NumericRecursionClosedArgumentRecipe(
      value=final_value,
      observation_count~,
      root_loc~,
      final_loc~,
    ),
  )
}

///|
fn classify_numeric_recursion_closed_body(
  body : Array[@ast.Stmt],
  binding : String?,
) -> NumericRecursionClosedBodyRecipe? {
  match body.length() {
    0 => Some(NumericRecursionClosedBodyEmpty)
    1 =>
      match body[0] {
        @ast.ExprStmt(@ast.NumberLit(value, lex_form, _), _) =>
          if lex_form == @token.LexForm::LexNormal {
            Some(NumericRecursionClosedNumberValue(value))
          } else {
            None
          }
        @ast.ThrowStmt(@ast.NumberLit(value, lex_form, _), _) =>
          if lex_form == @token.LexForm::LexNormal {
            Some(NumericRecursionClosedNumberThrow(value))
          } else {
            None
          }
        @ast.ExprStmt(@ast.Ident(name, _), _) =>
          match binding {
            Some(expected) if name == expected =>
              Some(NumericRecursionClosedBindingValue)
            _ => None
          }
        @ast.ThrowStmt(@ast.Ident(name, _), _) =>
          match binding {
            Some(expected) if name == expected =>
              Some(NumericRecursionClosedBindingThrow)
            _ => None
          }
        _ => None
      }
    _ => None
  }
}

///|
fn classify_numeric_recursion_catch(
  parameter : @ast.Pattern?,
  body : Array[@ast.Stmt]?,
) -> NumericRecursionCatchRecipe? {
  match (parameter, body) {
    (None, None) => Some(NumericRecursionNoCatch)
    (None, Some(stmts)) =>
      match classify_numeric_recursion_closed_body(stmts, None) {
        Some(
          NumericRecursionClosedBodyEmpty
          | NumericRecursionClosedNumberValue(_)
          | NumericRecursionClosedNumberThrow(_) as recipe
        ) => Some(NumericRecursionCatch(None, recipe))
        _ => None
      }
    (Some(@ast.IdentPat(name)), Some(stmts)) => {
      guard numeric_recursion_identifier_is_safe(name) else { return None }
      match classify_numeric_recursion_closed_body(stmts, Some(name)) {
        Some(
          NumericRecursionClosedBodyEmpty
          | NumericRecursionClosedBindingValue
          | NumericRecursionClosedBindingThrow as recipe
        ) => Some(NumericRecursionCatch(Some(name), recipe))
        _ => None
      }
    }
    _ => None
  }
}

///|
fn classify_numeric_recursion_finalizer(
  body : Array[@ast.Stmt]?,
) -> NumericRecursionFinalizerRecipe? {
  match body {
    None => Some(NumericRecursionNoFinalizer)
    Some(stmts) =>
      match classify_numeric_recursion_closed_body(stmts, None) {
        Some(
          NumericRecursionClosedBodyEmpty
          | NumericRecursionClosedNumberValue(_)
          | NumericRecursionClosedNumberThrow(_) as recipe
        ) => Some(NumericRecursionFinalizer(recipe))
        _ => None
      }
  }
}

///|
fn classify_numeric_recursion_protected_root(
  try_body : Array[@ast.Stmt],
  catch_parameter : @ast.Pattern?,
  catch_body : Array[@ast.Stmt]?,
  finalizer_body : Array[@ast.Stmt]?,
) -> (
  String,
  Double,
  NumericRecursionClosedArgumentRecipe?,
  @token.Loc,
  NumericRecursionRootControlRecipe,
)? {
  guard try_body.length() == 1 else { return None }
  let (
    entry_name,
    initial_argument,
    retained_argument,
    root_call_loc,
    try_completion,
  ) = match try_body[0] {
    @ast.ExprStmt(expr, _) =>
      match classify_numeric_recursion_root_call(expr) {
        Some((name, argument, retained, loc)) =>
          (name, argument, retained, loc, NumericRecursionRootCallValue)
        None => return None
      }
    @ast.ThrowStmt(expr, _) =>
      match classify_numeric_recursion_root_call(expr) {
        Some((name, argument, retained, loc)) =>
          (name, argument, retained, loc, NumericRecursionRootCallThrow)
        None => return None
      }
    _ => return None
  }
  guard classify_numeric_recursion_catch(catch_parameter, catch_body)
    is Some(catch_recipe) else {
    return None
  }
  guard classify_numeric_recursion_finalizer(finalizer_body)
    is Some(finalizer_recipe) else {
    return None
  }
  let has_catch = match catch_recipe {
    NumericRecursionNoCatch => false
    NumericRecursionCatch(_, _) => true
  }
  let has_finalizer = match finalizer_recipe {
    NumericRecursionNoFinalizer => false
    NumericRecursionFinalizer(_) => true
  }
  guard has_catch || has_finalizer else { return None }
  Some(
    (
      entry_name,
      initial_argument,
      retained_argument,
      root_call_loc,
      NumericRecursionProtectedRoot(
        NumericRecursionProtectedRootRecipe(
          try_completion~,
          catch_recipe~,
          finalizer_recipe~,
        ),
      ),
    ),
  )
}

///|
fn classify_self_numeric_recursion_program(
  stmts : Array[@ast.Stmt],
) -> NumericRecursionPlan? {
  guard classify_numeric_recursion_function(stmts[0], 0) is Some(entry) else {
    return None
  }
  guard classify_numeric_recursion_root(stmts[1])
    is Some(
      (
        entry_name,
        initial_argument,
        retained_argument,
        root_call_loc,
        root_control,
      )
    ) else {
    return None
  }
  let retained_shape_is_allowed = match
    (
      entry.syntax.retained_parameter,
      retained_argument,
      entry.syntax.return_recipe,
      root_control,
    ) {
    (None, None, _, _) => true
    (Some(_), Some(_), NumericRecursionDirectReturn, NumericRecursionDirectRoot) =>
      true
    _ => false
  }
  guard entry.syntax.recursive_callee == entry.syntax.name &&
    entry_name == entry.syntax.name &&
    retained_shape_is_allowed else {
    return None
  }
  Some(
    NumericRecursionPlan(
      entry~,
      peer=None,
      initial_argument~,
      retained_argument~,
      root_call_loc~,
      root_control~,
    ),
  )
}

///|
fn classify_mutual_numeric_recursion_program(
  stmts : Array[@ast.Stmt],
) -> NumericRecursionPlan? {
  guard classify_numeric_recursion_function(stmts[0], 0) is Some(entry) else {
    return None
  }
  guard classify_numeric_recursion_function(stmts[1], 1) is Some(peer) else {
    return None
  }
  guard classify_numeric_recursion_root(stmts[2])
    is Some(
      (
        entry_name,
        initial_argument,
        retained_argument,
        root_call_loc,
        root_control,
      )
    ) else {
    return None
  }
  guard entry.syntax.name != peer.syntax.name &&
    entry.syntax.parameter != peer.syntax.name &&
    peer.syntax.parameter != entry.syntax.name &&
    entry.syntax.recursive_callee == peer.syntax.name &&
    peer.syntax.recursive_callee == entry.syntax.name &&
    entry_name == entry.syntax.name &&
    entry.syntax.retained_parameter is None &&
    peer.syntax.retained_parameter is None &&
    retained_argument is None else {
    return None
  }
  Some(
    NumericRecursionPlan(
      entry~,
      peer=Some(peer),
      initial_argument~,
      retained_argument~,
      root_call_loc~,
      root_control~,
    ),
  )
}

///|
#warnings("-unused_value")
fn classify_numeric_recursion_program(
  stmts : Array[@ast.Stmt],
) -> NumericRecursionPlan? {
  match stmts.length() {
    2 => classify_self_numeric_recursion_program(stmts)
    3 => classify_mutual_numeric_recursion_program(stmts)
    _ => None
  }
}

///|
#warnings("-unused_value")
fn numeric_recursion_plan_is_dispatchable(plan : NumericRecursionPlan) -> Bool {
  match plan.root_control {
    NumericRecursionDirectRoot | NumericRecursionProtectedRoot(_) => true
  }
}