// SHA-256 (FIPS 180-4) self-implementation for the RDFC-1.0 battle
// (spec.md section 8.132 route A; user ruling 2026-10-03: option b,
// self-implementation self-certified against RFC 6234 official vectors).
// SHA-256 is a core dependency of the six-step algorithm, not a tail
// piece: the hex string of first-degree / n-degree hashes IS the bnode
// ordering key. Whole-buffer padding shape, 512-bit blocks, 64 rounds.

///|
/// 64 round constants: first 32 bits of the fractional parts of the
/// cube roots of the first 64 primes (FIPS 180-4 section 4.2.3).
let sha256_round_constants : Array[UInt] = [
  0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4,
  0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe,
  0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f,
  0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
  0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc,
  0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b,
  0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116,
  0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
  0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7,
  0xc67178f2,
]

///|
/// Initial state: first 32 bits of the fractional parts of the square
/// roots of the first 8 primes (FIPS 180-4 section 4.2.2).
let sha256_initial_state : Array[UInt] = [
  0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab,
  0x5be0cd19,
]

///|
/// Rotate-right (FIPS 180-4 section 4.1.2 rotr); UInt logical shift
/// keeps the high bits zero-filled.
fn sha256_rotr(word : UInt, count : Int) -> UInt {
  (word >> count) | (word << (32 - count))
}

///|
/// Small sigma zero (FIPS 180-4 section 4.1.2): ROTR7 ^ ROTR18 ^ SHR3.
fn sha256_sigma0(word : UInt) -> UInt {
  sha256_rotr(word, 7) ^ sha256_rotr(word, 18) ^ (word >> 3)
}

///|
/// Small sigma one (FIPS 180-4 section 4.1.2): ROTR17 ^ ROTR19 ^ SHR10.
fn sha256_sigma1(word : UInt) -> UInt {
  sha256_rotr(word, 17) ^ sha256_rotr(word, 19) ^ (word >> 10)
}

///|
/// Big sigma zero (FIPS 180-4 section 4.1.2): ROTR2 ^ ROTR13 ^ ROTR22.
fn sha256_big_sigma0(word : UInt) -> UInt {
  sha256_rotr(word, 2) ^ sha256_rotr(word, 13) ^ sha256_rotr(word, 22)
}

///|
/// Big sigma one (FIPS 180-4 section 4.1.2): ROTR6 ^ ROTR11 ^ ROTR25.
fn sha256_big_sigma1(word : UInt) -> UInt {
  sha256_rotr(word, 6) ^ sha256_rotr(word, 11) ^ sha256_rotr(word, 25)
}

///|
/// Choose (FIPS 180-4 section 4.1.2 Ch): (e AND f) XOR (NOT e AND g);
/// NOT shaped as XOR with all-ones to keep the expression flat.
const SHA256_ALL_ONES : UInt = 0xFFFFFFFF

///|
fn sha256_choose(word : UInt, then_word : UInt, else_word : UInt) -> UInt {
  (word & then_word) ^ (else_word & (word ^ SHA256_ALL_ONES))
}

///|
/// Majority (FIPS 180-4 section 4.1.2 Maj): (a AND b) XOR (a AND c) XOR
/// (b AND c).
fn sha256_majority(first : UInt, second : UInt, third : UInt) -> UInt {
  (first & second) ^ (first & third) ^ (second & third)
}

///|
/// Digest computation: whole-buffer padding (FIPS 180-4 section 5.1.1:
/// 0x80, zeros, 64-bit big-endian bit length) then 512-bit block
/// compression per section 6.2. Returns the 8 final state words.
fn sha256_words(data : Bytes) -> Array[UInt] {
  let message_length = data.length()
  let bit_length = message_length.to_uint64() * 8UL
  let padded : Array[Byte] = []
  for index in 0..> shift).to_uint().reinterpret_as_int().to_byte())
  }
  let state : Array[UInt] = Array::make(8, 0)
  for index in 0..<8 {
    state[index] = sha256_initial_state[index]
  }
  let mut block_base = 0
  while block_base < padded.length() {
    let schedule : Array[UInt] = Array::make(64, 0)
    for t in 0..<16 {
      let base = block_base + t * 4
      schedule[t] = padded[base].to_uint() << 24
      schedule[t] = schedule[t] | (padded[base + 1].to_uint() << 16)
      schedule[t] = schedule[t] | (padded[base + 2].to_uint() << 8)
      schedule[t] = schedule[t] | padded[base + 3].to_uint()
    }
    for t in 16..<64 {
      schedule[t] = sha256_sigma1(schedule[t - 2]) +
        schedule[t - 7] +
        sha256_sigma0(schedule[t - 15]) +
        schedule[t - 16]
    }
    let mut working_a = state[0]
    let mut working_b = state[1]
    let mut working_c = state[2]
    let mut working_d = state[3]
    let mut working_e = state[4]
    let mut working_f = state[5]
    let mut working_g = state[6]
    let mut working_h = state[7]
    for round_index in 0..<64 {
      let sum_one = working_h +
        sha256_big_sigma1(working_e) +
        sha256_choose(working_e, working_f, working_g) +
        sha256_round_constants[round_index] +
        schedule[round_index]
      let sum_two = sha256_big_sigma0(working_a) +
        sha256_majority(working_a, working_b, working_c)
      working_h = working_g
      working_g = working_f
      working_f = working_e
      working_e = working_d + sum_one
      working_d = working_c
      working_c = working_b
      working_b = working_a
      working_a = sum_one + sum_two
    }
    state[0] = state[0] + working_a
    state[1] = state[1] + working_b
    state[2] = state[2] + working_c
    state[3] = state[3] + working_d
    state[4] = state[4] + working_e
    state[5] = state[5] + working_f
    state[6] = state[6] + working_g
    state[7] = state[7] + working_h
    block_base = block_base + 64
  }
  state
}

///|
/// Digest as 64-char lowercase hex (the RDFC-1.0 ordering-key shape,
/// rdfc-1.0 spec section 4.6.2: hash expressed as lowercase hex string).
fn sha256_hex(data : Bytes) -> String {
  let builder = StringBuilder()
  for word in sha256_words(data) {
    for shift in [28, 24, 20, 16, 12, 8, 4, 0] {
      let nibble = ((word >> shift) & 15).reinterpret_as_int()
      let digit_char = if nibble < 10 {
        Int::unsafe_to_char(48 + nibble)
      } else {
        Int::unsafe_to_char(87 + nibble)
      }
      builder.write_char(digit_char)
    }
  }
  builder.to_string()
}

///|
/// SHA-256 of a string via UTF-8 bytes (the RDFC-1.0 entry shape: the
/// algorithm hashes the canonical serialization strings, section 4.6.2).
fn sha256_utf8_hex(input : String) -> String {
  sha256_hex(@utf8.encode(input))
}