1use crate::data::datatable::DataValue;
2use crate::sql::functions::{ArgCount, FunctionCategory, FunctionSignature, SqlFunction};
3use anyhow::Result;
4
5pub struct ToWords;
7
8impl SqlFunction for ToWords {
9 fn signature(&self) -> FunctionSignature {
10 FunctionSignature {
11 name: "TO_WORDS",
12 category: FunctionCategory::String,
13 arg_count: ArgCount::Fixed(1),
14 description: "Convert number to English words",
15 returns: "String with number spelled out in words",
16 examples: vec![
17 "SELECT TO_WORDS(42) -- Returns 'forty-two'",
18 "SELECT TO_WORDS(999) -- Returns 'nine hundred ninety-nine'",
19 "SELECT TO_WORDS(1234) -- Returns 'one thousand two hundred thirty-four'",
20 "SELECT TO_WORDS(1000000) -- Returns 'one million'",
21 ],
22 }
23 }
24
25 fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
26 if args.len() != 1 {
27 return Ok(DataValue::Null);
28 }
29
30 let num = match &args[0] {
31 DataValue::Float(n) => *n as i64,
32 DataValue::Integer(n) => *n,
33 DataValue::Null => return Ok(DataValue::Null),
34 _ => return Ok(DataValue::Null),
35 };
36
37 if num < 0 {
38 Ok(DataValue::String(format!(
39 "negative {}",
40 number_to_words(-num)
41 )))
42 } else {
43 Ok(DataValue::String(number_to_words(num)))
44 }
45 }
46}
47
48pub struct ToOrdinal;
50
51impl SqlFunction for ToOrdinal {
52 fn signature(&self) -> FunctionSignature {
53 FunctionSignature {
54 name: "TO_ORDINAL",
55 category: FunctionCategory::String,
56 arg_count: ArgCount::Fixed(1),
57 description: "Convert number to ordinal form (1st, 2nd, 3rd, etc.)",
58 returns: "String with ordinal representation",
59 examples: vec![
60 "SELECT TO_ORDINAL(1) -- Returns '1st'",
61 "SELECT TO_ORDINAL(2) -- Returns '2nd'",
62 "SELECT TO_ORDINAL(21) -- Returns '21st'",
63 "SELECT TO_ORDINAL(123) -- Returns '123rd'",
64 ],
65 }
66 }
67
68 fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
69 if args.len() != 1 {
70 return Ok(DataValue::Null);
71 }
72
73 let num = match &args[0] {
74 DataValue::Float(n) => *n as i64,
75 DataValue::Integer(n) => *n,
76 DataValue::Null => return Ok(DataValue::Null),
77 _ => return Ok(DataValue::Null),
78 };
79
80 Ok(DataValue::String(number_to_ordinal(num)))
81 }
82}
83
84pub struct ToOrdinalWords;
86
87impl SqlFunction for ToOrdinalWords {
88 fn signature(&self) -> FunctionSignature {
89 FunctionSignature {
90 name: "TO_ORDINAL_WORDS",
91 category: FunctionCategory::String,
92 arg_count: ArgCount::Fixed(1),
93 description: "Convert number to ordinal words (first, second, third, etc.)",
94 returns: "String with ordinal words",
95 examples: vec![
96 "SELECT TO_ORDINAL_WORDS(1) -- Returns 'first'",
97 "SELECT TO_ORDINAL_WORDS(21) -- Returns 'twenty-first'",
98 "SELECT TO_ORDINAL_WORDS(100) -- Returns 'one hundredth'",
99 ],
100 }
101 }
102
103 fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
104 if args.len() != 1 {
105 return Ok(DataValue::Null);
106 }
107
108 let num = match &args[0] {
109 DataValue::Float(n) => *n as i64,
110 DataValue::Integer(n) => *n,
111 DataValue::Null => return Ok(DataValue::Null),
112 _ => return Ok(DataValue::Null),
113 };
114
115 Ok(DataValue::String(number_to_ordinal_words(num)))
116 }
117}
118
119fn number_to_words(n: i64) -> String {
121 if n == 0 {
122 return "zero".to_string();
123 }
124
125 let units = [
126 "",
127 "one",
128 "two",
129 "three",
130 "four",
131 "five",
132 "six",
133 "seven",
134 "eight",
135 "nine",
136 "ten",
137 "eleven",
138 "twelve",
139 "thirteen",
140 "fourteen",
141 "fifteen",
142 "sixteen",
143 "seventeen",
144 "eighteen",
145 "nineteen",
146 ];
147
148 let tens = [
149 "", "", "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety",
150 ];
151
152 let thousands = [
153 "",
154 "thousand",
155 "million",
156 "billion",
157 "trillion",
158 "quadrillion",
159 ];
160
161 fn convert_hundreds(n: i64, units: &[&str], tens: &[&str]) -> String {
162 let mut result = String::new();
163
164 let hundreds = n / 100;
165 let remainder = n % 100;
166
167 if hundreds > 0 {
168 result.push_str(units[hundreds as usize]);
169 result.push_str(" hundred");
170 if remainder > 0 {
171 result.push(' ');
172 }
173 }
174
175 if remainder < 20 {
176 result.push_str(units[remainder as usize]);
177 } else {
178 let tens_digit = remainder / 10;
179 let ones_digit = remainder % 10;
180 result.push_str(tens[tens_digit as usize]);
181 if ones_digit > 0 {
182 result.push('-');
183 result.push_str(units[ones_digit as usize]);
184 }
185 }
186
187 result
188 }
189
190 let mut num = n;
191 let mut parts = Vec::new();
192 let mut thousand_index = 0;
193
194 while num > 0 && thousand_index < thousands.len() {
195 let group = num % 1000;
196 if group > 0 {
197 let mut part = convert_hundreds(group, &units, &tens);
198 if !thousands[thousand_index].is_empty() {
199 part.push(' ');
200 part.push_str(thousands[thousand_index]);
201 }
202 parts.push(part);
203 }
204 num /= 1000;
205 thousand_index += 1;
206 }
207
208 parts.reverse();
209 parts.join(" ")
210}
211
212fn number_to_ordinal(n: i64) -> String {
214 let abs_n = n.abs();
215 let suffix = if abs_n % 100 >= 11 && abs_n % 100 <= 13 {
216 "th"
217 } else {
218 match abs_n % 10 {
219 1 => "st",
220 2 => "nd",
221 3 => "rd",
222 _ => "th",
223 }
224 };
225
226 format!("{}{}", n, suffix)
227}
228
229fn number_to_ordinal_words(n: i64) -> String {
231 match n {
233 1 => return "first".to_string(),
234 2 => return "second".to_string(),
235 3 => return "third".to_string(),
236 4 => return "fourth".to_string(),
237 5 => return "fifth".to_string(),
238 6 => return "sixth".to_string(),
239 7 => return "seventh".to_string(),
240 8 => return "eighth".to_string(),
241 9 => return "ninth".to_string(),
242 10 => return "tenth".to_string(),
243 11 => return "eleventh".to_string(),
244 12 => return "twelfth".to_string(),
245 20 => return "twentieth".to_string(),
246 30 => return "thirtieth".to_string(),
247 40 => return "fortieth".to_string(),
248 50 => return "fiftieth".to_string(),
249 60 => return "sixtieth".to_string(),
250 70 => return "seventieth".to_string(),
251 80 => return "eightieth".to_string(),
252 90 => return "ninetieth".to_string(),
253 100 => return "one hundredth".to_string(),
254 1000 => return "one thousandth".to_string(),
255 _ => {}
256 }
257
258 if n < 100 && n > 20 {
260 let tens = (n / 10) * 10;
261 let ones = n % 10;
262 if ones == 0 {
263 return number_to_ordinal_words(tens);
264 }
265 let tens_word = number_to_words(tens);
266 let ones_ordinal = number_to_ordinal_words(ones);
267 return format!("{}-{}", tens_word, ones_ordinal);
268 }
269
270 let words = number_to_words(n);
272
273 if words.ends_with("one") {
275 format!(
276 "{}st",
277 words
278 .trim_end_matches("one")
279 .trim_end_matches('-')
280 .trim_end_matches(' ')
281 )
282 } else if words.ends_with("two") {
283 format!(
284 "{}second",
285 words
286 .trim_end_matches("two")
287 .trim_end_matches('-')
288 .trim_end_matches(' ')
289 )
290 } else if words.ends_with("three") {
291 format!(
292 "{}third",
293 words
294 .trim_end_matches("three")
295 .trim_end_matches('-')
296 .trim_end_matches(' ')
297 )
298 } else if words.ends_with("y") {
299 format!("{}ieth", words.trim_end_matches('y'))
300 } else {
301 format!("{}th", words)
302 }
303}
304
305#[cfg(test)]
306mod tests {
307 use super::*;
308
309 #[test]
310 fn test_to_words() {
311 let func = ToWords;
312
313 assert_eq!(
314 func.evaluate(&[DataValue::Float(0.0)]).unwrap(),
315 DataValue::String("zero".to_string())
316 );
317
318 assert_eq!(
319 func.evaluate(&[DataValue::Float(42.0)]).unwrap(),
320 DataValue::String("forty-two".to_string())
321 );
322
323 assert_eq!(
324 func.evaluate(&[DataValue::Float(999.0)]).unwrap(),
325 DataValue::String("nine hundred ninety-nine".to_string())
326 );
327
328 assert_eq!(
329 func.evaluate(&[DataValue::Float(1234.0)]).unwrap(),
330 DataValue::String("one thousand two hundred thirty-four".to_string())
331 );
332
333 assert_eq!(
334 func.evaluate(&[DataValue::Float(1000000.0)]).unwrap(),
335 DataValue::String("one million".to_string())
336 );
337 }
338
339 #[test]
340 fn test_to_ordinal() {
341 let func = ToOrdinal;
342
343 assert_eq!(
344 func.evaluate(&[DataValue::Float(1.0)]).unwrap(),
345 DataValue::String("1st".to_string())
346 );
347
348 assert_eq!(
349 func.evaluate(&[DataValue::Float(2.0)]).unwrap(),
350 DataValue::String("2nd".to_string())
351 );
352
353 assert_eq!(
354 func.evaluate(&[DataValue::Float(3.0)]).unwrap(),
355 DataValue::String("3rd".to_string())
356 );
357
358 assert_eq!(
359 func.evaluate(&[DataValue::Float(11.0)]).unwrap(),
360 DataValue::String("11th".to_string())
361 );
362
363 assert_eq!(
364 func.evaluate(&[DataValue::Float(21.0)]).unwrap(),
365 DataValue::String("21st".to_string())
366 );
367
368 assert_eq!(
369 func.evaluate(&[DataValue::Float(123.0)]).unwrap(),
370 DataValue::String("123rd".to_string())
371 );
372 }
373
374 #[test]
375 fn test_to_ordinal_words() {
376 let func = ToOrdinalWords;
377
378 assert_eq!(
379 func.evaluate(&[DataValue::Float(1.0)]).unwrap(),
380 DataValue::String("first".to_string())
381 );
382
383 assert_eq!(
384 func.evaluate(&[DataValue::Float(21.0)]).unwrap(),
385 DataValue::String("twenty-first".to_string())
386 );
387
388 assert_eq!(
389 func.evaluate(&[DataValue::Float(100.0)]).unwrap(),
390 DataValue::String("one hundredth".to_string())
391 );
392 }
393}