format.go 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. // Copyright ©2013 The Gonum Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package mat
  5. import (
  6. "fmt"
  7. "strconv"
  8. )
  9. // Formatted returns a fmt.Formatter for the matrix m using the given options.
  10. func Formatted(m Matrix, options ...FormatOption) fmt.Formatter {
  11. f := formatter{
  12. matrix: m,
  13. dot: '.',
  14. }
  15. for _, o := range options {
  16. o(&f)
  17. }
  18. return f
  19. }
  20. type formatter struct {
  21. matrix Matrix
  22. prefix string
  23. margin int
  24. dot byte
  25. squeeze bool
  26. }
  27. // FormatOption is a functional option for matrix formatting.
  28. type FormatOption func(*formatter)
  29. // Prefix sets the formatted prefix to the string p. Prefix is a string that is prepended to
  30. // each line of output.
  31. func Prefix(p string) FormatOption {
  32. return func(f *formatter) { f.prefix = p }
  33. }
  34. // Excerpt sets the maximum number of rows and columns to print at the margins of the matrix
  35. // to m. If m is zero or less all elements are printed.
  36. func Excerpt(m int) FormatOption {
  37. return func(f *formatter) { f.margin = m }
  38. }
  39. // DotByte sets the dot character to b. The dot character is used to replace zero elements
  40. // if the result is printed with the fmt ' ' verb flag. Without a DotByte option, the default
  41. // dot character is '.'.
  42. func DotByte(b byte) FormatOption {
  43. return func(f *formatter) { f.dot = b }
  44. }
  45. // Squeeze sets the printing behaviour to minimise column width for each individual column.
  46. func Squeeze() FormatOption {
  47. return func(f *formatter) { f.squeeze = true }
  48. }
  49. // Format satisfies the fmt.Formatter interface.
  50. func (f formatter) Format(fs fmt.State, c rune) {
  51. if c == 'v' && fs.Flag('#') {
  52. fmt.Fprintf(fs, "%#v", f.matrix)
  53. return
  54. }
  55. format(f.matrix, f.prefix, f.margin, f.dot, f.squeeze, fs, c)
  56. }
  57. // format prints a pretty representation of m to the fs io.Writer. The format character c
  58. // specifies the numerical representation of elements; valid values are those for float64
  59. // specified in the fmt package, with their associated flags. In addition to this, a space
  60. // preceding a verb indicates that zero values should be represented by the dot character.
  61. // The printed range of the matrix can be limited by specifying a positive value for margin;
  62. // If margin is greater than zero, only the first and last margin rows/columns of the matrix
  63. // are output. If squeeze is true, column widths are determined on a per-column basis.
  64. //
  65. // format will not provide Go syntax output.
  66. func format(m Matrix, prefix string, margin int, dot byte, squeeze bool, fs fmt.State, c rune) {
  67. rows, cols := m.Dims()
  68. var printed int
  69. if margin <= 0 {
  70. printed = rows
  71. if cols > printed {
  72. printed = cols
  73. }
  74. } else {
  75. printed = margin
  76. }
  77. prec, pOk := fs.Precision()
  78. if !pOk {
  79. prec = -1
  80. }
  81. var (
  82. maxWidth int
  83. widths widther
  84. buf, pad []byte
  85. )
  86. if squeeze {
  87. widths = make(columnWidth, cols)
  88. } else {
  89. widths = new(uniformWidth)
  90. }
  91. switch c {
  92. case 'v', 'e', 'E', 'f', 'F', 'g', 'G':
  93. if c == 'v' {
  94. buf, maxWidth = maxCellWidth(m, 'g', printed, prec, widths)
  95. } else {
  96. buf, maxWidth = maxCellWidth(m, c, printed, prec, widths)
  97. }
  98. default:
  99. fmt.Fprintf(fs, "%%!%c(%T=Dims(%d, %d))", c, m, rows, cols)
  100. return
  101. }
  102. width, _ := fs.Width()
  103. width = max(width, maxWidth)
  104. pad = make([]byte, max(width, 2))
  105. for i := range pad {
  106. pad[i] = ' '
  107. }
  108. first := true
  109. if rows > 2*printed || cols > 2*printed {
  110. first = false
  111. fmt.Fprintf(fs, "Dims(%d, %d)\n", rows, cols)
  112. }
  113. skipZero := fs.Flag(' ')
  114. for i := 0; i < rows; i++ {
  115. if !first {
  116. fmt.Fprint(fs, prefix)
  117. }
  118. first = false
  119. var el string
  120. switch {
  121. case rows == 1:
  122. fmt.Fprint(fs, "[")
  123. el = "]"
  124. case i == 0:
  125. fmt.Fprint(fs, "⎡")
  126. el = "⎤\n"
  127. case i < rows-1:
  128. fmt.Fprint(fs, "⎢")
  129. el = "⎥\n"
  130. default:
  131. fmt.Fprint(fs, "⎣")
  132. el = "⎦"
  133. }
  134. for j := 0; j < cols; j++ {
  135. if j >= printed && j < cols-printed {
  136. j = cols - printed - 1
  137. if i == 0 || i == rows-1 {
  138. fmt.Fprint(fs, "... ... ")
  139. } else {
  140. fmt.Fprint(fs, " ")
  141. }
  142. continue
  143. }
  144. v := m.At(i, j)
  145. if v == 0 && skipZero {
  146. buf = buf[:1]
  147. buf[0] = dot
  148. } else {
  149. if c == 'v' {
  150. buf = strconv.AppendFloat(buf[:0], v, 'g', prec, 64)
  151. } else {
  152. buf = strconv.AppendFloat(buf[:0], v, byte(c), prec, 64)
  153. }
  154. }
  155. if fs.Flag('-') {
  156. fs.Write(buf)
  157. fs.Write(pad[:widths.width(j)-len(buf)])
  158. } else {
  159. fs.Write(pad[:widths.width(j)-len(buf)])
  160. fs.Write(buf)
  161. }
  162. if j < cols-1 {
  163. fs.Write(pad[:2])
  164. }
  165. }
  166. fmt.Fprint(fs, el)
  167. if i >= printed-1 && i < rows-printed && 2*printed < rows {
  168. i = rows - printed - 1
  169. fmt.Fprintf(fs, "%s .\n%[1]s .\n%[1]s .\n", prefix)
  170. continue
  171. }
  172. }
  173. }
  174. func maxCellWidth(m Matrix, c rune, printed, prec int, w widther) ([]byte, int) {
  175. var (
  176. buf = make([]byte, 0, 64)
  177. rows, cols = m.Dims()
  178. max int
  179. )
  180. for i := 0; i < rows; i++ {
  181. if i >= printed-1 && i < rows-printed && 2*printed < rows {
  182. i = rows - printed - 1
  183. continue
  184. }
  185. for j := 0; j < cols; j++ {
  186. if j >= printed && j < cols-printed {
  187. continue
  188. }
  189. buf = strconv.AppendFloat(buf, m.At(i, j), byte(c), prec, 64)
  190. if len(buf) > max {
  191. max = len(buf)
  192. }
  193. if len(buf) > w.width(j) {
  194. w.setWidth(j, len(buf))
  195. }
  196. buf = buf[:0]
  197. }
  198. }
  199. return buf, max
  200. }
  201. type widther interface {
  202. width(i int) int
  203. setWidth(i, w int)
  204. }
  205. type uniformWidth int
  206. func (u *uniformWidth) width(_ int) int { return int(*u) }
  207. func (u *uniformWidth) setWidth(_, w int) { *u = uniformWidth(w) }
  208. type columnWidth []int
  209. func (c columnWidth) width(i int) int { return c[i] }
  210. func (c columnWidth) setWidth(i, w int) { c[i] = w }