mirror of
https://github.com/gonum/gonum.git
synced 2025-10-25 00:00:24 +08:00
cmplxs/cscalar: new package containing non-vector functions from cmplxs
This commit is contained in:
279
cmplxs/cscalar/parse.go
Normal file
279
cmplxs/cscalar/parse.go
Normal file
@@ -0,0 +1,279 @@
|
||||
// Copyright ©2020 The Gonum Authors. All rights reserved.
|
||||
// Use of this code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file
|
||||
|
||||
package cscalar
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/cmplx"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// parse converts the string s to a complex128. The string may be parenthesized and
|
||||
// has the format [±]N±Ni. The order of the components is not strict.
|
||||
func parse(s string) (complex128, error) {
|
||||
if len(s) == 0 {
|
||||
return 0, parseError{state: -1}
|
||||
}
|
||||
orig := s
|
||||
|
||||
wantClose := s[0] == '('
|
||||
if wantClose {
|
||||
if s[len(s)-1] != ')' {
|
||||
return 0, parseError{string: orig, state: -1}
|
||||
}
|
||||
s = s[1 : len(s)-1]
|
||||
}
|
||||
if len(s) == 0 {
|
||||
return 0, parseError{string: orig, state: -1}
|
||||
}
|
||||
switch s[0] {
|
||||
case 'n', 'N':
|
||||
if strings.ToLower(s) == "nan" {
|
||||
return cmplx.NaN(), nil
|
||||
}
|
||||
case 'i', 'I':
|
||||
if strings.ToLower(s) == "inf" {
|
||||
return cmplx.Inf(), nil
|
||||
}
|
||||
}
|
||||
|
||||
var q complex128
|
||||
var parts byte
|
||||
for i := 0; i < 4; i++ {
|
||||
beg, end, p, err := floatPart(s)
|
||||
if err != nil {
|
||||
return q, parseError{string: orig, state: -1}
|
||||
}
|
||||
if parts&(1<<p) != 0 {
|
||||
return q, parseError{string: orig, state: -1}
|
||||
}
|
||||
parts |= 1 << p
|
||||
var v float64
|
||||
switch s[:end] {
|
||||
case "-":
|
||||
if len(s[end:]) == 0 {
|
||||
return q, parseError{string: orig, state: -1}
|
||||
}
|
||||
v = -1
|
||||
case "+":
|
||||
if len(s[end:]) == 0 {
|
||||
return q, parseError{string: orig, state: -1}
|
||||
}
|
||||
v = 1
|
||||
default:
|
||||
v, err = strconv.ParseFloat(s[beg:end], 64)
|
||||
if err != nil {
|
||||
return q, err
|
||||
}
|
||||
}
|
||||
s = s[end:]
|
||||
switch p {
|
||||
case 0:
|
||||
q += complex(v, 0)
|
||||
case 1:
|
||||
q += complex(0, v)
|
||||
s = s[1:]
|
||||
}
|
||||
if len(s) == 0 {
|
||||
return q, nil
|
||||
}
|
||||
if !isSign(rune(s[0])) {
|
||||
return q, parseError{string: orig, state: -1}
|
||||
}
|
||||
}
|
||||
|
||||
return q, parseError{string: orig, state: -1}
|
||||
}
|
||||
|
||||
func floatPart(s string) (beg, end int, part uint, err error) {
|
||||
const (
|
||||
wantMantSign = iota
|
||||
wantMantIntInit
|
||||
wantMantInt
|
||||
wantMantFrac
|
||||
wantExpSign
|
||||
wantExpInt
|
||||
|
||||
wantInfN
|
||||
wantInfF
|
||||
wantCloseInf
|
||||
|
||||
wantNaNA
|
||||
wantNaNN
|
||||
wantCloseNaN
|
||||
)
|
||||
var i, state int
|
||||
var r rune
|
||||
for i, r = range s {
|
||||
switch state {
|
||||
case wantMantSign:
|
||||
switch {
|
||||
default:
|
||||
return 0, i, 0, parseError{string: s, state: state, rune: r}
|
||||
case isSign(r):
|
||||
state = wantMantIntInit
|
||||
case isDigit(r):
|
||||
state = wantMantInt
|
||||
case isDot(r):
|
||||
state = wantMantFrac
|
||||
case r == 'i', r == 'I':
|
||||
state = wantInfN
|
||||
case r == 'n', r == 'N':
|
||||
state = wantNaNA
|
||||
}
|
||||
|
||||
case wantMantIntInit:
|
||||
switch {
|
||||
default:
|
||||
return 0, i, 0, parseError{string: s, state: state, rune: r}
|
||||
case isDigit(r):
|
||||
state = wantMantInt
|
||||
case isDot(r):
|
||||
state = wantMantFrac
|
||||
case r == 'i':
|
||||
// We need to sneak a look-ahead here.
|
||||
if i == len(s)-1 || s[i+1] == '-' || s[i+1] == '+' {
|
||||
return 0, i, 1, nil
|
||||
}
|
||||
fallthrough
|
||||
case r == 'I':
|
||||
state = wantInfN
|
||||
case r == 'n', r == 'N':
|
||||
state = wantNaNA
|
||||
}
|
||||
|
||||
case wantMantInt:
|
||||
switch {
|
||||
default:
|
||||
return 0, i, 0, parseError{string: s, state: state, rune: r}
|
||||
case isDigit(r):
|
||||
// Do nothing
|
||||
case isDot(r):
|
||||
state = wantMantFrac
|
||||
case isExponent(r):
|
||||
state = wantExpSign
|
||||
case isSign(r):
|
||||
return 0, i, 0, nil
|
||||
case r == 'i':
|
||||
return 0, i, 1, nil
|
||||
}
|
||||
|
||||
case wantMantFrac:
|
||||
switch {
|
||||
default:
|
||||
return 0, i, 0, parseError{string: s, state: state, rune: r}
|
||||
case isDigit(r):
|
||||
// Do nothing
|
||||
case isExponent(r):
|
||||
state = wantExpSign
|
||||
case isSign(r):
|
||||
return 0, i, 0, nil
|
||||
case r == 'i':
|
||||
return 0, i, 1, nil
|
||||
}
|
||||
|
||||
case wantExpSign:
|
||||
switch {
|
||||
default:
|
||||
return 0, i, 0, parseError{string: s, state: state, rune: r}
|
||||
case isSign(r) || isDigit(r):
|
||||
state = wantExpInt
|
||||
}
|
||||
|
||||
case wantExpInt:
|
||||
switch {
|
||||
default:
|
||||
return 0, i, 0, parseError{string: s, state: state, rune: r}
|
||||
case isDigit(r):
|
||||
// Do nothing
|
||||
case isSign(r):
|
||||
return 0, i, 0, nil
|
||||
case r == 'i':
|
||||
return 0, i, 1, nil
|
||||
}
|
||||
|
||||
case wantInfN:
|
||||
if r != 'n' && r != 'N' {
|
||||
return 0, i, 0, parseError{string: s, state: state, rune: r}
|
||||
}
|
||||
state = wantInfF
|
||||
case wantInfF:
|
||||
if r != 'f' && r != 'F' {
|
||||
return 0, i, 0, parseError{string: s, state: state, rune: r}
|
||||
}
|
||||
state = wantCloseInf
|
||||
case wantCloseInf:
|
||||
switch {
|
||||
default:
|
||||
return 0, i, 0, parseError{string: s, state: state, rune: r}
|
||||
case isSign(r):
|
||||
return 0, i, 0, nil
|
||||
case r == 'i':
|
||||
return 0, i, 1, nil
|
||||
}
|
||||
|
||||
case wantNaNA:
|
||||
if r != 'a' && r != 'A' {
|
||||
return 0, i, 0, parseError{string: s, state: state, rune: r}
|
||||
}
|
||||
state = wantNaNN
|
||||
case wantNaNN:
|
||||
if r != 'n' && r != 'N' {
|
||||
return 0, i, 0, parseError{string: s, state: state, rune: r}
|
||||
}
|
||||
state = wantCloseNaN
|
||||
case wantCloseNaN:
|
||||
if isSign(rune(s[0])) {
|
||||
beg = 1
|
||||
}
|
||||
switch {
|
||||
default:
|
||||
return beg, i, 0, parseError{string: s, state: state, rune: r}
|
||||
case isSign(r):
|
||||
return beg, i, 0, nil
|
||||
case r == 'i':
|
||||
return beg, i, 1, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
switch state {
|
||||
case wantMantSign, wantExpSign, wantExpInt:
|
||||
if state == wantExpInt && isDigit(r) {
|
||||
break
|
||||
}
|
||||
return 0, i, 0, parseError{string: s, state: state, rune: r}
|
||||
}
|
||||
return 0, len(s), 0, nil
|
||||
}
|
||||
|
||||
func isSign(r rune) bool {
|
||||
return r == '+' || r == '-'
|
||||
}
|
||||
|
||||
func isDigit(r rune) bool {
|
||||
return '0' <= r && r <= '9'
|
||||
}
|
||||
|
||||
func isExponent(r rune) bool {
|
||||
return r == 'e' || r == 'E'
|
||||
}
|
||||
|
||||
func isDot(r rune) bool {
|
||||
return r == '.'
|
||||
}
|
||||
|
||||
type parseError struct {
|
||||
string string
|
||||
state int
|
||||
rune rune
|
||||
}
|
||||
|
||||
func (e parseError) Error() string {
|
||||
if e.state < 0 {
|
||||
return fmt.Sprintf("quat: failed to parse: %q", e.string)
|
||||
}
|
||||
return fmt.Sprintf("quat: failed to parse in state %d with %q: %q", e.state, e.rune, e.string)
|
||||
}
|
||||
Reference in New Issue
Block a user