1// Copyright 2020 The Go 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.
4
5package strconv_test
6
7import (
8	"math"
9	"math/cmplx"
10	"reflect"
11	. "strconv"
12	"testing"
13)
14
15var (
16	infp0 = complex(math.Inf(+1), 0)
17	infm0 = complex(math.Inf(-1), 0)
18	inf0p = complex(0, math.Inf(+1))
19	inf0m = complex(0, math.Inf(-1))
20
21	infpp = complex(math.Inf(+1), math.Inf(+1))
22	infpm = complex(math.Inf(+1), math.Inf(-1))
23	infmp = complex(math.Inf(-1), math.Inf(+1))
24	infmm = complex(math.Inf(-1), math.Inf(-1))
25)
26
27type atocTest struct {
28	in  string
29	out complex128
30	err error
31}
32
33func TestParseComplex(t *testing.T) {
34	tests := []atocTest{
35		// Clearly invalid
36		{"", 0, ErrSyntax},
37		{" ", 0, ErrSyntax},
38		{"(", 0, ErrSyntax},
39		{")", 0, ErrSyntax},
40		{"i", 0, ErrSyntax},
41		{"+i", 0, ErrSyntax},
42		{"-i", 0, ErrSyntax},
43		{"1I", 0, ErrSyntax},
44		{"10  + 5i", 0, ErrSyntax},
45		{"3+", 0, ErrSyntax},
46		{"3+5", 0, ErrSyntax},
47		{"3+5+5i", 0, ErrSyntax},
48
49		// Parentheses
50		{"()", 0, ErrSyntax},
51		{"(i)", 0, ErrSyntax},
52		{"(0)", 0, nil},
53		{"(1i)", 1i, nil},
54		{"(3.0+5.5i)", 3.0 + 5.5i, nil},
55		{"(1)+1i", 0, ErrSyntax},
56		{"(3.0+5.5i", 0, ErrSyntax},
57		{"3.0+5.5i)", 0, ErrSyntax},
58
59		// NaNs
60		{"NaN", complex(math.NaN(), 0), nil},
61		{"NANi", complex(0, math.NaN()), nil},
62		{"nan+nAni", complex(math.NaN(), math.NaN()), nil},
63		{"+NaN", 0, ErrSyntax},
64		{"-NaN", 0, ErrSyntax},
65		{"NaN-NaNi", 0, ErrSyntax},
66
67		// Infs
68		{"Inf", infp0, nil},
69		{"+inf", infp0, nil},
70		{"-inf", infm0, nil},
71		{"Infinity", infp0, nil},
72		{"+INFINITY", infp0, nil},
73		{"-infinity", infm0, nil},
74		{"+infi", inf0p, nil},
75		{"0-infinityi", inf0m, nil},
76		{"Inf+Infi", infpp, nil},
77		{"+Inf-Infi", infpm, nil},
78		{"-Infinity+Infi", infmp, nil},
79		{"inf-inf", 0, ErrSyntax},
80
81		// Zeros
82		{"0", 0, nil},
83		{"0i", 0, nil},
84		{"-0.0i", 0, nil},
85		{"0+0.0i", 0, nil},
86		{"0e+0i", 0, nil},
87		{"0e-0+0i", 0, nil},
88		{"-0.0-0.0i", 0, nil},
89		{"0e+012345", 0, nil},
90		{"0x0p+012345i", 0, nil},
91		{"0x0.00p-012345i", 0, nil},
92		{"+0e-0+0e-0i", 0, nil},
93		{"0e+0+0e+0i", 0, nil},
94		{"-0e+0-0e+0i", 0, nil},
95
96		// Regular non-zeroes
97		{"0.1", 0.1, nil},
98		{"0.1i", 0 + 0.1i, nil},
99		{"0.123", 0.123, nil},
100		{"0.123i", 0 + 0.123i, nil},
101		{"0.123+0.123i", 0.123 + 0.123i, nil},
102		{"99", 99, nil},
103		{"+99", 99, nil},
104		{"-99", -99, nil},
105		{"+1i", 1i, nil},
106		{"-1i", -1i, nil},
107		{"+3+1i", 3 + 1i, nil},
108		{"30+3i", 30 + 3i, nil},
109		{"+3e+3-3e+3i", 3e+3 - 3e+3i, nil},
110		{"+3e+3+3e+3i", 3e+3 + 3e+3i, nil},
111		{"+3e+3+3e+3i+", 0, ErrSyntax},
112
113		// Separators
114		{"0.1", 0.1, nil},
115		{"0.1i", 0 + 0.1i, nil},
116		{"0.1_2_3", 0.123, nil},
117		{"+0x_3p3i", 0x3p3i, nil},
118		{"0_0+0x_0p0i", 0, nil},
119		{"0x_10.3p-8+0x3p3i", 0x10.3p-8 + 0x3p3i, nil},
120		{"+0x_1_0.3p-8+0x_3_0p3i", 0x10.3p-8 + 0x30p3i, nil},
121		{"0x1_0.3p+8-0x_3p3i", 0x10.3p+8 - 0x3p3i, nil},
122
123		// Hexadecimals
124		{"0x10.3p-8+0x3p3i", 0x10.3p-8 + 0x3p3i, nil},
125		{"+0x10.3p-8+0x3p3i", 0x10.3p-8 + 0x3p3i, nil},
126		{"0x10.3p+8-0x3p3i", 0x10.3p+8 - 0x3p3i, nil},
127		{"0x1p0", 1, nil},
128		{"0x1p1", 2, nil},
129		{"0x1p-1", 0.5, nil},
130		{"0x1ep-1", 15, nil},
131		{"-0x1ep-1", -15, nil},
132		{"-0x2p3", -16, nil},
133		{"0x1e2", 0, ErrSyntax},
134		{"1p2", 0, ErrSyntax},
135		{"0x1e2i", 0, ErrSyntax},
136
137		// ErrRange
138		// next float64 - too large
139		{"+0x1p1024", infp0, ErrRange},
140		{"-0x1p1024", infm0, ErrRange},
141		{"+0x1p1024i", inf0p, ErrRange},
142		{"-0x1p1024i", inf0m, ErrRange},
143		{"+0x1p1024+0x1p1024i", infpp, ErrRange},
144		{"+0x1p1024-0x1p1024i", infpm, ErrRange},
145		{"-0x1p1024+0x1p1024i", infmp, ErrRange},
146		{"-0x1p1024-0x1p1024i", infmm, ErrRange},
147		// the border is ...158079
148		// borderline - okay
149		{"+0x1.fffffffffffff7fffp1023+0x1.fffffffffffff7fffp1023i", 1.7976931348623157e+308 + 1.7976931348623157e+308i, nil},
150		{"+0x1.fffffffffffff7fffp1023-0x1.fffffffffffff7fffp1023i", 1.7976931348623157e+308 - 1.7976931348623157e+308i, nil},
151		{"-0x1.fffffffffffff7fffp1023+0x1.fffffffffffff7fffp1023i", -1.7976931348623157e+308 + 1.7976931348623157e+308i, nil},
152		{"-0x1.fffffffffffff7fffp1023-0x1.fffffffffffff7fffp1023i", -1.7976931348623157e+308 - 1.7976931348623157e+308i, nil},
153		// borderline - too large
154		{"+0x1.fffffffffffff8p1023", infp0, ErrRange},
155		{"-0x1fffffffffffff.8p+971", infm0, ErrRange},
156		{"+0x1.fffffffffffff8p1023i", inf0p, ErrRange},
157		{"-0x1fffffffffffff.8p+971i", inf0m, ErrRange},
158		{"+0x1.fffffffffffff8p1023+0x1.fffffffffffff8p1023i", infpp, ErrRange},
159		{"+0x1.fffffffffffff8p1023-0x1.fffffffffffff8p1023i", infpm, ErrRange},
160		{"-0x1fffffffffffff.8p+971+0x1fffffffffffff.8p+971i", infmp, ErrRange},
161		{"-0x1fffffffffffff8p+967-0x1fffffffffffff8p+967i", infmm, ErrRange},
162		// a little too large
163		{"1e308+1e308i", 1e+308 + 1e+308i, nil},
164		{"2e308+2e308i", infpp, ErrRange},
165		{"1e309+1e309i", infpp, ErrRange},
166		{"0x1p1025+0x1p1025i", infpp, ErrRange},
167		{"2e308", infp0, ErrRange},
168		{"1e309", infp0, ErrRange},
169		{"0x1p1025", infp0, ErrRange},
170		{"2e308i", inf0p, ErrRange},
171		{"1e309i", inf0p, ErrRange},
172		{"0x1p1025i", inf0p, ErrRange},
173		// way too large
174		{"+1e310+1e310i", infpp, ErrRange},
175		{"+1e310-1e310i", infpm, ErrRange},
176		{"-1e310+1e310i", infmp, ErrRange},
177		{"-1e310-1e310i", infmm, ErrRange},
178		// under/overflow exponent
179		{"1e-4294967296", 0, nil},
180		{"1e-4294967296i", 0, nil},
181		{"1e-4294967296+1i", 1i, nil},
182		{"1+1e-4294967296i", 1, nil},
183		{"1e-4294967296+1e-4294967296i", 0, nil},
184		{"1e+4294967296", infp0, ErrRange},
185		{"1e+4294967296i", inf0p, ErrRange},
186		{"1e+4294967296+1e+4294967296i", infpp, ErrRange},
187		{"1e+4294967296-1e+4294967296i", infpm, ErrRange},
188	}
189	for i := range tests {
190		test := &tests[i]
191		if test.err != nil {
192			test.err = &NumError{Func: "ParseComplex", Num: test.in, Err: test.err}
193		}
194		got, err := ParseComplex(test.in, 128)
195		if !reflect.DeepEqual(err, test.err) {
196			t.Fatalf("ParseComplex(%q, 128) = %v, %v; want %v, %v", test.in, got, err, test.out, test.err)
197		}
198		if !(cmplx.IsNaN(test.out) && cmplx.IsNaN(got)) && got != test.out {
199			t.Fatalf("ParseComplex(%q, 128) = %v, %v; want %v, %v", test.in, got, err, test.out, test.err)
200		}
201
202		if complex128(complex64(test.out)) == test.out {
203			got, err := ParseComplex(test.in, 64)
204			if !reflect.DeepEqual(err, test.err) {
205				t.Fatalf("ParseComplex(%q, 64) = %v, %v; want %v, %v", test.in, got, err, test.out, test.err)
206			}
207			got64 := complex64(got)
208			if complex128(got64) != test.out {
209				t.Fatalf("ParseComplex(%q, 64) = %v, %v; want %v, %v", test.in, got, err, test.out, test.err)
210			}
211		}
212	}
213}
214
215// Issue 42297: allow ParseComplex(s, not_32_or_64) for legacy reasons
216func TestParseComplexIncorrectBitSize(t *testing.T) {
217	const s = "1.5e308+1.0e307i"
218	const want = 1.5e308 + 1.0e307i
219
220	for _, bitSize := range []int{0, 10, 100, 256} {
221		c, err := ParseComplex(s, bitSize)
222		if err != nil {
223			t.Fatalf("ParseComplex(%q, %d) gave error %s", s, bitSize, err)
224		}
225		if c != want {
226			t.Fatalf("ParseComplex(%q, %d) = %g (expected %g)", s, bitSize, c, want)
227		}
228	}
229}
230