From b754c03a5f507e78ec396d3a22e628f8b459bdab Mon Sep 17 00:00:00 2001
From: Etienne Kneuss <colder@php.net>
Date: Thu, 5 Jun 2014 19:05:08 +0200
Subject: [PATCH] Proper handling of ASCII tables

---
 src/main/scala/leon/utils/ASCIITable.scala    | 122 ++++++++++++++++++
 .../verification/VerificationReport.scala     |  59 ++++-----
 2 files changed, 145 insertions(+), 36 deletions(-)
 create mode 100644 src/main/scala/leon/utils/ASCIITable.scala

diff --git a/src/main/scala/leon/utils/ASCIITable.scala b/src/main/scala/leon/utils/ASCIITable.scala
new file mode 100644
index 000000000..0daba6f96
--- /dev/null
+++ b/src/main/scala/leon/utils/ASCIITable.scala
@@ -0,0 +1,122 @@
+package leon.utils
+
+object ASCIITables {
+  case class Table(title: String, rows: Seq[TableRow] = Nil) {
+    def +(r: TableRow): Table = this ++ Seq(r)
+    def ++(rs: Iterable[TableRow]): Table = copy(rows = rows ++ rs)
+
+
+    def computeColumnSizes = {
+      // First check constraints
+      var constraints = Map[(Int, Int), Int]()
+
+      var cellsPerRow = 0
+      for((r, i) <- rows.zipWithIndex) r match {
+        case r @ Row(cells) =>
+          if (cellsPerRow > 0) {
+            assert(r.cellsSize == cellsPerRow, "Row $i has incompatible number of virtual cells (${r.cellsSize} v.s. $cellsPerRow")
+          } else {
+            cellsPerRow = r.cellsSize
+            constraints += (0, cellsPerRow-1) -> 80
+          }
+
+          var i = 0
+          for (c <- cells) {
+            val k = i -> (i+c.spanning-1)
+
+            val cols = constraints.getOrElse(k, 1)
+
+            val size = c.vString.size
+
+            constraints += k -> (cols max size)
+
+            i += c.spanning
+          }
+        case _ =>
+      }
+
+      // discharge constraints that are spanning
+      val toRemove = constraints.keySet.filter { case (from, to) => from != to }.toSeq.sortBy{ s => s._2 - s._1 }
+
+      for (k @ (from, to) <- toRemove) {
+        val min = constraints(k)
+
+        val parts = (from to to).map(i => constraints((i, i)))
+
+        var sum = parts.sum
+
+        if (sum < min) {
+          var remaining = min - sum
+
+          for ((s, i) <- parts.zipWithIndex.sortBy(- _._1)) {
+            val inc = Math.round(s*remaining*1d/sum).toInt
+            constraints += (from+i, from+i) -> (s + inc)
+            remaining -= inc
+            sum -= s
+          }
+        }
+
+        constraints -= k
+      }
+
+      (0 until cellsPerRow).map(i => constraints((i, i)))
+    }
+
+    def render: String = {
+      val colSizes = computeColumnSizes
+
+      val fullWidth = colSizes.sum + colSizes.size*2
+
+      val sb = new StringBuffer
+
+      sb append "  ┌─"+("─"*title.size)+"─┐\n"
+      sb append "╔═╡ "+      title     +" ╞" + ("═" * (fullWidth-title.size-5)) + "╗\n"
+      sb append "║ └─"+("─"*title.size)+"─┘" + (" " * (fullWidth-title.size-5)) + "║\n"
+
+      for (r <- rows) r match {
+        case Separator =>
+          sb append "╟" + ("┄" * fullWidth) + "╢\n"
+
+        case Row(cells) =>
+          sb append "║ "
+          var i = 0
+          for (c <- cells) {
+            if (i > 0) {
+              sb append "  "
+            }
+            val size = (i to i+c.spanning-1).map(colSizes).sum + (c.spanning-1) * 2
+
+            if (c.align == Left) {
+              sb append ("%-"+size+"s").format(c.vString)
+            } else {
+              sb append ("%"+size+"s").format(c.vString)
+            }
+
+            i += c.spanning
+          }
+          sb append " ║\n"
+      }
+
+      sb append "╚" + ("═" * fullWidth) + "╝"
+
+      sb.toString
+    }
+  }
+
+  abstract class TableRow
+  case object Separator extends TableRow
+  case class Row(cells: Seq[Cell]) extends TableRow {
+    def cellsSize = {
+      cells.map(_.spanning).sum
+    }
+  }
+  sealed abstract class Alignment
+  case object Left extends Alignment
+  case object Right extends Alignment
+
+
+  case class Cell(v: Any, spanning: Int = 1, align: Alignment = Left) {
+    lazy val vString = v.toString
+  }
+
+}
diff --git a/src/main/scala/leon/verification/VerificationReport.scala b/src/main/scala/leon/verification/VerificationReport.scala
index 01b129d77..b884d229e 100644
--- a/src/main/scala/leon/verification/VerificationReport.scala
+++ b/src/main/scala/leon/verification/VerificationReport.scala
@@ -20,45 +20,32 @@ class VerificationReport(val fvcs: Map[FunDef, List[VerificationCondition]]) {
   lazy val totalUnknown : Int = conditions.count(_.value == None)
 
   def summaryString : String = if(totalConditions >= 0) {
-    VerificationReport.infoHeader +
-    conditions.map(VerificationReport.infoLine).mkString("\n", "\n", "\n") +
-    VerificationReport.infoSep +
-    ("║ total: %-4d   valid: %-4d   invalid: %-4d   unknown %-4d " +
-      (" " * 16) +
-      " %7.3f ║\n").format(totalConditions, totalValid, totalInvalid, totalUnknown, totalTime) +
-    VerificationReport.infoFooter
-  } else {
-    "No verification conditions were analyzed."
-  }
-}
-
-object VerificationReport {
-  def emptyReport : VerificationReport = new VerificationReport(Map())
-
-  private def fit(str : String, maxLength : Int) : String = {
-    if(str.length <= maxLength) {
-      str
-    } else {
-      str.substring(0, maxLength - 1) + "…"
+    import utils.ASCIITables._
+
+    var t = Table("Verification Summary")
+
+    t ++= conditions.map { vc =>
+      val timeStr = vc.time.map(t => "%-3.3f".format(t)).getOrElse("")
+      Row(Seq(
+        Cell(vc.funDef.id.toString),
+        Cell(vc.kind),
+        Cell(vc.status),
+        Cell(vc.tacticStr),
+        Cell(vc.solverStr),
+        Cell(timeStr, align = Right)
+      ))
     }
-  }
 
-  private val infoSep    : String = "╟" + ("┄" * 83) + "╢\n"
-  private val infoFooter : String = "╚" + ("═" * 83) + "╝"
-  private val infoHeader : String = ". ┌─────────┐\n" +
-                                    "╔═╡ Summary ╞" + ("═" * 71) + "╗\n" +
-                                    "║ └─────────┘" + (" " * 71) + "║"
+    t += Separator
+
+    t += Row(Seq(
+      Cell(f"total: $totalConditions%-4d   valid: $totalValid%-4d   invalid: $totalInvalid%-4d   unknown $totalUnknown%-4d", 5),
+      Cell(f"$totalTime%7.3f", align = Right)
+    ))
 
-  private def infoLine(vc : VerificationCondition) : String = {
-    val timeStr = vc.time.map(t => "%-3.3f".format(t)).getOrElse("")
+    t.render
 
-    "║ %-25s %-9s %9s %-8s %-10s %-7s %7s ║".format(
-      fit(vc.funDef.id.toString, 25),
-      vc.kind,
-      vc.getPos,
-      vc.status,
-      vc.tacticStr,
-      vc.solverStr,
-      timeStr)
+  } else {
+    "No verification conditions were analyzed."
   }
 }
-- 
GitLab