/
decode.go
120 lines (102 loc) · 2.36 KB
/
decode.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
package xyz
import (
"bufio"
"errors"
"io"
"strconv"
"strings"
)
var (
// ErrInvalidMolecule is returned when the given molecule input isn't valid.
ErrInvalidMolecule = errors.New("xyz: input is not a valid molecule")
)
// Decoder reads and decodes lists of molecules.
type Decoder struct {
reader io.Reader
scanner *bufio.Scanner
err error
}
// NewDecoder returns a decoder reading from the given reader.
//
// The decoder is buffered and may read data beyond the molecule data
// requests.
func NewDecoder(reader io.Reader) *Decoder {
return &Decoder{reader: reader, scanner: bufio.NewScanner(reader)}
}
// Decode decodes the next molecule in the reader.
func (decoder *Decoder) Decode() (*Molecule, error) {
if decoder.err != nil {
return nil, decoder.err
}
molecule := &Molecule{Atoms: make([]*Atom, 0, 16)}
num := 0
i := -1
for decoder.scanner.Scan() {
line := strings.TrimSpace(decoder.scanner.Text())
if i == -1 && line == "" {
continue
}
i++
// The first line should contain the number of atoms to read.
if i == 0 {
var err error
num, err = strconv.Atoi(line)
if err != nil {
decoder.err = ErrInvalidMolecule
return nil, decoder.err
}
continue
}
// Next is the comment for the molecule.
if i == 1 {
molecule.Comment = line
// If no molecules need to be read break.
if num == 0 {
break
}
continue
}
// Parse the atoms informations.
items := strings.Fields(line)
if len(items) != 4 {
decoder.err = ErrInvalidMolecule
return nil, decoder.err
}
element, err := ParseElement([]byte(items[0]))
if err != nil {
decoder.err = err
return nil, err
}
var y float64
var z float64
x, err := strconv.ParseFloat(items[1], 64)
if err == nil {
y, err = strconv.ParseFloat(items[2], 64)
}
if err == nil {
z, err = strconv.ParseFloat(items[3], 64)
}
if err != nil {
decoder.err = err
return nil, err
}
atom := &Atom{Element: element, X: x, Y: y, Z: z}
molecule.Atoms = append(molecule.Atoms, atom)
// If we're at the last atom for the molecule break, that way
// we don't read items for another molecule.
if i == num+1 {
break
}
}
// If i hasn't been set, then it's EOF.
if i == -1 {
decoder.err = io.EOF
return nil, decoder.err
}
err := decoder.scanner.Err()
if err != nil {
decoder.err = err
return nil, err
}
return molecule, nil
}