// E-node vocabulary: the terms an e-graph is built out of.
//
// `EOpcode` is the operation alphabet, `ENode` one applied operation over
// child classes, and `EOpcodeTag` the coarse key the opcode index buckets
// by. Nothing here knows about equivalence -- that starts in
// `union_find.mbt`.

///|
/// E-class identifier (index into the e-graph's class array)
pub(all) struct EClassId(Int) derive(Eq, Hash, Debug, Compare)

///|
/// E-node opcode for e-graph expressions
/// Standard IR opcodes for optimization
pub(all) enum EOpcode {
  // Constants
  Const(Int64) // Integer constant
  // Float constants (bits stored as UInt64, type indicated by context)
  // F32 uses lower 32 bits, F64 uses all 64 bits
  Fconst(UInt64) // Float constant (bit pattern)

  // Integer arithmetic (binary)
  Add
  Sub
  Mul
  Sdiv
  Udiv
  Srem
  Urem

  // Bitwise operations (binary)
  And
  Or
  Xor
  Shl
  Sshr
  Ushr
  Rotl // Rotate left
  Rotr // Rotate right

  // Unary arithmetic
  Neg // Integer negation (-x)
  Bnot // Bitwise NOT (~x)

  // Bit counting/manipulation (unary)
  Clz // Count leading zeros
  Ctz // Count trailing zeros
  Popcnt // Population count (count 1 bits)
  Bswap // Byte swap
  Bitrev // Bit reverse

  // Comparison (binary, returns i1)
  // IntCC: 0=eq, 1=ne, 2=slt, 3=sle, 4=sgt, 5=sge, 6=ult, 7=ule, 8=ugt, 9=uge
  Icmp(Int) // Integer compare with condition code
  Eq // Equal (shorthand for Icmp(0))
  Ne // Not equal (shorthand for Icmp(1))

  // Conditional
  Select // select(cond, a, b) = cond ? a : b
  Bmask // Boolean mask: 0 -> 0, nonzero -> -1

  // Integer min/max (binary)
  Smin // Signed minimum
  Smax // Signed maximum
  Umin // Unsigned minimum
  Umax // Unsigned maximum

  // Integer absolute value (unary)
  Iabs // Integer absolute value

  // Three-way comparison (spaceship operator, returns -1, 0, or 1)
  SpaceshipS // Signed three-way comparison: (x > y) - (x < y)
  SpaceshipU // Unsigned three-way comparison: (x > y) - (x < y)

  // Type conversions (integer) - with bit width info for type-aware optimizations
  // (from_bits, to_bits): e.g., Ireduce(64, 32) means i64 -> i32
  Ireduce(Int, Int) // Integer reduce (truncate to smaller type)
  Uextend(Int, Int) // Unsigned extend to larger type
  Sextend(Int, Int) // Signed extend to larger type

  // Floating point arithmetic (binary)
  Fadd // Float add
  Fsub // Float subtract
  Fmul // Float multiply
  Fdiv // Float divide
  Fmin // Float minimum
  Fmax // Float maximum
  Fcopysign // Copy sign from second operand to first

  // Floating point unary
  Fneg // Float negate
  Fabs // Float absolute value
  Fsqrt // Float square root
  Fceil // Float ceiling
  Ffloor // Float floor
  Ftrunc // Float truncate toward zero
  Fnearest // Float round to nearest

  // Floating point comparison
  // FloatCC: 0=ord, 1=uno, 2=eq, 3=ne, 4=lt, 5=le, 6=gt, 7=ge
  Fcmp(Int) // Float compare with condition code

  // Float-integer conversions
  Fpromote // Promote f32 to f64
  Fdemote // Demote f64 to f32
  FcvtToSint // Float to signed int
  FcvtToUint // Float to unsigned int
  SintToFcvt // Signed int to float
  UintToFcvt // Unsigned int to float

  // Vector operations
  Splat // Broadcast scalar to vector
  Vconst(Bytes) // Vector constant (128-bit)

  // Variable reference (leaf node representing an IR value)
  Var(Int) // IR Value id
} derive(Eq, Hash, Debug, Compare)

///|
/// Keep `Show` behavior while migrating from deprecated `derive(Show)` to
/// `derive(Debug)`.
pub impl Show for EClassId with fn output(self, logger) {
  logger.write_string(Repr(self).to_string())
}

///|
pub impl Show for EOpcode with fn output(self, logger) {
  logger.write_string(Repr(self).to_string())
}

///|
/// E-opcode tag for indexing (ignores parameters like Const value)
enum EOpcodeTag {
  TConst
  TFconst
  // Arithmetic
  TAdd
  TSub
  TMul
  TSdiv
  TUdiv
  TSrem
  TUrem
  // Bitwise
  TAnd
  TOr
  TXor
  TShl
  TSshr
  TUshr
  TRotl
  TRotr
  // Unary
  TNeg
  TBnot
  // Bit manipulation
  TClz
  TCtz
  TPopcnt
  TBswap
  TBitrev
  // Comparison
  TIcmp
  TEq
  TNe
  // Conditional
  TSelect
  TBmask
  // Integer min/max
  TSmin
  TSmax
  TUmin
  TUmax
  // Integer absolute value
  TIabs
  // Three-way comparison
  TSpaceshipS
  TSpaceshipU
  // Type conversion (integer)
  TIreduce
  TUextend
  TSextend
  // Float arithmetic
  TFadd
  TFsub
  TFmul
  TFdiv
  TFmin
  TFmax
  TFcopysign
  // Float unary
  TFneg
  TFabs
  TFsqrt
  TFceil
  TFfloor
  TFtrunc
  TFnearest
  // Float comparison
  TFcmp
  // Float-integer conversion
  TFpromote
  TFdemote
  TFcvtToSint
  TFcvtToUint
  TSintToFcvt
  TUintToFcvt
  // Vector
  TSplat
  TVconst
  // Variable
  TVar
} derive(Eq, Hash, Debug)

///|
/// Get the tag of an opcode (for indexing)
pub fn EOpcode::tag(self : EOpcode) -> EOpcodeTag {
  match self {
    Const(_) => TConst
    Fconst(_) => TFconst
    // Arithmetic
    Add => TAdd
    Sub => TSub
    Mul => TMul
    Sdiv => TSdiv
    Udiv => TUdiv
    Srem => TSrem
    Urem => TUrem
    // Bitwise
    And => TAnd
    Or => TOr
    Xor => TXor
    Shl => TShl
    Sshr => TSshr
    Ushr => TUshr
    Rotl => TRotl
    Rotr => TRotr
    // Unary
    Neg => TNeg
    Bnot => TBnot
    // Bit manipulation
    Clz => TClz
    Ctz => TCtz
    Popcnt => TPopcnt
    Bswap => TBswap
    Bitrev => TBitrev
    // Comparison
    Icmp(_) => TIcmp
    Eq => TEq
    Ne => TNe
    // Conditional
    Select => TSelect
    Bmask => TBmask
    // Integer min/max
    Smin => TSmin
    Smax => TSmax
    Umin => TUmin
    Umax => TUmax
    // Integer absolute value
    Iabs => TIabs
    // Three-way comparison
    SpaceshipS => TSpaceshipS
    SpaceshipU => TSpaceshipU
    // Type conversion (integer) - ignore bit width parameters for tagging
    Ireduce(_, _) => TIreduce
    Uextend(_, _) => TUextend
    Sextend(_, _) => TSextend
    // Float arithmetic
    Fadd => TFadd
    Fsub => TFsub
    Fmul => TFmul
    Fdiv => TFdiv
    Fmin => TFmin
    Fmax => TFmax
    Fcopysign => TFcopysign
    // Float unary
    Fneg => TFneg
    Fabs => TFabs
    Fsqrt => TFsqrt
    Fceil => TFceil
    Ffloor => TFfloor
    Ftrunc => TFtrunc
    Fnearest => TFnearest
    // Float comparison
    Fcmp(_) => TFcmp
    // Float-integer conversion
    Fpromote => TFpromote
    Fdemote => TFdemote
    FcvtToSint => TFcvtToSint
    FcvtToUint => TFcvtToUint
    SintToFcvt => TSintToFcvt
    UintToFcvt => TUintToFcvt
    // Vector
    Splat => TSplat
    Vconst(_) => TVconst
    // Variable
    Var(_) => TVar
  }
}

///|
/// E-node: an expression node in the e-graph
/// Children are EClassIds, not direct node references
pub(all) struct ENode {
  op : EOpcode
  children : Array[EClassId]
} derive(Eq, Hash, Debug, Compare)