summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/nicksnyder/go-i18n/i18n/language/operands.go
blob: 877bcc89ddfa3b1eac572ae826b8f34ef8809d45 (plain) (blame)
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
package language

import (
	"fmt"
	"strconv"
	"strings"
)

// http://unicode.org/reports/tr35/tr35-numbers.html#Operands
type operands struct {
	N float64 // absolute value of the source number (integer and decimals)
	I int64   // integer digits of n
	V int64   // number of visible fraction digits in n, with trailing zeros
	W int64   // number of visible fraction digits in n, without trailing zeros
	F int64   // visible fractional digits in n, with trailing zeros
	T int64   // visible fractional digits in n, without trailing zeros
}

// NmodEqualAny returns true if o represents an integer equal to any of the arguments.
func (o *operands) NequalsAny(any ...int64) bool {
	for _, i := range any {
		if o.I == i && o.T == 0 {
			return true
		}
	}
	return false
}

// NmodEqualAny returns true if o represents an integer equal to any of the arguments modulo mod.
func (o *operands) NmodEqualsAny(mod int64, any ...int64) bool {
	modI := o.I % mod
	for _, i := range any {
		if modI == i && o.T == 0 {
			return true
		}
	}
	return false
}

// NmodInRange returns true if o represents an integer in the closed interval [from, to].
func (o *operands) NinRange(from, to int64) bool {
	return o.T == 0 && from <= o.I && o.I <= to
}

// NmodInRange returns true if o represents an integer in the closed interval [from, to] modulo mod.
func (o *operands) NmodInRange(mod, from, to int64) bool {
	modI := o.I % mod
	return o.T == 0 && from <= modI && modI <= to
}

func newOperands(v interface{}) (*operands, error) {
	switch v := v.(type) {
	case int:
		return newOperandsInt64(int64(v)), nil
	case int8:
		return newOperandsInt64(int64(v)), nil
	case int16:
		return newOperandsInt64(int64(v)), nil
	case int32:
		return newOperandsInt64(int64(v)), nil
	case int64:
		return newOperandsInt64(v), nil
	case string:
		return newOperandsString(v)
	case float32, float64:
		return nil, fmt.Errorf("floats should be formatted into a string")
	default:
		return nil, fmt.Errorf("invalid type %T; expected integer or string", v)
	}
}

func newOperandsInt64(i int64) *operands {
	if i < 0 {
		i = -i
	}
	return &operands{float64(i), i, 0, 0, 0, 0}
}

func newOperandsString(s string) (*operands, error) {
	if s[0] == '-' {
		s = s[1:]
	}
	n, err := strconv.ParseFloat(s, 64)
	if err != nil {
		return nil, err
	}
	ops := &operands{N: n}
	parts := strings.SplitN(s, ".", 2)
	ops.I, err = strconv.ParseInt(parts[0], 10, 64)
	if err != nil {
		return nil, err
	}
	if len(parts) == 1 {
		return ops, nil
	}
	fraction := parts[1]
	ops.V = int64(len(fraction))
	for i := ops.V - 1; i >= 0; i-- {
		if fraction[i] != '0' {
			ops.W = i + 1
			break
		}
	}
	if ops.V > 0 {
		f, err := strconv.ParseInt(fraction, 10, 0)
		if err != nil {
			return nil, err
		}
		ops.F = f
	}
	if ops.W > 0 {
		t, err := strconv.ParseInt(fraction[:ops.W], 10, 0)
		if err != nil {
			return nil, err
		}
		ops.T = t
	}
	return ops, nil
}