blob: eaf92d4b47ea3d199c123a0881578fb05bb5dd88 [file] [log] [blame]
// Copyright ©2017 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 mmarket provides a type to read Matrix Market format files.
package mmarket
import (
"bufio"
"errors"
"fmt"
"io"
"strings"
"gonum.org/v1/exp/linsolve/internal/triplet"
)
var (
errBadFormat = errors.New("mmarket: bad file format")
errUnsupported = errors.New("mmarket: matrix type not supported")
)
type Reader struct {
s *bufio.Scanner
}
func NewReader(r io.Reader) *Reader {
return &Reader{
s: bufio.NewScanner(r),
}
}
// Read a real matrix in coordinate format and return its triplet representation.
func (r *Reader) Read() (*triplet.Matrix, error) {
r.s.Scan()
if err := r.s.Err(); err != nil {
return nil, err
}
header := strings.Fields(r.s.Text())
if header[0] != "%%MatrixMarket" {
return nil, errBadFormat
}
if header[1] != "matrix" {
return nil, errBadFormat
}
if header[2] != "coordinate" {
return nil, errBadFormat
}
if header[3] != "real" {
return nil, errUnsupported
}
sym := header[4] == "symmetric"
var nr, nc, nnz int
for r.s.Scan() {
line := r.s.Text()
if line[0] == '%' {
continue
}
n, err := fmt.Sscan(line, &nr, &nc, &nnz)
if err != nil {
return nil, err
}
if n != 3 {
return nil, errBadFormat
}
break
}
if err := r.s.Err(); err != nil {
return nil, err
}
if sym && nr != nc {
return nil, errBadFormat
}
m := triplet.NewMatrix(nr, nc)
for i := 0; i < nnz; i++ {
if !r.s.Scan() {
return nil, errBadFormat
}
var (
i, j int
v float64
)
n, err := fmt.Sscan(r.s.Text(), &i, &j, &v)
if err != nil {
return nil, err
}
if n != 3 {
return nil, errBadFormat
}
if i < 1 || nr < i {
return nil, errBadFormat
}
if j < 1 || nc < j {
return nil, errBadFormat
}
m.Append(i-1, j-1, v)
if sym && i != j {
m.Append(j-1, i-1, v)
}
}
return m, nil
}