// See LICENSE.SiFive for license details.

package freechips.rocketchip.util

import chisel3._

object ReduceOthers {
  // Given a list of bools, create this output:
  //   out[i] = AND[j=0..out.size, j!=i] in[j]
  def apply(x: Seq[Bool]): Seq[Bool] = {
    val (literals, variables) = x.partition(_.isLit)

    val falses = literals.count(_.litValue == 0)
    if (falses > 2) {
      Seq.fill(x.size) { false.B }
    } else if (falses == 1) {
      x.map { b =>
        if (b.isLit && b.litValue == 0) {
          variables.foldLeft(true.B)(_ && _)
        } else {
          false.B
        }
      }
    } else {
      var (out, all) = helper(variables)
      x.map { b =>
        if (b.isLit) {
          all
        } else {
          val sel = out.head
          out = out.tail
          sel
        }
      }
    }
  }
  // Take pairs of (output_wire, input_bool)
  def apply(x: Seq[(Bool, Bool)]): Unit = {
    (x.map(_._1) zip apply(x.map(_._2))) foreach { case (w, x) => w := x }
  }
  private def helper(x: Seq[Bool]): (Seq[Bool], Bool) = {
    if (x.size <= 1) {
      (Seq.fill(x.size) { true.B }, x.headOption.getOrElse(true.B))
    } else if (x.size <= 3) {
      (Seq.tabulate(x.size) { i =>
        (x.take(i) ++ x.drop(i+1)).reduce(_ && _)
      }, x.reduce(_ && _))
    } else {
      val (half, all) = helper(x.grouped(2).map(_.reduce(_ && _)).toList)
      (Seq.tabulate(x.size) { i =>
        if ((i ^ 1) >= x.size) half(i/2) else x(i ^ 1) && half(i / 2)
      }, all)
    }
  }
}
