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"
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 int, 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 = int(internal.Parent[langIndex])
55			} else {
56				// The index points to a list of symbol data indexes.
57				for _, e := range langToAlt[p&^hasNonLatnMask:] {
58					if int(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							// http://unicode.org/reports/tr35/#Locale_Inheritance.
71							ns = numLatn
72							langIndex = compactIndex
73							continue outerLoop
74						}
75						// Fall back to parent.
76						langIndex = int(internal.Parent[langIndex])
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	for {
104		if index, ok := language.CompactIndex(t); ok {
105			return InfoFromLangID(index, t.TypeForKey("nu"))
106		}
107		t = t.Parent()
108	}
109}
110
111// IsDecimal reports if the numbering system can convert decimal to native
112// symbols one-to-one.
113func (n Info) IsDecimal() bool {
114	return int(n.system.id) < len(numSysData)
115}
116
117// WriteDigit writes the UTF-8 sequence for n corresponding to the given ASCII
118// digit to dst and reports the number of bytes written. dst must be large
119// enough to hold the rune (can be up to utf8.UTFMax bytes).
120func (n Info) WriteDigit(dst []byte, asciiDigit rune) int {
121	copy(dst, n.system.zero[:n.system.digitSize])
122	dst[n.system.digitSize-1] += byte(asciiDigit - '0')
123	return int(n.system.digitSize)
124}
125
126// AppendDigit appends the UTF-8 sequence for n corresponding to the given digit
127// to dst and reports the number of bytes written. dst must be large enough to
128// hold the rune (can be up to utf8.UTFMax bytes).
129func (n Info) AppendDigit(dst []byte, digit byte) []byte {
130	dst = append(dst, n.system.zero[:n.system.digitSize]...)
131	dst[len(dst)-1] += digit
132	return dst
133}
134
135// Digit returns the digit for the numbering system for the corresponding ASCII
136// value. For example, ni.Digit('3') could return '三'. Note that the argument
137// is the rune constant '3', which equals 51, not the integer constant 3.
138func (n Info) Digit(asciiDigit rune) rune {
139	var x [utf8.UTFMax]byte
140	n.WriteDigit(x[:], asciiDigit)
141	r, _ := utf8.DecodeRune(x[:])
142	return r
143}
144
145// Symbol returns the string for the given symbol type.
146func (n Info) Symbol(t SymbolType) string {
147	return symData.Elem(int(symIndex[n.symIndex][t]))
148}
149
150func formatForLang(t language.Tag, index []byte) *Pattern {
151	for ; ; t = t.Parent() {
152		if x, ok := language.CompactIndex(t); ok {
153			return &formats[index[x]]
154		}
155	}
156}
157