///|
/// Concatenate two VCD values without losing vector width.
pub fn value_concat(left : Value, right : Value) -> Value {
  let bits = left.bits() + right.bits()
  if bits.length() == 1 {
    Scalar(bits[0].to_char())
  } else {
    Vector(bits_to_string(bits))
  }
}

///|
/// Reduce a vector with four-state AND semantics.
pub fn value_reduce_and(value : Value) -> Value {
  reduce_value(value, "and")
}

///|
/// Reduce a vector with four-state OR semantics.
pub fn value_reduce_or(value : Value) -> Value {
  reduce_value(value, "or")
}

///|
/// Reduce a vector with four-state XOR semantics.
pub fn value_reduce_xor(value : Value) -> Value {
  reduce_value(value, "xor")
}

///|
/// Add two binary values, returning unknown for any unknown input bit.
pub fn value_add(left : Value, right : Value) -> Value {
  let left_bits = left.bits()
  let right_bits = right.bits()
  let width = Int::max(left_bits.length(), right_bits.length())
  let result : Array[LogicBit] = []
  let mut carry = Zero
  let mut i = width - 1
  while i >= 0 {
    let (sum, next_carry) = logic_add_three(
      aligned_bit(left_bits, width, i),
      aligned_bit(right_bits, width, i),
      carry,
    )
    result.insert(0, sum)
    carry = next_carry
    i -= 1
  }
  if carry is One {
    result.insert(0, One)
  }
  bits_value(result)
}

///|
/// Subtract two binary values with a fixed-width result.
pub fn value_sub(left : Value, right : Value) -> Value {
  let width = Int::max(left.width(), right.width())
  let a = value_zero_extend(left, width)
  let b = value_zero_extend(right, width)
  if a.is_unknown() || b.is_unknown() {
    return Vector("x".repeat(width))
  }
  match (a.to_int(), b.to_int()) {
    (Some(left_number), Some(right_number)) => {
      let modulus = pow2(width)
      let raw = if left_number >= right_number {
        left_number - right_number
      } else {
        modulus - (right_number - left_number)
      }
      value_from_int(raw, width)
    }
    _ => Vector("x".repeat(width))
  }
}

///|
/// Compare two known binary values as unsigned integers.
pub fn value_compare_unsigned(left : Value, right : Value) -> Int? {
  if left.is_unknown() || right.is_unknown() {
    return None
  }
  let a = left.bits()
  let b = right.bits()
  let width = Int::max(a.length(), b.length())
  for i in 0.. Some(-1)
        (One, Zero) => Some(1)
        _ => None
      }
    }
  }
  Some(0)
}

///|
/// Shift a value left and fill vacated bits with zero.
pub fn value_shift_left(value : Value, amount : Int) -> Value {
  let bits = value.bits()
  let width = bits.length()
  if width == 0 {
    return value
  }
  let shift = Int::max(0, amount)
  let result : Array[LogicBit] = []
  for i in 0..= width {
      result.push(Zero)
    } else {
      result.push(bits[source])
    }
  }
  bits_value(result)
}

///|
/// Shift a value right and fill vacated bits with zero.
pub fn value_shift_right(value : Value, amount : Int) -> Value {
  let bits = value.bits()
  let width = bits.length()
  if width == 0 {
    return value
  }
  let shift = Int::max(0, amount)
  let result : Array[LogicBit] = []
  for i in 0.. Value {
  let bits = value.bits()
  let width = bits.length()
  if width == 0 {
    return value
  }
  let shift = (amount % width + width) % width
  let result : Array[LogicBit] = []
  for i in 0.. Value {
  let bits = value.bits()
  let width = bits.length()
  if width == 0 {
    return value
  }
  let shift = (amount % width + width) % width
  let result : Array[LogicBit] = []
  for i in 0.. Int {
  value.bits().count(One)
}

///|
/// Count unknown and high-impedance bits in a value.
pub fn value_count_unknown(value : Value) -> Int {
  value
  .bits()
  .count_if(fn(bit) {
    match bit {
      Unknown | HighImpedance => true
      _ => false
    }
  })
}

///|
/// Return whether every bit is logically zero.
pub fn value_is_zero(value : Value) -> Bool {
  let bits = value.bits()
  bits.length() > 0 && bits.all(fn(bit) { bit is Zero })
}

///|
/// Return whether a vector contains at least one known one bit.
pub fn value_has_one(value : Value) -> Bool {
  value.bits().any(fn(bit) { bit is One })
}

///|
/// Construct a value from a non-negative integer and a fixed width.
pub fn value_from_int(number : Int64, width : Int) -> Value {
  let safe_width = Int::max(1, width)
  let result : Array[LogicBit] = []
  let mut current = Int64::max(0L, number)
  for _ in 0.. Value {
  let bits = value.bits()
  if bits.length() == 0 {
    return Scalar('x')
  }
  let mut result = bits[0]
  for i in 1.. Value {
  if bits.length() == 1 {
    Scalar(bits[0].to_char())
  } else {
    Vector(bits_to_string(bits))
  }
}

///|
fn logic_add_three(
  left : LogicBit,
  right : LogicBit,
  carry : LogicBit,
) -> (LogicBit, LogicBit) {
  match (left, right, carry) {
    (Zero, Zero, Zero) => (Zero, Zero)
    (Zero, Zero, One) => (One, Zero)
    (Zero, One, Zero) | (One, Zero, Zero) => (One, Zero)
    (Zero, One, One) | (One, Zero, One) => (Zero, One)
    (One, One, Zero) => (Zero, One)
    (One, One, One) => (One, One)
    _ => (Unknown, Unknown)
  }
}

///|
fn pow2(width : Int) -> Int64 {
  let mut result : Int64 = 1L
  for _ in 0.. Value {
  let bits = value.bits()
  let target = Int::max(width, bits.length())
  let sign = match bits.get(0) {
    Some(One) => One
    _ => Zero
  }
  let result : Array[LogicBit] = []
  for _ in 0..<(target - bits.length()) {
    result.push(sign)
  }
  result.append(bits)
  bits_value(result)
}