abhedya_chhandas/
lib.rs

1
2// Basic mappings for Sanskrit Phonemes to integers
3// Consonants (Vyambjana) - 33 Standard
4const CONSONANTS: [&str; 33] = [
5    "k", "kh", "g", "gh", "ṅ",
6    "c", "ch", "j", "jh", "ñ",
7    "ṭ", "ṭh", "ḍ", "ḍh", "ṇ",
8    "t", "th", "d", "dh", "n",
9    "p", "ph", "b", "bh", "m",
10    "y", "r", "l", "v",
11    "ś", "ṣ", "s", "h"
12];
13
14// Matras (Svara) - 16 Standard
15// Short (Laghu): a, i, u, ṛ, ḷ
16// Long (Guru): ā, ī, ū, ṝ, ḹ, e, ai, o, au, aṃ, aḥ
17const MATRAS: [&str; 16] = [
18    "a", "ā", "i", "ī", "u", "ū", "ṛ", "ṝ", 
19    "ḷ", "ḹ", "e", "ai", "o", "au", "aṃ", "aḥ"
20];
21
22#[derive(Debug, Clone, Copy, PartialEq)]
23pub enum MatraWeight {
24    Laghu, // Short
25    Guru,  // Long
26}
27
28
29
30#[derive(Debug, Clone, PartialEq)]
31pub struct Syllable {
32    pub value: u16, // The integer value in Z_q
33}
34
35impl Syllable {
36    pub fn new(val: u16) -> self::Syllable {
37        Syllable { value: val }
38    }
39
40    pub fn to_sanskrit(&self) -> String {
41        let v = self.value as usize;
42        let c_idx = v / 16;
43        let m_idx = v % 16;
44        
45        // Use modulo to wrap around for larger Q values to maintain uniform distribution across consonants
46        let c = CONSONANTS[c_idx % 33];
47        
48        // Balanced Mapping for Crypto Uniformity:
49        // Indices 0..8 (8 total) -> Map to Laghu (size 5)
50        // Indices 8..16 (8 total) -> Map to Guru (size 11)
51        let m_str = if m_idx < 8 {
52             // Weights (0-7): Laghu
53             // Cycle through the 5 Laghus: a, i, u, r, l (indices 0, 2, 4, 6, 8 in original array)
54             let lags = [0, 2, 4, 6, 8];
55             MATRAS[lags[m_idx % 5]]
56        } else {
57             // Weights (8-15): Guru
58             // Cycle through the 11 Gurus
59             let gurs = [1, 3, 5, 7, 9, 10, 11, 12, 13, 14, 15];
60             MATRAS[gurs[(m_idx - 8) % 11]]
61        };
62
63        format!("{}{}", c, m_str)
64    }
65}
66
67pub fn get_matra_weight(idx: usize) -> MatraWeight {
68    if idx < 8 {
69        MatraWeight::Laghu
70    } else {
71        MatraWeight::Guru
72    }
73}
74
75pub fn encode_vector(vec: &[u16], use_meter: bool) -> String {
76    let mut result = Vec::new();
77    // In Chhandas (Meter) mode, we use the 'break' (Yati) as a dimension separator.
78    // Standard Anushtubh meter has 4 quarters (padas) of 8 syllables.
79    // For PoC, we can insert a danda '।' every 8 syllables if meter is enabled.
80    
81    for (i, val) in vec.iter().enumerate() {
82        result.push(Syllable::new(*val).to_sanskrit());
83        if use_meter && (i + 1) % 8 == 0 {
84            result.push("।".to_string());
85        }
86    }
87    result.join(" ")
88}
89
90pub fn decode_verse(verse: &str) -> Vec<u16> {
91    // This is a naive decoder for the PoC
92    // It assumes space separation and exact matches
93    // In a real system, we'd have a robust parser
94    
95    verse.split_whitespace()
96        .map(|s| {
97            // Reverse lookup fallback
98            // This is O(N) basic search, okay for PoC
99            let mut val = 0;
100            // Try to match easiest way: iterate all combos
101            for c in 0..33 {
102                for m_idx in 0..16 {
103                   let candidate_val = (c * 16 + m_idx) as u16;
104                   let built = Syllable::new(candidate_val).to_sanskrit();
105                   if built == s {
106                       return candidate_val;
107                   }
108                }
109            }
110            0 // Fail safe
111        })
112        .collect()
113}