package xiangshan.backend.fu.fpu.fma

import chisel3._
import chisel3.util._

class LzaIO(len: Int) extends Bundle {
  val a, b = Input(UInt(len.W))
  val out = Output(UInt(log2Up(len+1).W))
  override def cloneType: LzaIO.this.type = new LzaIO(len).asInstanceOf[this.type]
}

// Leading Zero Anticipator
class LZA(len: Int) extends Module {
  val io = IO(new LzaIO(len))
  /**  msb             lsb
    *  0    1    2 ... n-1
    */
  val (a, b) = (io.a.asBools().reverse, io.b.asBools().reverse)
  //
  val g, s, e, f = Wire(Vec(len, Bool()))

  for(i <- 0 until len){
    g(i) := a(i) & !b(i)
    s(i) := !a(i) & b(i)
    e(i) := a(i) === b(i)
  }

  f(0) := (s(0) & !s(1)) | (g(0) & !g(1))
  f(len-1) := false.B

  for(i <- 1 until len-1){
    f(i) :=  (e(i-1) & g(i) & !s(i+1)) |
      (!e(i-1) & s(i) & !s(i+1)) |
      (e(i-1) & s(i) & !g(i+1)) |
      (!e(i-1) & g(i) & !g(i+1))
  }

  val res = PriorityEncoder(f)

  val p, n, z = Wire(Vec(len, Bool()))
  p(0) := g(0)
  n(0) := s(0)
  p(1) := g(1)
  n(1) := s(1)
  for(i <- 2 until len){
    p(i) := (e(i-1) | e(i-2) & g(i-1) | !e(i-2) & s(i-1)) & g(i)
    n(i) := (e(i-1) | e(i-2) & s(i-1) | !e(i-2) & g(i-1)) & s(i)
  }
  for(i <- 0 until len){
    z(i) := !(p(i) | n(i))
  }

  class TreeNode extends Bundle {
    val Z, P, N = Bool()
  }

  def buildOneLevel(nodes: Seq[TreeNode]): Seq[TreeNode] = {
    nodes match {
      case Seq(_) => nodes
      case Seq(_, _) => nodes
      case Seq(left, mid, right) =>
        val next_l, next_r = Wire(new TreeNode)
        next_l.P := left.P | left.Z & mid.P
        next_l.N := left.N | left.Z & mid.N
        next_l.Z := left.Z & mid.Z
        next_r.P := !left.Z & mid.P | right.P & (left.Z | mid.Z)
        next_r.N := !left.Z & mid.N | right.N & (left.Z | mid.Z)
        next_r.Z := right.Z & (left.Z | mid.Z)
        Seq(next_l, next_r)
      case _ =>
        buildOneLevel(nodes.take(3)) ++ buildOneLevel(nodes.drop(3))
    }
  }

  def detectionTree(nodes: Seq[TreeNode]): Bool = {
    assert(nodes.size >= 2)
    nodes match {
      case Seq(left, right) =>
        left.P & right.N | left.N & right.P
      case _ =>
        val nextLevel = buildOneLevel(nodes)
        detectionTree(nextLevel)
    }
  }

  val nodes = (0 until len).map(i => {
    val treeNode = Wire(new TreeNode)
    treeNode.P := p(i)
    treeNode.N := n(i)
    treeNode.Z := z(i)
    treeNode
  })

  val error = detectionTree(nodes)

  io.out := res + error
}
