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.
12 // Formatted returns a fmt.Formatter for the matrix m using the given options.
13 func Formatted(m Matrix, options ...FormatOption) fmt.Formatter {
18 for _, o := range options {
24 type formatter struct {
32 // FormatOption is a functional option for matrix formatting.
33 type FormatOption func(*formatter)
35 // Prefix sets the formatted prefix to the string p. Prefix is a string that is prepended to
36 // each line of output.
37 func Prefix(p string) FormatOption {
38 return func(f *formatter) { f.prefix = p }
41 // Excerpt sets the maximum number of rows and columns to print at the margins of the matrix
42 // to m. If m is zero or less all elements are printed.
43 func Excerpt(m int) FormatOption {
44 return func(f *formatter) { f.margin = m }
47 // DotByte sets the dot character to b. The dot character is used to replace zero elements
48 // if the result is printed with the fmt ' ' verb flag. Without a DotByte option, the default
49 // dot character is '.'.
50 func DotByte(b byte) FormatOption {
51 return func(f *formatter) { f.dot = b }
54 // Squeeze sets the printing behaviour to minimise column width for each individual column.
55 func Squeeze() FormatOption {
56 return func(f *formatter) { f.squeeze = true }
59 // Format satisfies the fmt.Formatter interface.
60 func (f formatter) Format(fs fmt.State, c rune) {
61 if c == 'v' && fs.Flag('#') {
62 fmt.Fprintf(fs, "%#v", f.matrix)
65 format(f.matrix, f.prefix, f.margin, f.dot, f.squeeze, fs, c)
68 // format prints a pretty representation of m to the fs io.Writer. The format character c
69 // specifies the numerical representation of of elements; valid values are those for float64
70 // specified in the fmt package, with their associated flags. In addition to this, a space
71 // preceding a verb indicates that zero values should be represented by the dot character.
72 // The printed range of the matrix can be limited by specifying a positive value for margin;
73 // If margin is greater than zero, only the first and last margin rows/columns of the matrix
74 // are output. If squeeze is true, column widths are determined on a per-column basis.
76 // format will not provide Go syntax output.
77 func format(m Matrix, prefix string, margin int, dot byte, squeeze bool, fs fmt.State, c rune) {
78 rows, cols := m.Dims()
90 prec, pOk := fs.Precision()
101 widths = make(columnWidth, cols)
103 widths = new(uniformWidth)
106 case 'v', 'e', 'E', 'f', 'F', 'g', 'G':
108 buf, maxWidth = maxCellWidth(m, 'g', printed, prec, widths)
110 buf, maxWidth = maxCellWidth(m, c, printed, prec, widths)
113 fmt.Fprintf(fs, "%%!%c(%T=Dims(%d, %d))", c, m, rows, cols)
116 width, _ := fs.Width()
117 width = max(width, maxWidth)
118 pad = make([]byte, max(width, 2))
124 if rows > 2*printed || cols > 2*printed {
126 fmt.Fprintf(fs, "Dims(%d, %d)\n", rows, cols)
129 skipZero := fs.Flag(' ')
130 for i := 0; i < rows; i++ {
132 fmt.Fprint(fs, prefix)
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, "... ... ")
163 if v == 0 && skipZero {
168 buf = strconv.AppendFloat(buf[:0], v, 'g', prec, 64)
170 buf = strconv.AppendFloat(buf[:0], v, byte(c), prec, 64)
175 fs.Write(pad[:widths.width(j)-len(buf)])
177 fs.Write(pad[:widths.width(j)-len(buf)])
188 if i >= printed-1 && i < rows-printed && 2*printed < rows {
189 i = rows - printed - 1
190 fmt.Fprintf(fs, "%s .\n%[1]s .\n%[1]s .\n", prefix)
196 func maxCellWidth(m Matrix, c rune, printed, prec int, w widther) ([]byte, int) {
198 buf = make([]byte, 0, 64)
199 rows, cols = m.Dims()
202 for i := 0; i < rows; i++ {
203 if i >= printed-1 && i < rows-printed && 2*printed < rows {
204 i = rows - printed - 1
207 for j := 0; j < cols; j++ {
208 if j >= printed && j < cols-printed {
212 buf = strconv.AppendFloat(buf, m.At(i, j), byte(c), prec, 64)
216 if len(buf) > w.width(j) {
217 w.setWidth(j, len(buf))
225 type widther interface {
230 type uniformWidth int
232 func (u *uniformWidth) width(_ int) int { return int(*u) }
233 func (u *uniformWidth) setWidth(_, w int) { *u = uniformWidth(w) }
235 type columnWidth []int
237 func (c columnWidth) width(i int) int { return c[i] }
238 func (c columnWidth) setWidth(i, w int) { c[i] = w }