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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
//! Changing the case of a string, which is six questions rather than one.
//!
//! `upper` and `lower` are the two that look easy, and even those are not a
//! per-character mapping: `'ß'.upper()` is two characters and `'Σ'.lower()` is
//! one of two characters depending on what is around it. The other four are
//! each different again. `title` has to know where a word starts, `capitalize`
//! uses a third mapping that is neither of the first two, `swapcase` picks per
//! character, and `casefold` is a mapping of its own that disagrees with
//! `lower` on a few hundred code points.
//!
//! The data all of that needs is in the `table` module next door, generated
//! from the CPython whose answers we are matching. Rust's standard library has
//! some of it and is not used, because it carries its own copy of the Unicode
//! data and the two are not always the same release.
//!
//! ## The final sigma
//!
//! Greek writes a lowercase sigma as `ς` at the end of a word and `σ`
//! everywhere else, and the uppercase is `Σ` either way, so lowercasing a
//! sigma is the one decision here that is not a fact about the code point. The
//! rule is that the final form is used when there is a cased character before
//! the sigma and none after it, looking past case-ignorable characters in both
//! directions.
//!
//! It matters that the scan reads the original string and not the output. In
//! `'ΑΣΣ'` the first sigma is followed by a cased character and the second is
//! not, so the answer is `'ασς'`, and a runtime that lowercased left to right
//! while looking at what it had already written would get the first one wrong.
//! Four of the six methods lowercase something, and all four take their
//! context from the input.
use ;
use crateamong;
/// `Σ`, the only code point whose lowercase depends on where it is.
const CAPITAL_SIGMA: u32 = 0x03A3;
/// `ς`, the form used at the end of a word.
const FINAL_SIGMA: u32 = 0x03C2;
/// `σ`, the form used everywhere else.
const SMALL_SIGMA: u32 = 0x03C3;
/// `str.upper`.
///
/// Context free, unlike its opposite, so this is the whole of it.
/// `str.lower`.
/// `str.casefold`, which is for comparing rather than for displaying.
///
/// It has no final sigma rule, and does not want one: the point of folding is
/// that `'ΑΣ'` and `'Ας'` come out the same, which they do because both fold
/// to `'ασ'`. Lowercasing them would keep them apart.
/// `str.swapcase`.
///
/// Not `upper` and `lower` applied to alternate halves of the alphabet. The
/// test is per character and is the `Uppercase` and `Lowercase` properties, so
/// a titlecase character such as `Dž` is neither and is left where it is.
/// `str.title`.
///
/// A word starts after anything that is not cased, and the character that
/// starts one gets the titlecase mapping rather than the uppercase one. Those
/// differ for the digraphs: `'dž'.title()` is `'Dž'` and `'dž'.upper()` is `'DŽ'`.
///
/// The predicate for carrying on a word is `Cased` and emphatically not
/// `isalpha`, which disagrees with it in both directions. `'あa'.title()` is
/// `'あA'` because hiragana is alphabetic and not cased, and `'ⅰa'.title()` is
/// `'Ⅰa'` because a lowercase roman numeral is cased and not alphabetic.
/// `str.capitalize`, which is `title` that stops looking for words after the
/// first character.
///
/// The first character gets the titlecase mapping, which is worth saying
/// because the name suggests the uppercase one and they are not the same:
/// `'dža'.capitalize()` is `'Dža'` and not `'DŽa'`.
/// Lowercase the code point at `at`, which needs the whole string for the one
/// case where the answer depends on it.
/// Whether the sigma at `at` is at the end of a word.
///
/// Something cased before it and nothing cased after it, looking past
/// case-ignorable characters on both sides. Running off the front counts as
/// nothing cased before, and running off the end counts as nothing cased
/// after, which is why a sigma on its own is `'σ'` and `'ας'` ends in the
/// final form.
/// Whether the first code point in this direction that the rule does not look
/// past is a cased one.
///
/// The two tests are in this order and not folded together, because a code
/// point can be both: a modifier letter is cased and is still looked past.
/// Write what `table` says about `cp`, or `cp` itself if it says nothing.
/// Whether `cp` is lowercase, which is the `Lowercase` property and so is
/// wider than the `Ll` category: a modifier letter such as `ʰ` is in it.
/// Whether `cp` is uppercase.
/// Whether `cp` is titlecase, which is neither of the other two.
///
/// Thirty one code points, being the three Latin digraphs and the Greek
/// letters with an iota subscript. `Dž` is the one in the middle of `DŽ` and
/// `dž`, and `'Dž'.swapcase()` is itself because it is neither upper nor lower.
/// Whether `cp` is cased at all, which is what decides where `title` sees a
/// word start and is wider than the three above put together.