1// Copyright 2016 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
5//go:generate go run gen.go gen_common.go
6
7// Package number contains tools and data for formatting numbers.
8package number
9
10import (
11	"unicode/utf8"
12
13	"golang.org/x/text/internal/language/compact"
14	"golang.org/x/text/language"
15)
16
17// Info holds number formatting configuration data.
18type Info struct {
19	system   systemData // numbering system information
20	symIndex symOffset  // index to symbols
21}
22
23// InfoFromLangID returns a Info for the given compact language identifier and
24// numbering system identifier. If system is the empty string, the default
25// numbering system will be taken for that language.
26func InfoFromLangID(compactIndex compact.ID, numberSystem string) Info {
27	p := langToDefaults[compactIndex]
28	// Lookup the entry for the language.
29	pSymIndex := symOffset(0) // Default: Latin, default symbols
30	system, ok := systemMap[numberSystem]
31	if !ok {
32		// Take the value for the default numbering system. This is by far the
33		// most common case as an alternative numbering system is hardly used.
34		if p&hasNonLatnMask == 0 { // Latn digits.
35			pSymIndex = p
36		} else { // Non-Latn or multiple numbering systems.
37			// Take the first entry from the alternatives list.
38			data := langToAlt[p&^hasNonLatnMask]
39			pSymIndex = data.symIndex
40			system = data.system
41		}
42	} else {
43		langIndex := compactIndex
44		ns := system
45	outerLoop:
46		for ; ; p = langToDefaults[langIndex] {
47			if p&hasNonLatnMask == 0 {
48				if ns == 0 {
49					// The index directly points to the symbol data.
50					pSymIndex = p
51					break
52				}
53				// Move to the parent and retry.
54				langIndex = langIndex.Parent()
55			} else {
56				// The index points to a list of symbol data indexes.
57				for _, e := range langToAlt[p&^hasNonLatnMask:] {
58					if e.compactTag != langIndex {
59						if langIndex == 0 {
60							// The CLDR root defines full symbol information for
61							// all numbering systems (even though mostly by
62							// means of aliases). Fall back to the default entry
63							// for Latn if there is no data for the numbering
64							// system of this language.
65							if ns == 0 {
66								break
67							}
68							// Fall back to Latin and start from the original
69							// language. See
70							// https://unicode.org/reports/tr35/#Locale_Inheritance.
71							ns = numLatn
72							langIndex = compactIndex
73							continue outerLoop
74						}
75						// Fall back to parent.
76						langIndex = langIndex.Parent()
77					} else if e.system == ns {
78						pSymIndex = e.symIndex
79						break outerLoop
80					}
81				}
82			}
83		}
84	}
85	if int(system) >= len(numSysData) { // algorithmic
86		// Will generate ASCII digits in case the user inadvertently calls
87		// WriteDigit or Digit on it.
88		d := numSysData[0]
89		d.id = system
90		return Info{
91			system:   d,
92			symIndex: pSymIndex,
93		}
94	}
95	return Info{
96		system:   numSysData[system],
97		symIndex: pSymIndex,
98	}
99}
100
101// InfoFromTag returns a Info for the given language tag.
102func InfoFromTag(t language.Tag) Info {
103	return InfoFromLangID(tagToID(t), t.TypeForKey("nu"))
104}
105
106// IsDecimal reports if the numbering system can convert decimal to native
107// symbols one-to-one.
108func (n Info) IsDecimal() bool {
109	return int(n.system.id) < len(numSysData)
110}
111
112// WriteDigit writes the UTF-8 sequence for n corresponding to the given ASCII
113// digit to dst and reports the number of bytes written. dst must be large
114// enough to hold the rune (can be up to utf8.UTFMax bytes).
115func (n Info) WriteDigit(dst []byte, asciiDigit rune) int {
116	copy(dst, n.system.zero[:n.system.digitSize])
117	dst[n.system.digitSize-1] += byte(asciiDigit - '0')
118	return int(n.system.digitSize)
119}
120
121// AppendDigit appends the UTF-8 sequence for n corresponding to the given digit
122// to dst and reports the number of bytes written. dst must be large enough to
123// hold the rune (can be up to utf8.UTFMax bytes).
124func (n Info) AppendDigit(dst []byte, digit byte) []byte {
125	dst = append(dst, n.system.zero[:n.system.digitSize]...)
126	dst[len(dst)-1] += digit
127	return dst
128}
129
130// Digit returns the digit for the numbering system for the corresponding ASCII
131// value. For example, ni.Digit('3') could return '三'. Note that the argument
132// is the rune constant '3', which equals 51, not the integer constant 3.
133func (n Info) Digit(asciiDigit rune) rune {
134	var x [utf8.UTFMax]byte
135	n.WriteDigit(x[:], asciiDigit)
136	r, _ := utf8.DecodeRune(x[:])
137	return r
138}
139
140// Symbol returns the string for the given symbol type.
141func (n Info) Symbol(t SymbolType) string {
142	return symData.Elem(int(symIndex[n.symIndex][t]))
143}
144
145func formatForLang(t language.Tag, index []byte) *Pattern {
146	return &formats[index[tagToID(t)]]
147}
148
149func tagToID(t language.Tag) compact.ID {
150	id, _ := compact.RegionalID(compact.Tag(t))
151	return id
152}
153