format.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516
  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. "strings"
  9. )
  10. // Formatted returns a fmt.Formatter for the matrix m using the given options.
  11. func Formatted(m Matrix, options ...FormatOption) fmt.Formatter {
  12. f := formatter{
  13. matrix: m,
  14. dot: '.',
  15. }
  16. for _, o := range options {
  17. o(&f)
  18. }
  19. return f
  20. }
  21. type formatter struct {
  22. matrix Matrix
  23. prefix string
  24. margin int
  25. dot byte
  26. squeeze bool
  27. format func(m Matrix, prefix string, margin int, dot byte, squeeze bool, fs fmt.State, c rune)
  28. }
  29. // FormatOption is a functional option for matrix formatting.
  30. type FormatOption func(*formatter)
  31. // Prefix sets the formatted prefix to the string p. Prefix is a string that is prepended to
  32. // each line of output after the first line.
  33. func Prefix(p string) FormatOption {
  34. return func(f *formatter) { f.prefix = p }
  35. }
  36. // Excerpt sets the maximum number of rows and columns to print at the margins of the matrix
  37. // to m. If m is zero or less all elements are printed.
  38. func Excerpt(m int) FormatOption {
  39. return func(f *formatter) { f.margin = m }
  40. }
  41. // DotByte sets the dot character to b. The dot character is used to replace zero elements
  42. // if the result is printed with the fmt ' ' verb flag. Without a DotByte option, the default
  43. // dot character is '.'.
  44. func DotByte(b byte) FormatOption {
  45. return func(f *formatter) { f.dot = b }
  46. }
  47. // Squeeze sets the printing behavior to minimise column width for each individual column.
  48. func Squeeze() FormatOption {
  49. return func(f *formatter) { f.squeeze = true }
  50. }
  51. // FormatMATLAB sets the printing behavior to output MATLAB syntax. If MATLAB syntax is
  52. // specified, the ' ' verb flag and Excerpt option are ignored. If the alternative syntax
  53. // verb flag, '#' is used the matrix is formatted in rows and columns.
  54. func FormatMATLAB() FormatOption {
  55. return func(f *formatter) { f.format = formatMATLAB }
  56. }
  57. // FormatPython sets the printing behavior to output Python syntax. If Python syntax is
  58. // specified, the ' ' verb flag and Excerpt option are ignored. If the alternative syntax
  59. // verb flag, '#' is used the matrix is formatted in rows and columns.
  60. func FormatPython() FormatOption {
  61. return func(f *formatter) { f.format = formatPython }
  62. }
  63. // Format satisfies the fmt.Formatter interface.
  64. func (f formatter) Format(fs fmt.State, c rune) {
  65. if c == 'v' && fs.Flag('#') && f.format == nil {
  66. fmt.Fprintf(fs, "%#v", f.matrix)
  67. return
  68. }
  69. if f.format == nil {
  70. f.format = format
  71. }
  72. f.format(f.matrix, f.prefix, f.margin, f.dot, f.squeeze, fs, c)
  73. }
  74. // format prints a pretty representation of m to the fs io.Writer. The format character c
  75. // specifies the numerical representation of elements; valid values are those for float64
  76. // specified in the fmt package, with their associated flags. In addition to this, a space
  77. // preceding a verb indicates that zero values should be represented by the dot character.
  78. // The printed range of the matrix can be limited by specifying a positive value for margin;
  79. // If margin is greater than zero, only the first and last margin rows/columns of the matrix
  80. // are output. If squeeze is true, column widths are determined on a per-column basis.
  81. //
  82. // format will not provide Go syntax output.
  83. func format(m Matrix, prefix string, margin int, dot byte, squeeze bool, fs fmt.State, c rune) {
  84. rows, cols := m.Dims()
  85. var printed int
  86. if margin <= 0 {
  87. printed = rows
  88. if cols > printed {
  89. printed = cols
  90. }
  91. } else {
  92. printed = margin
  93. }
  94. prec, pOk := fs.Precision()
  95. if !pOk {
  96. prec = -1
  97. }
  98. var (
  99. maxWidth int
  100. widths widther
  101. buf, pad []byte
  102. )
  103. if squeeze {
  104. widths = make(columnWidth, cols)
  105. } else {
  106. widths = new(uniformWidth)
  107. }
  108. switch c {
  109. case 'v', 'e', 'E', 'f', 'F', 'g', 'G':
  110. if c == 'v' {
  111. buf, maxWidth = maxCellWidth(m, 'g', printed, prec, widths)
  112. } else {
  113. buf, maxWidth = maxCellWidth(m, c, printed, prec, widths)
  114. }
  115. default:
  116. fmt.Fprintf(fs, "%%!%c(%T=Dims(%d, %d))", c, m, rows, cols)
  117. return
  118. }
  119. width, _ := fs.Width()
  120. width = max(width, maxWidth)
  121. pad = make([]byte, max(width, 2))
  122. for i := range pad {
  123. pad[i] = ' '
  124. }
  125. first := true
  126. if rows > 2*printed || cols > 2*printed {
  127. first = false
  128. fmt.Fprintf(fs, "Dims(%d, %d)\n", rows, cols)
  129. }
  130. skipZero := fs.Flag(' ')
  131. for i := 0; i < rows; i++ {
  132. if !first {
  133. fmt.Fprint(fs, prefix)
  134. }
  135. first = false
  136. var el string
  137. switch {
  138. case rows == 1:
  139. fmt.Fprint(fs, "[")
  140. el = "]"
  141. case i == 0:
  142. fmt.Fprint(fs, "⎡")
  143. el = "⎤\n"
  144. case i < rows-1:
  145. fmt.Fprint(fs, "⎢")
  146. el = "⎥\n"
  147. default:
  148. fmt.Fprint(fs, "⎣")
  149. el = "⎦"
  150. }
  151. for j := 0; j < cols; j++ {
  152. if j >= printed && j < cols-printed {
  153. j = cols - printed - 1
  154. if i == 0 || i == rows-1 {
  155. fmt.Fprint(fs, "... ... ")
  156. } else {
  157. fmt.Fprint(fs, " ")
  158. }
  159. continue
  160. }
  161. v := m.At(i, j)
  162. if v == 0 && skipZero {
  163. buf = buf[:1]
  164. buf[0] = dot
  165. } else {
  166. if c == 'v' {
  167. buf = strconv.AppendFloat(buf[:0], v, 'g', prec, 64)
  168. } else {
  169. buf = strconv.AppendFloat(buf[:0], v, byte(c), prec, 64)
  170. }
  171. }
  172. if fs.Flag('-') {
  173. fs.Write(buf)
  174. fs.Write(pad[:widths.width(j)-len(buf)])
  175. } else {
  176. fs.Write(pad[:widths.width(j)-len(buf)])
  177. fs.Write(buf)
  178. }
  179. if j < cols-1 {
  180. fs.Write(pad[:2])
  181. }
  182. }
  183. fmt.Fprint(fs, el)
  184. if i >= printed-1 && i < rows-printed && 2*printed < rows {
  185. i = rows - printed - 1
  186. fmt.Fprintf(fs, "%s .\n%[1]s .\n%[1]s .\n", prefix)
  187. continue
  188. }
  189. }
  190. }
  191. // formatMATLAB prints a MATLAB representation of m to the fs io.Writer. The format character c
  192. // specifies the numerical representation of elements; valid values are those for float64
  193. // specified in the fmt package, with their associated flags.
  194. // The printed range of the matrix can be limited by specifying a positive value for margin;
  195. // If squeeze is true, column widths are determined on a per-column basis.
  196. //
  197. // formatMATLAB will not provide Go syntax output.
  198. func formatMATLAB(m Matrix, prefix string, _ int, _ byte, squeeze bool, fs fmt.State, c rune) {
  199. rows, cols := m.Dims()
  200. prec, pOk := fs.Precision()
  201. width, _ := fs.Width()
  202. if !fs.Flag('#') {
  203. switch c {
  204. case 'v', 'e', 'E', 'f', 'F', 'g', 'G':
  205. default:
  206. fmt.Fprintf(fs, "%%!%c(%T=Dims(%d, %d))", c, m, rows, cols)
  207. return
  208. }
  209. format := fmtString(fs, c, prec, width)
  210. fs.Write([]byte{'['})
  211. for i := 0; i < rows; i++ {
  212. if i != 0 {
  213. fs.Write([]byte("; "))
  214. }
  215. for j := 0; j < cols; j++ {
  216. if j != 0 {
  217. fs.Write([]byte{' '})
  218. }
  219. fmt.Fprintf(fs, format, m.At(i, j))
  220. }
  221. }
  222. fs.Write([]byte{']'})
  223. return
  224. }
  225. if !pOk {
  226. prec = -1
  227. }
  228. printed := rows
  229. if cols > printed {
  230. printed = cols
  231. }
  232. var (
  233. maxWidth int
  234. widths widther
  235. buf, pad []byte
  236. )
  237. if squeeze {
  238. widths = make(columnWidth, cols)
  239. } else {
  240. widths = new(uniformWidth)
  241. }
  242. switch c {
  243. case 'v', 'e', 'E', 'f', 'F', 'g', 'G':
  244. if c == 'v' {
  245. buf, maxWidth = maxCellWidth(m, 'g', printed, prec, widths)
  246. } else {
  247. buf, maxWidth = maxCellWidth(m, c, printed, prec, widths)
  248. }
  249. default:
  250. fmt.Fprintf(fs, "%%!%c(%T=Dims(%d, %d))", c, m, rows, cols)
  251. return
  252. }
  253. width = max(width, maxWidth)
  254. pad = make([]byte, max(width, 1))
  255. for i := range pad {
  256. pad[i] = ' '
  257. }
  258. for i := 0; i < rows; i++ {
  259. var el string
  260. switch {
  261. case rows == 1:
  262. fmt.Fprint(fs, "[")
  263. el = "]"
  264. case i == 0:
  265. fmt.Fprint(fs, "[\n"+prefix+" ")
  266. el = "\n"
  267. case i < rows-1:
  268. fmt.Fprint(fs, prefix+" ")
  269. el = "\n"
  270. default:
  271. fmt.Fprint(fs, prefix+" ")
  272. el = "\n" + prefix + "]"
  273. }
  274. for j := 0; j < cols; j++ {
  275. v := m.At(i, j)
  276. if c == 'v' {
  277. buf = strconv.AppendFloat(buf[:0], v, 'g', prec, 64)
  278. } else {
  279. buf = strconv.AppendFloat(buf[:0], v, byte(c), prec, 64)
  280. }
  281. if fs.Flag('-') {
  282. fs.Write(buf)
  283. fs.Write(pad[:widths.width(j)-len(buf)])
  284. } else {
  285. fs.Write(pad[:widths.width(j)-len(buf)])
  286. fs.Write(buf)
  287. }
  288. if j < cols-1 {
  289. fs.Write(pad[:1])
  290. }
  291. }
  292. fmt.Fprint(fs, el)
  293. }
  294. }
  295. // formatPython prints a Python representation of m to the fs io.Writer. The format character c
  296. // specifies the numerical representation of elements; valid values are those for float64
  297. // specified in the fmt package, with their associated flags.
  298. // The printed range of the matrix can be limited by specifying a positive value for margin;
  299. // If squeeze is true, column widths are determined on a per-column basis.
  300. //
  301. // formatPython will not provide Go syntax output.
  302. func formatPython(m Matrix, prefix string, _ int, _ byte, squeeze bool, fs fmt.State, c rune) {
  303. rows, cols := m.Dims()
  304. prec, pOk := fs.Precision()
  305. width, _ := fs.Width()
  306. if !fs.Flag('#') {
  307. switch c {
  308. case 'v', 'e', 'E', 'f', 'F', 'g', 'G':
  309. default:
  310. fmt.Fprintf(fs, "%%!%c(%T=Dims(%d, %d))", c, m, rows, cols)
  311. return
  312. }
  313. format := fmtString(fs, c, prec, width)
  314. fs.Write([]byte{'['})
  315. if rows > 1 {
  316. fs.Write([]byte{'['})
  317. }
  318. for i := 0; i < rows; i++ {
  319. if i != 0 {
  320. fs.Write([]byte("], ["))
  321. }
  322. for j := 0; j < cols; j++ {
  323. if j != 0 {
  324. fs.Write([]byte(", "))
  325. }
  326. fmt.Fprintf(fs, format, m.At(i, j))
  327. }
  328. }
  329. if rows > 1 {
  330. fs.Write([]byte{']'})
  331. }
  332. fs.Write([]byte{']'})
  333. return
  334. }
  335. if !pOk {
  336. prec = -1
  337. }
  338. printed := rows
  339. if cols > printed {
  340. printed = cols
  341. }
  342. var (
  343. maxWidth int
  344. widths widther
  345. buf, pad []byte
  346. )
  347. if squeeze {
  348. widths = make(columnWidth, cols)
  349. } else {
  350. widths = new(uniformWidth)
  351. }
  352. switch c {
  353. case 'v', 'e', 'E', 'f', 'F', 'g', 'G':
  354. if c == 'v' {
  355. buf, maxWidth = maxCellWidth(m, 'g', printed, prec, widths)
  356. } else {
  357. buf, maxWidth = maxCellWidth(m, c, printed, prec, widths)
  358. }
  359. default:
  360. fmt.Fprintf(fs, "%%!%c(%T=Dims(%d, %d))", c, m, rows, cols)
  361. return
  362. }
  363. width = max(width, maxWidth)
  364. pad = make([]byte, max(width, 1))
  365. for i := range pad {
  366. pad[i] = ' '
  367. }
  368. for i := 0; i < rows; i++ {
  369. if i != 0 {
  370. fmt.Fprint(fs, prefix)
  371. }
  372. var el string
  373. switch {
  374. case rows == 1:
  375. fmt.Fprint(fs, "[")
  376. el = "]"
  377. case i == 0:
  378. fmt.Fprint(fs, "[[")
  379. el = "],\n"
  380. case i < rows-1:
  381. fmt.Fprint(fs, " [")
  382. el = "],\n"
  383. default:
  384. fmt.Fprint(fs, " [")
  385. el = "]]"
  386. }
  387. for j := 0; j < cols; j++ {
  388. v := m.At(i, j)
  389. if c == 'v' {
  390. buf = strconv.AppendFloat(buf[:0], v, 'g', prec, 64)
  391. } else {
  392. buf = strconv.AppendFloat(buf[:0], v, byte(c), prec, 64)
  393. }
  394. if fs.Flag('-') {
  395. fs.Write(buf)
  396. fs.Write(pad[:widths.width(j)-len(buf)])
  397. } else {
  398. fs.Write(pad[:widths.width(j)-len(buf)])
  399. fs.Write(buf)
  400. }
  401. if j < cols-1 {
  402. fs.Write([]byte{','})
  403. fs.Write(pad[:1])
  404. }
  405. }
  406. fmt.Fprint(fs, el)
  407. }
  408. }
  409. // This is horrible, but it's what we have.
  410. func fmtString(fs fmt.State, c rune, prec, width int) string {
  411. var b strings.Builder
  412. b.WriteByte('%')
  413. for _, f := range "0+- " {
  414. if fs.Flag(int(f)) {
  415. b.WriteByte(byte(f))
  416. }
  417. }
  418. if width >= 0 {
  419. fmt.Fprint(&b, width)
  420. }
  421. if prec >= 0 {
  422. b.WriteByte('.')
  423. if prec > 0 {
  424. fmt.Fprint(&b, prec)
  425. }
  426. }
  427. b.WriteRune(c)
  428. return b.String()
  429. }
  430. func maxCellWidth(m Matrix, c rune, printed, prec int, w widther) ([]byte, int) {
  431. var (
  432. buf = make([]byte, 0, 64)
  433. rows, cols = m.Dims()
  434. max int
  435. )
  436. for i := 0; i < rows; i++ {
  437. if i >= printed-1 && i < rows-printed && 2*printed < rows {
  438. i = rows - printed - 1
  439. continue
  440. }
  441. for j := 0; j < cols; j++ {
  442. if j >= printed && j < cols-printed {
  443. continue
  444. }
  445. buf = strconv.AppendFloat(buf, m.At(i, j), byte(c), prec, 64)
  446. if len(buf) > max {
  447. max = len(buf)
  448. }
  449. if len(buf) > w.width(j) {
  450. w.setWidth(j, len(buf))
  451. }
  452. buf = buf[:0]
  453. }
  454. }
  455. return buf, max
  456. }
  457. type widther interface {
  458. width(i int) int
  459. setWidth(i, w int)
  460. }
  461. type uniformWidth int
  462. func (u *uniformWidth) width(_ int) int { return int(*u) }
  463. func (u *uniformWidth) setWidth(_, w int) { *u = uniformWidth(w) }
  464. type columnWidth []int
  465. func (c columnWidth) width(i int) int { return c[i] }
  466. func (c columnWidth) setWidth(i, w int) { c[i] = w }