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
use crate::{Error, Nominal, NominalString, NominalSystem, UnsignedInteger, WithNominal};

/// Hebrew numerals.
#[doc = include_str!("./previews/Hebrew.md")]
pub struct Hebrew;

impl<T> NominalSystem<T> for Hebrew
where
    T: Nominal + UnsignedInteger + TryFrom<u32> + From<u8>,
{
    fn try_format_nominal(&self, nominal: T) -> Result<NominalString, Error<T>> {
        let mut remaining = nominal;
        if remaining.is_zero() {
            return Err(Error::NoZeroSymbol);
        }

        let fifteen = T::from(15);
        let sixteen = T::from(16);

        let mut formatted = NominalString::default();
        'symbol_loop: for (symbol, value) in [
            ('ת', 400u32),
            ('ש', 300),
            ('ר', 200),
            ('ק', 100),
            ('צ', 90),
            ('פ', 80),
            ('ע', 70),
            ('ס', 60),
            ('נ', 50),
            ('מ', 40),
            ('ל', 30),
            ('כ', 20),
            ('י', 10),
            ('ט', 9),
            ('ח', 8),
            ('ז', 7),
            ('ו', 6),
            ('ה', 5),
            ('ד', 4),
            ('ג', 3),
            ('ב', 2),
            ('א', 1),
        ] {
            let Ok(value) = T::try_from(value) else {
                continue;
            };

            while remaining >= value {
                if remaining == fifteen {
                    formatted.try_push_str("ט״ו").with_nominal(nominal)?;
                    break 'symbol_loop;
                } else if remaining == sixteen {
                    formatted.try_push_str("ט״ז").with_nominal(nominal)?;
                    break 'symbol_loop;
                }

                // When a single symbol is used to represent a number, the
                // symbol is wrapped in geresh and gershayim characters to
                // distinguish it from a word.
                let single_symbol = value == remaining && formatted.is_empty();
                if single_symbol {
                    formatted.try_push('׳').with_nominal(nominal)?;
                }
                remaining -= value;
                formatted.try_push(symbol).with_nominal(nominal)?;
                if single_symbol {
                    formatted.try_push('״').with_nominal(nominal)?;
                    break;
                }
            }
        }

        Ok(formatted)
    }
}

#[test]
fn hebrew() {
    assert_eq!(Hebrew.format_nominal(997_u32), "תתקצז");
    assert_eq!(Hebrew.format_nominal(1_u32), "׳א״");
    assert_eq!(Hebrew.format_nominal(15_u32), "ט״ו");
    assert_eq!(Hebrew.format_nominal(16_u32), "ט״ז");
    assert_eq!(Hebrew.try_format_nominal(0_u32), Err(Error::NoZeroSymbol));
}