///|
fn LibraryLowerer::int32_invoke(
  self : LibraryLowerer,
  name : String,
  arguments : Array[(@ir.Value, @ir.Type)],
  result : @ir.Type,
  source : @ir.SourceLocation,
  role : String,
) -> @ir.Value raise {
  self.invoke(
    name,
    arguments,
    result,
    source,
    role,
    metadata_key="moonbit-source-number-v1",
    runtime_extension=if name.has_prefix("morphir/moonbit-source:") {
      "morphir-moonbit-source-int32-v1"
    } else {
      ""
    },
  )
}

///|
/// Bind source operands once, widen to exact Int, then normalize only arithmetic
/// results. A comparison observes the exact values of the bounded operands.
fn LibraryLowerer::int32_binary(
  self : LibraryLowerer,
  op : String,
  left : @ir.Value,
  right : @ir.Value,
  source : @ir.SourceLocation,
) -> @ir.Value raise {
  let int32 = LibraryTypeUse::{
    scalar: SignedInt32,
    spelling: "Int32",
    source,
    profile: self.profile,
  }.to_ir(self.metadata)
  let exact = library_integer_type()
  let left_name = self.fresh(left, "int32-left", source)
  let right_name = self.fresh(right, "int32-right", source)
  let left_value = @ir.Value::Variable(
    library_attributes(source, int32),
    left_name,
  )
  let right_value = @ir.Value::Variable(
    library_attributes(source, int32),
    right_name,
  )
  let left_exact = self.int32_invoke(
    "morphir/SDK:int#from-int-32",
    [(left_value, int32)],
    exact,
    source,
    "widen-left",
  )
  let right_exact = self.int32_invoke(
    "morphir/SDK:int#from-int-32",
    [(right_value, int32)],
    exact,
    source,
    "widen-right",
  )
  let operation = match op {
    "+" => "add"
    "-" => "subtract"
    "*" => "multiply"
    "==" => "equal"
    "!=" => "not-equal"
    "<" => "less-than"
    "<=" => "less-than-or-equal"
    ">" => "greater-than"
    ">=" => "greater-than-or-equal"
    _ => abort("checked Int32 operator")
  }
  let arithmetic = ["+", "-", "*"].contains(op)
  let operation_type = if arithmetic { exact } else { self.boolean }
  let calculation = self.int32_invoke(
    "morphir/SDK:basics#" + operation,
    [(left_exact, exact), (right_exact, exact)],
    operation_type,
    source,
    "int32-" + operation,
  )
  let result = if arithmetic {
    self.int32_invoke(
      "morphir/moonbit-source:int32#normalize",
      [(calculation, exact)],
      int32,
      source,
      "signed-width-normalization",
    )
  } else {
    calculation
  }
  let output = if arithmetic { int32 } else { self.boolean }
  @ir.Value::LetDefinition(
    library_attributes(source, output),
    left_name,
    @ir.ValueDefinition::ExpressionBody([], int32, left),
    @ir.Value::LetDefinition(
      library_attributes(source, output),
      right_name,
      @ir.ValueDefinition::ExpressionBody([], int32, right),
      result,
    ),
  )
}

///|
fn LibraryLowerer::int32_negate(
  self : LibraryLowerer,
  value : @ir.Value,
  source : @ir.SourceLocation,
) -> @ir.Value raise {
  let int32 = LibraryTypeUse::{
    scalar: SignedInt32,
    spelling: "Int32",
    source,
    profile: self.profile,
  }.to_ir(self.metadata)
  let exact = library_integer_type()
  let widened = self.int32_invoke(
    "morphir/SDK:int#from-int-32",
    [(value, int32)],
    exact,
    source,
    "widen-negated",
  )
  let negated = self.int32_invoke(
    "morphir/SDK:basics#negate",
    [(widened, exact)],
    exact,
    source,
    "negate-exact",
  )
  self.int32_invoke(
    "morphir/moonbit-source:int32#normalize",
    [(negated, exact)],
    int32,
    source,
    "signed-width-normalization",
  )
}