dental_notation/
tooth.rs

1use crate::alloc::borrow::ToOwned;
2use alloc::format;
3use alloc::string::{String, ToString};
4
5#[derive(Copy, Clone)]
6pub enum NotationKind {
7    /// [FDI / ISO 3950 notation](https://en.wikipedia.org/wiki/FDI_World_Dental_Federation_notation)
8    Iso,
9    /// [UNS (Universal Numbering System) notation](https://en.wikipedia.org/wiki/Universal_Numbering_System)
10    Uns,
11    /// [Alphanumeric notation](https://en.wikipedia.org/wiki/Dental_notation#Alphanumeric_notation)
12    Alphanumeric,
13}
14
15#[derive(PartialEq, Clone, Copy, Debug)]
16pub enum QuadrantKind {
17    TopLeft,
18    TopRight,
19    BottomLeft,
20    BottomRight,
21}
22
23pub enum ToothType {
24    Incisor,
25    Canine,
26    Premolar,
27    Molar,
28}
29
30/// Represent a tooth notation. It provides the different methods to convert from and to different notations in two different ways:
31/// - [`from`](Tooth::from) and [`to`](Tooth::to), and providing the [NotationKind] as parameter,
32/// - `from_xx` and `to_xx`, `xx` being one of the following: `iso`, `uns` and `alphanumeric.
33#[derive(PartialEq)]
34pub struct Tooth {
35    quadrant: QuadrantKind,
36    number: u8,
37    permanent: bool,
38}
39
40impl Tooth {
41    pub fn new(number: u8, quadrant: QuadrantKind, permanent: bool) -> Result<Tooth, String> {
42        if let Err(err) = Tooth::check_tooth_number(number, permanent) {
43            return Err(err);
44        }
45
46        Ok(Tooth {
47            number,
48            quadrant,
49            permanent,
50        })
51    }
52
53    fn check_tooth_number(number: u8, permanent: bool) -> Result<(), String> {
54        if permanent && (number < 1 || number > 8) {
55            Err(format!(
56                "Permanent tooth {} should be in range [1; 8]",
57                number
58            ))
59        } else if !permanent && (number < 1 || number > 5) {
60            Err(format!(
61                "Primary tooth {} should be in range [1; 5]",
62                number
63            ))
64        } else {
65            Ok(())
66        }
67    }
68
69    /// Get the maximum value for a tooth number (depending if it is a primary dentition or not)
70    pub fn quadrant_max(permanent: bool) -> u8 {
71        if permanent {
72            8
73        } else {
74            5
75        }
76    }
77
78    pub fn from_iso(value: &str) -> Result<Self, String> {
79        if value.len() != 2 {
80            return Err(format!("{} is not a valid ISO dental notation", value));
81        }
82        let mut char_iter = value.chars();
83        let (quadrant, permanent) = match char_iter.next().unwrap() {
84            '1' => (QuadrantKind::TopLeft, true),
85            '2' => (QuadrantKind::TopRight, true),
86            '3' => (QuadrantKind::BottomRight, true),
87            '4' => (QuadrantKind::BottomLeft, true),
88            '5' => (QuadrantKind::TopLeft, false),
89            '6' => (QuadrantKind::TopRight, false),
90            '7' => (QuadrantKind::BottomRight, false),
91            '8' => (QuadrantKind::BottomLeft, false),
92            x => return Err(format!("Quadrant value {} not included in range [1; 8]", x)),
93        };
94
95        let tooth_string = char_iter.next().unwrap();
96        let tooth_number_option = tooth_string.to_digit(10);
97
98        if tooth_number_option.is_none() {
99            return Err(format!("{} is not a number", tooth_string));
100        }
101        let number = tooth_number_option.unwrap() as u8;
102        if let Err(err) = Tooth::check_tooth_number(number, permanent) {
103            return Err(err);
104        }
105        Ok(Tooth {
106            quadrant,
107            permanent,
108            number,
109        })
110    }
111
112    pub fn to_iso(&self) -> String {
113        let quadrant_number: u8 = match (&self.quadrant, self.permanent) {
114            (QuadrantKind::TopLeft, true) => 1,
115            (QuadrantKind::TopRight, true) => 2,
116            (QuadrantKind::BottomLeft, true) => 4,
117            (QuadrantKind::BottomRight, true) => 3,
118            (QuadrantKind::TopLeft, false) => 5,
119            (QuadrantKind::TopRight, false) => 6,
120            (QuadrantKind::BottomLeft, false) => 8,
121            (QuadrantKind::BottomRight, false) => 7,
122        };
123        quadrant_number.to_string() + &self.number.to_string()
124    }
125
126    pub fn from_uns(value: &str) -> Result<Self, String> {
127        let number_value_option = value.parse::<u8>();
128        let mut permanent = false;
129        let uns = if number_value_option.is_ok() {
130            permanent = true;
131            number_value_option.unwrap()
132        } else {
133            (value.chars().next().expect("No value available") as i32 - 64) as u8
134        };
135
136        if permanent && (uns < 1 || uns > 32) {
137            return Err(format!(
138                "UNS permanent tooth has to be in range [1; 32] (currently {})",
139                uns
140            ));
141        } else if !permanent && (uns < 1 || uns > 20) {
142            return Err(format!("UNS primary tooth has to be in range [A; T]"));
143        }
144        let max = Tooth::quadrant_max(permanent);
145        let quadrant = match ((uns - 1) / max) + 1 {
146            1 => QuadrantKind::TopLeft,
147            2 => QuadrantKind::TopRight,
148            3 => QuadrantKind::BottomRight,
149            4 => QuadrantKind::BottomLeft,
150            _ => return Err(format!("UNS tooth value not in range")),
151        };
152        let number = if quadrant == QuadrantKind::TopRight || quadrant == QuadrantKind::BottomLeft {
153            ((uns - 1) % max) + 1
154        } else {
155            max - ((uns - 1) % max)
156        };
157        if let Err(err) = Tooth::check_tooth_number(number, permanent) {
158            return Err(err);
159        }
160        Ok(Tooth {
161            quadrant,
162            number,
163            permanent,
164        })
165    }
166
167    pub fn to_uns(&self) -> String {
168        let max = Tooth::quadrant_max(self.permanent);
169        let value: u8 = match (&self.quadrant, self.number) {
170            (QuadrantKind::TopLeft, x) => max - x + 1,
171            (QuadrantKind::TopRight, x) => x + max,
172            (QuadrantKind::BottomRight, x) => (max - x + 1) + max * 2,
173            (QuadrantKind::BottomLeft, x) => x + max * 3,
174        };
175
176        if self.permanent {
177            format!("{:2}", value)
178        } else {
179            ((value + 64) as char).to_string()
180        }
181    }
182
183    pub fn from_alphanumeric(value: &str) -> Result<Self, String> {
184        if value.len() != 3 {
185            return Err(format!(
186                "{} is not a valid alphanumeric dental notation",
187                value
188            ));
189        }
190        let quadrant = match &value[0..2] {
191            "UL" => QuadrantKind::TopLeft,
192            "UR" => QuadrantKind::TopRight,
193            "LR" => QuadrantKind::BottomRight,
194            "LL" => QuadrantKind::BottomLeft,
195            x => {
196                return Err(format!(
197                    "Quadrant value {} not a valid value (accepted: ['UL', 'UR', 'LR', 'LL'])",
198                    x
199                ))
200            }
201        };
202
203        let tooth_string = &value[2..3];
204
205        let (number, permanent) = match tooth_string {
206            "1" => (1, true),
207            "2" => (2, true),
208            "3" => (3, true),
209            "4" => (4, true),
210            "5" => (5, true),
211            "6" => (6, true),
212            "7" => (7, true),
213            "8" => (8, true),
214            "A" => (1, false),
215            "B" => (2, false),
216            "C" => (3, false),
217            "D" => (4, false),
218            "E" => (5, false),
219            x => {
220                return Err(format!(
221                    "Number value {} not a valid value (accepted: [1; 8] and ['A'; 'E'])",
222                    x
223                ))
224            }
225        };
226        if let Err(err) = Tooth::check_tooth_number(number, permanent) {
227            return Err(err);
228        }
229        Ok(Tooth {
230            quadrant,
231            permanent,
232            number,
233        })
234    }
235
236    pub fn to_alphanumeric(&self) -> String {
237        let quadrant = match &self.quadrant {
238            QuadrantKind::TopLeft => "UL",
239            QuadrantKind::TopRight => "UR",
240            QuadrantKind::BottomRight => "LR",
241            QuadrantKind::BottomLeft => "LL",
242        };
243
244        let number = if self.permanent {
245            self.number.to_string()
246        } else {
247            ((self.number + 64) as char).to_string()
248        };
249        quadrant.to_owned() + &number
250    }
251
252    pub fn from(value: &str, from: &NotationKind) -> Result<Self, String> {
253        match from {
254            NotationKind::Iso => Tooth::from_iso(value),
255            NotationKind::Uns => Tooth::from_uns(value),
256            NotationKind::Alphanumeric => Tooth::from_alphanumeric(value),
257        }
258    }
259    pub fn to(&self, to: &NotationKind) -> String {
260        match to {
261            NotationKind::Iso => self.to_iso(),
262            NotationKind::Uns => self.to_uns(),
263            NotationKind::Alphanumeric => self.to_alphanumeric(),
264        }
265    }
266
267    #[deprecated(since = "1.0.0", note = "Use 'to' and 'from' directly")]
268    pub fn convert(from: &NotationKind, to: &NotationKind, value: &str) -> Result<String, String> {
269        let tooth_result = Tooth::from(value, from);
270
271        match tooth_result {
272            Ok(tooth) => Ok(tooth.to(to)),
273            Err(err) => Err(err),
274        }
275    }
276
277    pub fn get_type(&self) -> ToothType {
278        match (&self.number, &self.permanent) {
279            (1..=2, _) => ToothType::Incisor,
280            (3, _) => ToothType::Canine,
281            (4..=5, true) => ToothType::Premolar,
282            _ => ToothType::Molar,
283        }
284    }
285}
286
287#[cfg(test)]
288mod test {
289    use super::*;
290
291    macro_rules! to {
292        ($name:ident, $func:ident, $quadrant:expr,$number:expr, $permanent:expr, $value:expr) => {
293            #[test]
294            fn $name() {
295                let tooth = Tooth {
296                    quadrant: $quadrant,
297                    number: $number,
298                    permanent: $permanent,
299                };
300                assert_eq!(tooth.$func(), $value);
301            }
302        };
303    }
304
305    macro_rules! from {
306        ($name:ident, $func:ident, $value:expr, $quadrant:expr,$number:expr, $permanent:expr) => {
307            #[test]
308            fn $name() {
309                let tooth = Tooth::$func($value).unwrap();
310
311                assert_eq!(tooth.quadrant, $quadrant);
312                assert_eq!(tooth.number, $number);
313                assert_eq!(tooth.permanent, $permanent);
314            }
315        };
316    }
317
318    macro_rules! from_fail {
319        ($name:ident, $func:ident, $value:expr) => {
320            #[test]
321            fn $name() {
322                assert!(Tooth::$func($value).is_err());
323            }
324        };
325    }
326
327    to!(to_iso_11, to_iso, QuadrantKind::TopLeft, 1, true, "11");
328    to!(to_iso_18, to_iso, QuadrantKind::TopLeft, 8, true, "18");
329    to!(to_iso_21, to_iso, QuadrantKind::TopRight, 1, true, "21");
330    to!(to_iso_28, to_iso, QuadrantKind::TopRight, 8, true, "28");
331    to!(to_iso_31, to_iso, QuadrantKind::BottomRight, 1, true, "31");
332    to!(to_iso_38, to_iso, QuadrantKind::BottomRight, 8, true, "38");
333    to!(to_iso_41, to_iso, QuadrantKind::BottomLeft, 1, true, "41");
334    to!(to_iso_48, to_iso, QuadrantKind::BottomLeft, 8, true, "48");
335    to!(to_iso_51, to_iso, QuadrantKind::TopLeft, 1, false, "51");
336    to!(to_iso_55, to_iso, QuadrantKind::TopLeft, 5, false, "55");
337    to!(to_iso_61, to_iso, QuadrantKind::TopRight, 1, false, "61");
338    to!(to_iso_65, to_iso, QuadrantKind::TopRight, 5, false, "65");
339    to!(to_iso_71, to_iso, QuadrantKind::BottomRight, 1, false, "71");
340    to!(to_iso_75, to_iso, QuadrantKind::BottomRight, 5, false, "75");
341    to!(to_iso_81, to_iso, QuadrantKind::BottomLeft, 1, false, "81");
342    to!(to_iso_85, to_iso, QuadrantKind::BottomLeft, 5, false, "85");
343
344    from!(from_iso_11, from_iso, "11", QuadrantKind::TopLeft, 1, true);
345    from!(from_iso_18, from_iso, "18", QuadrantKind::TopLeft, 8, true);
346    from!(from_iso_21, from_iso, "21", QuadrantKind::TopRight, 1, true);
347    from!(from_iso_28, from_iso, "28", QuadrantKind::TopRight, 8, true);
348    from!(
349        from_iso_31,
350        from_iso,
351        "31",
352        QuadrantKind::BottomRight,
353        1,
354        true
355    );
356    from!(
357        from_iso_38,
358        from_iso,
359        "38",
360        QuadrantKind::BottomRight,
361        8,
362        true
363    );
364    from!(
365        from_iso_41,
366        from_iso,
367        "41",
368        QuadrantKind::BottomLeft,
369        1,
370        true
371    );
372    from!(
373        from_iso_48,
374        from_iso,
375        "48",
376        QuadrantKind::BottomLeft,
377        8,
378        true
379    );
380    from!(from_iso_51, from_iso, "51", QuadrantKind::TopLeft, 1, false);
381    from!(from_iso_55, from_iso, "55", QuadrantKind::TopLeft, 5, false);
382    from!(
383        from_iso_61,
384        from_iso,
385        "61",
386        QuadrantKind::TopRight,
387        1,
388        false
389    );
390    from!(
391        from_iso_65,
392        from_iso,
393        "65",
394        QuadrantKind::TopRight,
395        5,
396        false
397    );
398    from!(
399        from_iso_71,
400        from_iso,
401        "71",
402        QuadrantKind::BottomRight,
403        1,
404        false
405    );
406    from!(
407        from_iso_75,
408        from_iso,
409        "75",
410        QuadrantKind::BottomRight,
411        5,
412        false
413    );
414    from!(
415        from_iso_81,
416        from_iso,
417        "81",
418        QuadrantKind::BottomLeft,
419        1,
420        false
421    );
422    from!(
423        from_iso_85,
424        from_iso,
425        "85",
426        QuadrantKind::BottomLeft,
427        5,
428        false
429    );
430
431    from_fail!(from_iso_fail_10, from_iso, "10");
432    from_fail!(from_iso_fail_19, from_iso, "19");
433    from_fail!(from_iso_fail_20, from_iso, "20");
434    from_fail!(from_iso_fail_29, from_iso, "29");
435    from_fail!(from_iso_fail_30, from_iso, "30");
436    from_fail!(from_iso_fail_39, from_iso, "39");
437    from_fail!(from_iso_fail_40, from_iso, "40");
438    from_fail!(from_iso_fail_49, from_iso, "49");
439    from_fail!(from_iso_fail_50, from_iso, "50");
440    from_fail!(from_iso_fail_56, from_iso, "56");
441    from_fail!(from_iso_fail_60, from_iso, "60");
442    from_fail!(from_iso_fail_66, from_iso, "66");
443    from_fail!(from_iso_fail_70, from_iso, "70");
444    from_fail!(from_iso_fail_76, from_iso, "76");
445    from_fail!(from_iso_fail_80, from_iso, "80");
446    from_fail!(from_iso_fail_86, from_iso, "86");
447
448    to!(to_uns_1, to_uns, QuadrantKind::TopLeft, 1, true, "8");
449    to!(to_uns_8, to_uns, QuadrantKind::TopLeft, 8, true, "1");
450    to!(to_uns_9, to_uns, QuadrantKind::TopRight, 1, true, "9");
451    to!(to_uns_16, to_uns, QuadrantKind::TopRight, 8, true, "16");
452    to!(to_uns_17, to_uns, QuadrantKind::BottomRight, 1, true, "24");
453    to!(to_uns_24, to_uns, QuadrantKind::BottomRight, 8, true, "17");
454    to!(to_uns_25, to_uns, QuadrantKind::BottomLeft, 1, true, "25");
455    to!(to_uns_32, to_uns, QuadrantKind::BottomLeft, 8, true, "32");
456    to!(to_uns_a, to_uns, QuadrantKind::TopLeft, 1, false, "E");
457    to!(to_uns_e, to_uns, QuadrantKind::TopLeft, 5, false, "A");
458    to!(to_uns_f, to_uns, QuadrantKind::TopRight, 1, false, "F");
459    to!(to_uns_j, to_uns, QuadrantKind::TopRight, 5, false, "J");
460    to!(to_uns_k, to_uns, QuadrantKind::BottomRight, 1, false, "O");
461    to!(to_uns_o, to_uns, QuadrantKind::BottomRight, 5, false, "K");
462    to!(to_uns_p, to_uns, QuadrantKind::BottomLeft, 1, false, "P");
463    to!(to_uns_t, to_uns, QuadrantKind::BottomLeft, 5, false, "T");
464
465    from!(from_uns_8, from_uns, "8", QuadrantKind::TopLeft, 1, true);
466    from!(from_uns_1, from_uns, "1", QuadrantKind::TopLeft, 8, true);
467    from!(from_uns_9, from_uns, "9", QuadrantKind::TopRight, 1, true);
468    from!(from_uns_16, from_uns, "16", QuadrantKind::TopRight, 8, true);
469    from!(
470        from_uns_24,
471        from_uns,
472        "24",
473        QuadrantKind::BottomRight,
474        1,
475        true
476    );
477    from!(
478        from_uns_17,
479        from_uns,
480        "17",
481        QuadrantKind::BottomRight,
482        8,
483        true
484    );
485    from!(
486        from_uns_25,
487        from_uns,
488        "25",
489        QuadrantKind::BottomLeft,
490        1,
491        true
492    );
493    from!(
494        from_uns_32,
495        from_uns,
496        "32",
497        QuadrantKind::BottomLeft,
498        8,
499        true
500    );
501    from!(from_uns_e, from_uns, "E", QuadrantKind::TopLeft, 1, false);
502    from!(from_uns_a, from_uns, "A", QuadrantKind::TopLeft, 5, false);
503    from!(from_uns_f, from_uns, "F", QuadrantKind::TopRight, 1, false);
504    from!(from_uns_j, from_uns, "J", QuadrantKind::TopRight, 5, false);
505    from!(
506        from_uns_o,
507        from_uns,
508        "O",
509        QuadrantKind::BottomRight,
510        1,
511        false
512    );
513    from!(
514        from_uns_k,
515        from_uns,
516        "K",
517        QuadrantKind::BottomRight,
518        5,
519        false
520    );
521    from!(
522        from_uns_p,
523        from_uns,
524        "P",
525        QuadrantKind::BottomLeft,
526        1,
527        false
528    );
529    from!(
530        from_uns_t,
531        from_uns,
532        "T",
533        QuadrantKind::BottomLeft,
534        5,
535        false
536    );
537
538    from_fail!(from_uns_fail_0, from_uns, "0");
539    from_fail!(from_uns_fail_33, from_uns, "33");
540    from_fail!(from_uns_fail_at, from_uns, "@");
541    from_fail!(from_uns_fail_u, from_uns, "U");
542
543    to!(
544        to_alphanumeric_ul1,
545        to_alphanumeric,
546        QuadrantKind::TopLeft,
547        1,
548        true,
549        "UL1"
550    );
551    to!(
552        to_alphanumeric_ul8,
553        to_alphanumeric,
554        QuadrantKind::TopLeft,
555        8,
556        true,
557        "UL8"
558    );
559    to!(
560        to_alphanumeric_ur1,
561        to_alphanumeric,
562        QuadrantKind::TopRight,
563        1,
564        true,
565        "UR1"
566    );
567    to!(
568        to_alphanumeric_ur8,
569        to_alphanumeric,
570        QuadrantKind::TopRight,
571        8,
572        true,
573        "UR8"
574    );
575    to!(
576        to_alphanumeric_lr1,
577        to_alphanumeric,
578        QuadrantKind::BottomRight,
579        1,
580        true,
581        "LR1"
582    );
583    to!(
584        to_alphanumeric_lr8,
585        to_alphanumeric,
586        QuadrantKind::BottomRight,
587        8,
588        true,
589        "LR8"
590    );
591    to!(
592        to_alphanumeric_ll1,
593        to_alphanumeric,
594        QuadrantKind::BottomLeft,
595        1,
596        true,
597        "LL1"
598    );
599    to!(
600        to_alphanumeric_ll8,
601        to_alphanumeric,
602        QuadrantKind::BottomLeft,
603        8,
604        true,
605        "LL8"
606    );
607    to!(
608        to_alphanumeric_ula,
609        to_alphanumeric,
610        QuadrantKind::TopLeft,
611        1,
612        false,
613        "ULA"
614    );
615    to!(
616        to_alphanumeric_ule,
617        to_alphanumeric,
618        QuadrantKind::TopLeft,
619        5,
620        false,
621        "ULE"
622    );
623    to!(
624        to_alphanumeric_ura,
625        to_alphanumeric,
626        QuadrantKind::TopRight,
627        1,
628        false,
629        "URA"
630    );
631    to!(
632        to_alphanumeric_ure,
633        to_alphanumeric,
634        QuadrantKind::TopRight,
635        5,
636        false,
637        "URE"
638    );
639    to!(
640        to_alphanumeric_lra,
641        to_alphanumeric,
642        QuadrantKind::BottomRight,
643        1,
644        false,
645        "LRA"
646    );
647    to!(
648        to_alphanumeric_lre,
649        to_alphanumeric,
650        QuadrantKind::BottomRight,
651        5,
652        false,
653        "LRE"
654    );
655    to!(
656        to_alphanumeric_lla,
657        to_alphanumeric,
658        QuadrantKind::BottomLeft,
659        1,
660        false,
661        "LLA"
662    );
663    to!(
664        to_alphanumeric_lle,
665        to_alphanumeric,
666        QuadrantKind::BottomLeft,
667        5,
668        false,
669        "LLE"
670    );
671    from!(
672        from_alphanumeric_ul1,
673        from_alphanumeric,
674        "UL1",
675        QuadrantKind::TopLeft,
676        1,
677        true
678    );
679    from!(
680        from_alphanumeric_ul8,
681        from_alphanumeric,
682        "UL8",
683        QuadrantKind::TopLeft,
684        8,
685        true
686    );
687    from!(
688        from_alphanumeric_ur1,
689        from_alphanumeric,
690        "UR1",
691        QuadrantKind::TopRight,
692        1,
693        true
694    );
695    from!(
696        from_alphanumeric_ur8,
697        from_alphanumeric,
698        "UR8",
699        QuadrantKind::TopRight,
700        8,
701        true
702    );
703    from!(
704        from_alphanumeric_lr1,
705        from_alphanumeric,
706        "LR1",
707        QuadrantKind::BottomRight,
708        1,
709        true
710    );
711    from!(
712        from_alphanumeric_lr8,
713        from_alphanumeric,
714        "LR8",
715        QuadrantKind::BottomRight,
716        8,
717        true
718    );
719    from!(
720        from_alphanumeric_ll1,
721        from_alphanumeric,
722        "LL1",
723        QuadrantKind::BottomLeft,
724        1,
725        true
726    );
727    from!(
728        from_alphanumeric_ll8,
729        from_alphanumeric,
730        "LL8",
731        QuadrantKind::BottomLeft,
732        8,
733        true
734    );
735    from!(
736        from_alphanumeric_ula,
737        from_alphanumeric,
738        "ULA",
739        QuadrantKind::TopLeft,
740        1,
741        false
742    );
743    from!(
744        from_alphanumeric_ule,
745        from_alphanumeric,
746        "ULE",
747        QuadrantKind::TopLeft,
748        5,
749        false
750    );
751    from!(
752        from_alphanumeric_ura,
753        from_alphanumeric,
754        "URA",
755        QuadrantKind::TopRight,
756        1,
757        false
758    );
759    from!(
760        from_alphanumeric_ure,
761        from_alphanumeric,
762        "URE",
763        QuadrantKind::TopRight,
764        5,
765        false
766    );
767    from!(
768        from_alphanumeric_lra,
769        from_alphanumeric,
770        "LRA",
771        QuadrantKind::BottomRight,
772        1,
773        false
774    );
775    from!(
776        from_alphanumeric_lre,
777        from_alphanumeric,
778        "LRE",
779        QuadrantKind::BottomRight,
780        5,
781        false
782    );
783    from!(
784        from_alphanumeric_lla,
785        from_alphanumeric,
786        "LLA",
787        QuadrantKind::BottomLeft,
788        1,
789        false
790    );
791    from!(
792        from_alphanumeric_lle,
793        from_alphanumeric,
794        "LLE",
795        QuadrantKind::BottomLeft,
796        5,
797        false
798    );
799    from_fail!(from_alphanumeric_fail_ul0, from_alphanumeric, "UL0");
800    from_fail!(from_alphanumeric_fail_ul9, from_alphanumeric, "UL9");
801    from_fail!(from_alphanumeric_fail_ur0, from_alphanumeric, "UR0");
802    from_fail!(from_alphanumeric_fail_ur9, from_alphanumeric, "UR9");
803    from_fail!(from_alphanumeric_fail_lr0, from_alphanumeric, "LR0");
804    from_fail!(from_alphanumeric_fail_lr9, from_alphanumeric, "LR9");
805    from_fail!(from_alphanumeric_fail_ll0, from_alphanumeric, "LL0");
806    from_fail!(from_alphanumeric_fail_ll9, from_alphanumeric, "LL9");
807    from_fail!(from_alphanumeric_fail_ulat, from_alphanumeric, "UL@");
808    from_fail!(from_alphanumeric_fail_ulf, from_alphanumeric, "ULF");
809    from_fail!(from_alphanumeric_fail_urat, from_alphanumeric, "UR@");
810    from_fail!(from_alphanumeric_fail_urf, from_alphanumeric, "URF");
811    from_fail!(from_alphanumeric_fail_lrat, from_alphanumeric, "LR@");
812    from_fail!(from_alphanumeric_fail_lrf, from_alphanumeric, "LRF");
813    from_fail!(from_alphanumeric_fail_llat, from_alphanumeric, "LL@");
814    from_fail!(from_alphanumeric_fail_llf, from_alphanumeric, "LLF");
815}