123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516 |
- // Copyright ©2013 The Gonum Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style
- // license that can be found in the LICENSE file.
- package mat
- import (
- "fmt"
- "strconv"
- "strings"
- )
- // Formatted returns a fmt.Formatter for the matrix m using the given options.
- func Formatted(m Matrix, options ...FormatOption) fmt.Formatter {
- f := formatter{
- matrix: m,
- dot: '.',
- }
- for _, o := range options {
- o(&f)
- }
- return f
- }
- type formatter struct {
- matrix Matrix
- prefix string
- margin int
- dot byte
- squeeze bool
- format func(m Matrix, prefix string, margin int, dot byte, squeeze bool, fs fmt.State, c rune)
- }
- // FormatOption is a functional option for matrix formatting.
- type FormatOption func(*formatter)
- // Prefix sets the formatted prefix to the string p. Prefix is a string that is prepended to
- // each line of output after the first line.
- func Prefix(p string) FormatOption {
- return func(f *formatter) { f.prefix = p }
- }
- // Excerpt sets the maximum number of rows and columns to print at the margins of the matrix
- // to m. If m is zero or less all elements are printed.
- func Excerpt(m int) FormatOption {
- return func(f *formatter) { f.margin = m }
- }
- // DotByte sets the dot character to b. The dot character is used to replace zero elements
- // if the result is printed with the fmt ' ' verb flag. Without a DotByte option, the default
- // dot character is '.'.
- func DotByte(b byte) FormatOption {
- return func(f *formatter) { f.dot = b }
- }
- // Squeeze sets the printing behavior to minimise column width for each individual column.
- func Squeeze() FormatOption {
- return func(f *formatter) { f.squeeze = true }
- }
- // FormatMATLAB sets the printing behavior to output MATLAB syntax. If MATLAB syntax is
- // specified, the ' ' verb flag and Excerpt option are ignored. If the alternative syntax
- // verb flag, '#' is used the matrix is formatted in rows and columns.
- func FormatMATLAB() FormatOption {
- return func(f *formatter) { f.format = formatMATLAB }
- }
- // FormatPython sets the printing behavior to output Python syntax. If Python syntax is
- // specified, the ' ' verb flag and Excerpt option are ignored. If the alternative syntax
- // verb flag, '#' is used the matrix is formatted in rows and columns.
- func FormatPython() FormatOption {
- return func(f *formatter) { f.format = formatPython }
- }
- // Format satisfies the fmt.Formatter interface.
- func (f formatter) Format(fs fmt.State, c rune) {
- if c == 'v' && fs.Flag('#') && f.format == nil {
- fmt.Fprintf(fs, "%#v", f.matrix)
- return
- }
- if f.format == nil {
- f.format = format
- }
- f.format(f.matrix, f.prefix, f.margin, f.dot, f.squeeze, fs, c)
- }
- // format prints a pretty representation of m to the fs io.Writer. The format character c
- // specifies the numerical representation of elements; valid values are those for float64
- // specified in the fmt package, with their associated flags. In addition to this, a space
- // preceding a verb indicates that zero values should be represented by the dot character.
- // The printed range of the matrix can be limited by specifying a positive value for margin;
- // If margin is greater than zero, only the first and last margin rows/columns of the matrix
- // are output. If squeeze is true, column widths are determined on a per-column basis.
- //
- // format will not provide Go syntax output.
- func format(m Matrix, prefix string, margin int, dot byte, squeeze bool, fs fmt.State, c rune) {
- rows, cols := m.Dims()
- var printed int
- if margin <= 0 {
- printed = rows
- if cols > printed {
- printed = cols
- }
- } else {
- printed = margin
- }
- prec, pOk := fs.Precision()
- if !pOk {
- prec = -1
- }
- var (
- maxWidth int
- widths widther
- buf, pad []byte
- )
- if squeeze {
- widths = make(columnWidth, cols)
- } else {
- widths = new(uniformWidth)
- }
- switch c {
- case 'v', 'e', 'E', 'f', 'F', 'g', 'G':
- if c == 'v' {
- buf, maxWidth = maxCellWidth(m, 'g', printed, prec, widths)
- } else {
- buf, maxWidth = maxCellWidth(m, c, printed, prec, widths)
- }
- default:
- fmt.Fprintf(fs, "%%!%c(%T=Dims(%d, %d))", c, m, rows, cols)
- return
- }
- width, _ := fs.Width()
- width = max(width, maxWidth)
- pad = make([]byte, max(width, 2))
- for i := range pad {
- pad[i] = ' '
- }
- first := true
- if rows > 2*printed || cols > 2*printed {
- first = false
- fmt.Fprintf(fs, "Dims(%d, %d)\n", rows, cols)
- }
- skipZero := fs.Flag(' ')
- for i := 0; i < rows; i++ {
- if !first {
- fmt.Fprint(fs, prefix)
- }
- first = false
- var el string
- switch {
- case rows == 1:
- fmt.Fprint(fs, "[")
- el = "]"
- case i == 0:
- fmt.Fprint(fs, "⎡")
- el = "⎤\n"
- case i < rows-1:
- fmt.Fprint(fs, "⎢")
- el = "⎥\n"
- default:
- fmt.Fprint(fs, "⎣")
- el = "⎦"
- }
- for j := 0; j < cols; j++ {
- if j >= printed && j < cols-printed {
- j = cols - printed - 1
- if i == 0 || i == rows-1 {
- fmt.Fprint(fs, "... ... ")
- } else {
- fmt.Fprint(fs, " ")
- }
- continue
- }
- v := m.At(i, j)
- if v == 0 && skipZero {
- buf = buf[:1]
- buf[0] = dot
- } else {
- if c == 'v' {
- buf = strconv.AppendFloat(buf[:0], v, 'g', prec, 64)
- } else {
- buf = strconv.AppendFloat(buf[:0], v, byte(c), prec, 64)
- }
- }
- if fs.Flag('-') {
- fs.Write(buf)
- fs.Write(pad[:widths.width(j)-len(buf)])
- } else {
- fs.Write(pad[:widths.width(j)-len(buf)])
- fs.Write(buf)
- }
- if j < cols-1 {
- fs.Write(pad[:2])
- }
- }
- fmt.Fprint(fs, el)
- if i >= printed-1 && i < rows-printed && 2*printed < rows {
- i = rows - printed - 1
- fmt.Fprintf(fs, "%s .\n%[1]s .\n%[1]s .\n", prefix)
- continue
- }
- }
- }
- // formatMATLAB prints a MATLAB representation of m to the fs io.Writer. The format character c
- // specifies the numerical representation of elements; valid values are those for float64
- // specified in the fmt package, with their associated flags.
- // The printed range of the matrix can be limited by specifying a positive value for margin;
- // If squeeze is true, column widths are determined on a per-column basis.
- //
- // formatMATLAB will not provide Go syntax output.
- func formatMATLAB(m Matrix, prefix string, _ int, _ byte, squeeze bool, fs fmt.State, c rune) {
- rows, cols := m.Dims()
- prec, pOk := fs.Precision()
- width, _ := fs.Width()
- if !fs.Flag('#') {
- switch c {
- case 'v', 'e', 'E', 'f', 'F', 'g', 'G':
- default:
- fmt.Fprintf(fs, "%%!%c(%T=Dims(%d, %d))", c, m, rows, cols)
- return
- }
- format := fmtString(fs, c, prec, width)
- fs.Write([]byte{'['})
- for i := 0; i < rows; i++ {
- if i != 0 {
- fs.Write([]byte("; "))
- }
- for j := 0; j < cols; j++ {
- if j != 0 {
- fs.Write([]byte{' '})
- }
- fmt.Fprintf(fs, format, m.At(i, j))
- }
- }
- fs.Write([]byte{']'})
- return
- }
- if !pOk {
- prec = -1
- }
- printed := rows
- if cols > printed {
- printed = cols
- }
- var (
- maxWidth int
- widths widther
- buf, pad []byte
- )
- if squeeze {
- widths = make(columnWidth, cols)
- } else {
- widths = new(uniformWidth)
- }
- switch c {
- case 'v', 'e', 'E', 'f', 'F', 'g', 'G':
- if c == 'v' {
- buf, maxWidth = maxCellWidth(m, 'g', printed, prec, widths)
- } else {
- buf, maxWidth = maxCellWidth(m, c, printed, prec, widths)
- }
- default:
- fmt.Fprintf(fs, "%%!%c(%T=Dims(%d, %d))", c, m, rows, cols)
- return
- }
- width = max(width, maxWidth)
- pad = make([]byte, max(width, 1))
- for i := range pad {
- pad[i] = ' '
- }
- for i := 0; i < rows; i++ {
- var el string
- switch {
- case rows == 1:
- fmt.Fprint(fs, "[")
- el = "]"
- case i == 0:
- fmt.Fprint(fs, "[\n"+prefix+" ")
- el = "\n"
- case i < rows-1:
- fmt.Fprint(fs, prefix+" ")
- el = "\n"
- default:
- fmt.Fprint(fs, prefix+" ")
- el = "\n" + prefix + "]"
- }
- for j := 0; j < cols; j++ {
- v := m.At(i, j)
- if c == 'v' {
- buf = strconv.AppendFloat(buf[:0], v, 'g', prec, 64)
- } else {
- buf = strconv.AppendFloat(buf[:0], v, byte(c), prec, 64)
- }
- if fs.Flag('-') {
- fs.Write(buf)
- fs.Write(pad[:widths.width(j)-len(buf)])
- } else {
- fs.Write(pad[:widths.width(j)-len(buf)])
- fs.Write(buf)
- }
- if j < cols-1 {
- fs.Write(pad[:1])
- }
- }
- fmt.Fprint(fs, el)
- }
- }
- // formatPython prints a Python representation of m to the fs io.Writer. The format character c
- // specifies the numerical representation of elements; valid values are those for float64
- // specified in the fmt package, with their associated flags.
- // The printed range of the matrix can be limited by specifying a positive value for margin;
- // If squeeze is true, column widths are determined on a per-column basis.
- //
- // formatPython will not provide Go syntax output.
- func formatPython(m Matrix, prefix string, _ int, _ byte, squeeze bool, fs fmt.State, c rune) {
- rows, cols := m.Dims()
- prec, pOk := fs.Precision()
- width, _ := fs.Width()
- if !fs.Flag('#') {
- switch c {
- case 'v', 'e', 'E', 'f', 'F', 'g', 'G':
- default:
- fmt.Fprintf(fs, "%%!%c(%T=Dims(%d, %d))", c, m, rows, cols)
- return
- }
- format := fmtString(fs, c, prec, width)
- fs.Write([]byte{'['})
- if rows > 1 {
- fs.Write([]byte{'['})
- }
- for i := 0; i < rows; i++ {
- if i != 0 {
- fs.Write([]byte("], ["))
- }
- for j := 0; j < cols; j++ {
- if j != 0 {
- fs.Write([]byte(", "))
- }
- fmt.Fprintf(fs, format, m.At(i, j))
- }
- }
- if rows > 1 {
- fs.Write([]byte{']'})
- }
- fs.Write([]byte{']'})
- return
- }
- if !pOk {
- prec = -1
- }
- printed := rows
- if cols > printed {
- printed = cols
- }
- var (
- maxWidth int
- widths widther
- buf, pad []byte
- )
- if squeeze {
- widths = make(columnWidth, cols)
- } else {
- widths = new(uniformWidth)
- }
- switch c {
- case 'v', 'e', 'E', 'f', 'F', 'g', 'G':
- if c == 'v' {
- buf, maxWidth = maxCellWidth(m, 'g', printed, prec, widths)
- } else {
- buf, maxWidth = maxCellWidth(m, c, printed, prec, widths)
- }
- default:
- fmt.Fprintf(fs, "%%!%c(%T=Dims(%d, %d))", c, m, rows, cols)
- return
- }
- width = max(width, maxWidth)
- pad = make([]byte, max(width, 1))
- for i := range pad {
- pad[i] = ' '
- }
- for i := 0; i < rows; i++ {
- if i != 0 {
- fmt.Fprint(fs, prefix)
- }
- var el string
- switch {
- case rows == 1:
- fmt.Fprint(fs, "[")
- el = "]"
- case i == 0:
- fmt.Fprint(fs, "[[")
- el = "],\n"
- case i < rows-1:
- fmt.Fprint(fs, " [")
- el = "],\n"
- default:
- fmt.Fprint(fs, " [")
- el = "]]"
- }
- for j := 0; j < cols; j++ {
- v := m.At(i, j)
- if c == 'v' {
- buf = strconv.AppendFloat(buf[:0], v, 'g', prec, 64)
- } else {
- buf = strconv.AppendFloat(buf[:0], v, byte(c), prec, 64)
- }
- if fs.Flag('-') {
- fs.Write(buf)
- fs.Write(pad[:widths.width(j)-len(buf)])
- } else {
- fs.Write(pad[:widths.width(j)-len(buf)])
- fs.Write(buf)
- }
- if j < cols-1 {
- fs.Write([]byte{','})
- fs.Write(pad[:1])
- }
- }
- fmt.Fprint(fs, el)
- }
- }
- // This is horrible, but it's what we have.
- func fmtString(fs fmt.State, c rune, prec, width int) string {
- var b strings.Builder
- b.WriteByte('%')
- for _, f := range "0+- " {
- if fs.Flag(int(f)) {
- b.WriteByte(byte(f))
- }
- }
- if width >= 0 {
- fmt.Fprint(&b, width)
- }
- if prec >= 0 {
- b.WriteByte('.')
- if prec > 0 {
- fmt.Fprint(&b, prec)
- }
- }
- b.WriteRune(c)
- return b.String()
- }
- func maxCellWidth(m Matrix, c rune, printed, prec int, w widther) ([]byte, int) {
- var (
- buf = make([]byte, 0, 64)
- rows, cols = m.Dims()
- max int
- )
- for i := 0; i < rows; i++ {
- if i >= printed-1 && i < rows-printed && 2*printed < rows {
- i = rows - printed - 1
- continue
- }
- for j := 0; j < cols; j++ {
- if j >= printed && j < cols-printed {
- continue
- }
- buf = strconv.AppendFloat(buf, m.At(i, j), byte(c), prec, 64)
- if len(buf) > max {
- max = len(buf)
- }
- if len(buf) > w.width(j) {
- w.setWidth(j, len(buf))
- }
- buf = buf[:0]
- }
- }
- return buf, max
- }
- type widther interface {
- width(i int) int
- setWidth(i, w int)
- }
- type uniformWidth int
- func (u *uniformWidth) width(_ int) int { return int(*u) }
- func (u *uniformWidth) setWidth(_, w int) { *u = uniformWidth(w) }
- type columnWidth []int
- func (c columnWidth) width(i int) int { return c[i] }
- func (c columnWidth) setWidth(i, w int) { c[i] = w }
|