fckeuspy-go/vendor/github.com/nicksnyder/go-i18n/v2/internal/plural/operands.go

193 lines
4.8 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package plural
import (
"fmt"
"strconv"
"strings"
)
// Operands is a representation of http://unicode.org/reports/tr35/tr35-numbers.html#Operands
// If there is a compact decimal exponent value C, then the N, I, V, W, F, and T values are computed after shifting the decimal point in the original by the c value.
// So for 1.2c3, the values are the same as those of 1200: i=1200 and f=0.
// Similarly, 1.2005c3 has i=1200 and f=5 (corresponding to 1200.5).
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
C int64 // compact decimal exponent value: exponent of the power of 10 used in compact decimal formatting.
}
// NEqualsAny 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
}
// NModEqualsAny 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
}
// NInRange 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
}
// NewOperands returns the operands for number.
func NewOperands(number interface{}) (*Operands, error) {
switch number := number.(type) {
case int:
return newOperandsInt64(int64(number)), nil
case int8:
return newOperandsInt64(int64(number)), nil
case int16:
return newOperandsInt64(int64(number)), nil
case int32:
return newOperandsInt64(int64(number)), nil
case int64:
return newOperandsInt64(number), nil
case string:
return newOperandsString(number)
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", number)
}
}
func newOperandsInt64(i int64) *Operands {
if i < 0 {
i = -i
}
return &Operands{float64(i), i, 0, 0, 0, 0, 0}
}
func splitSignificandExponent(s string) (significand, exponent string) {
i := strings.IndexAny(s, "eE")
if i < 0 {
return s, ""
}
return s[:i], s[i+1:]
}
func shiftDecimalLeft(s string, n int) string {
if n <= 0 {
return s
}
i := strings.IndexRune(s, '.')
tilt := 0
if i < 0 {
i = len(s)
tilt = -1
}
switch {
case n == i:
return "0." + s[:i] + s[i+1+tilt:]
case n > i:
return "0." + strings.Repeat("0", n-i) + s[:i] + s[i+1+tilt:]
default:
return s[:i-n] + "." + s[i-n:i] + s[i+1+tilt:]
}
}
func shiftDecimalRight(s string, n int) string {
if n <= 0 {
return s
}
i := strings.IndexRune(s, '.')
if i < 0 {
return s + strings.Repeat("0", n)
}
switch rest := len(s) - i - 1; {
case n == rest:
return s[:i] + s[i+1:]
case n > rest:
return s[:i] + s[i+1:] + strings.Repeat("0", n-rest)
default:
return s[:i] + s[i+1:i+1+n] + "." + s[i+1+n:]
}
}
func applyExponent(s string, exponent int) string {
switch {
case exponent > 0:
return shiftDecimalRight(s, exponent)
case exponent < 0:
return shiftDecimalLeft(s, -exponent)
}
return s
}
func newOperandsString(s string) (*Operands, error) {
if s[0] == '-' {
s = s[1:]
}
ops := &Operands{}
var err error
ops.N, err = strconv.ParseFloat(s, 64)
if err != nil {
return nil, err
}
significand, exponent := splitSignificandExponent(s)
if exponent != "" {
// We are storing C as an int64 but only allowing
// numbers that fit into the bitsize of an int
// so C is safe to cast as a int later.
ops.C, err = strconv.ParseInt(exponent, 10, 0)
if err != nil {
return nil, err
}
}
value := applyExponent(significand, int(ops.C))
parts := strings.SplitN(value, ".", 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
}