sql_cli/sql/functions/
constants.rs

1use anyhow::Result;
2
3use super::{ArgCount, FunctionCategory, FunctionSignature, SqlFunction};
4use crate::data::datatable::DataValue;
5
6/// PI constant function
7pub struct PiFunction;
8
9impl SqlFunction for PiFunction {
10    fn signature(&self) -> FunctionSignature {
11        FunctionSignature {
12            name: "PI",
13            category: FunctionCategory::Constant,
14            arg_count: ArgCount::Fixed(0),
15            description: "Returns the value of π (pi)",
16            returns: "FLOAT",
17            examples: vec!["SELECT PI()", "SELECT radius * 2 * PI() AS circumference"],
18        }
19    }
20
21    fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
22        self.validate_args(args)?;
23        Ok(DataValue::Float(std::f64::consts::PI))
24    }
25}
26
27/// E (Euler's number) constant function
28pub struct EFunction;
29
30impl SqlFunction for EFunction {
31    fn signature(&self) -> FunctionSignature {
32        FunctionSignature {
33            name: "E",
34            category: FunctionCategory::Constant,
35            arg_count: ArgCount::Fixed(0),
36            description: "Returns Euler's number (e ≈ 2.71828)",
37            returns: "FLOAT",
38            examples: vec!["SELECT E()", "SELECT POW(E(), x) AS exp_x"],
39        }
40    }
41
42    fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
43        self.validate_args(args)?;
44        Ok(DataValue::Float(std::f64::consts::E))
45    }
46}
47
48/// Mass of electron in kg
49pub struct MeFunction;
50
51/// Alias for ME function
52pub struct MassElectronFunction;
53
54impl SqlFunction for MeFunction {
55    fn signature(&self) -> FunctionSignature {
56        FunctionSignature {
57            name: "ME",
58            category: FunctionCategory::Constant,
59            arg_count: ArgCount::Fixed(0),
60            description: "Returns the mass of an electron in kg (9.10938356 × 10^-31)",
61            returns: "FLOAT",
62            examples: vec!["SELECT ME()", "SELECT mass / ME() AS electron_masses"],
63        }
64    }
65
66    fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
67        self.validate_args(args)?;
68        Ok(DataValue::Float(9.1093837015e-31))
69    }
70}
71
72impl SqlFunction for MassElectronFunction {
73    fn signature(&self) -> FunctionSignature {
74        FunctionSignature {
75            name: "MASS_ELECTRON",
76            category: FunctionCategory::Constant,
77            arg_count: ArgCount::Fixed(0),
78            description: "Alias for ME() - Returns the mass of an electron in kg",
79            returns: "FLOAT",
80            examples: vec!["SELECT MASS_ELECTRON()"],
81        }
82    }
83
84    fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
85        self.validate_args(args)?;
86        Ok(DataValue::Float(9.1093837015e-31))
87    }
88}
89
90/// TAU constant (2π)
91pub struct TauFunction;
92
93impl SqlFunction for TauFunction {
94    fn signature(&self) -> FunctionSignature {
95        FunctionSignature {
96            name: "TAU",
97            category: FunctionCategory::Constant,
98            arg_count: ArgCount::Fixed(0),
99            description: "Returns tau (τ = 2π = 6.28318...)",
100            returns: "FLOAT",
101            examples: vec!["SELECT TAU()", "SELECT radius * TAU() as circumference"],
102        }
103    }
104
105    fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
106        self.validate_args(args)?;
107        Ok(DataValue::Float(2.0 * std::f64::consts::PI))
108    }
109}
110
111/// PHI constant (Golden ratio)
112pub struct PhiFunction;
113
114impl SqlFunction for PhiFunction {
115    fn signature(&self) -> FunctionSignature {
116        FunctionSignature {
117            name: "PHI",
118            category: FunctionCategory::Constant,
119            arg_count: ArgCount::Fixed(0),
120            description: "Returns the golden ratio φ (1.61803...)",
121            returns: "FLOAT",
122            examples: vec!["SELECT PHI()", "SELECT width * PHI() as golden_height"],
123        }
124    }
125
126    fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
127        self.validate_args(args)?;
128        Ok(DataValue::Float(1.618033988749895)) // (1 + sqrt(5)) / 2
129    }
130}
131
132/// HBAR constant (Reduced Planck constant)
133pub struct HbarFunction;
134
135impl SqlFunction for HbarFunction {
136    fn signature(&self) -> FunctionSignature {
137        FunctionSignature {
138            name: "HBAR",
139            category: FunctionCategory::Constant,
140            arg_count: ArgCount::Fixed(0),
141            description: "Returns the reduced Planck constant ħ in J⋅s (1.055 × 10⁻³⁴)",
142            returns: "FLOAT",
143            examples: vec!["SELECT HBAR()", "SELECT HBAR() / (2 * PI()) as h_bar"],
144        }
145    }
146
147    fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
148        self.validate_args(args)?;
149        Ok(DataValue::Float(1.054571817e-34))
150    }
151}
152
153// 10,000 digits of π (from MIT's pi-billion.txt)
154// Remove decimal point for storage efficiency
155const PI_DIGITS: &str = include_str!("pi_10000_digits.txt");
156
157/// PI_DIGITS function - Returns π to arbitrary precision as a string
158pub struct PiDigitsFunction;
159
160impl SqlFunction for PiDigitsFunction {
161    fn signature(&self) -> FunctionSignature {
162        FunctionSignature {
163            name: "PI_DIGITS",
164            category: FunctionCategory::Constant,
165            arg_count: ArgCount::Fixed(1),
166            description: "Returns π (pi) to N decimal places as a string (up to 10,000 digits)",
167            returns: "STRING",
168            examples: vec![
169                "SELECT PI_DIGITS(10)",
170                "SELECT PI_DIGITS(100)",
171                "SELECT PI_DIGITS(1000)",
172            ],
173        }
174    }
175
176    fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
177        self.validate_args(args)?;
178
179        let places = match &args[0] {
180            DataValue::Integer(n) => *n as usize,
181            DataValue::Float(f) => *f as usize,
182            _ => anyhow::bail!("PI_DIGITS requires an integer argument"),
183        };
184
185        if places == 0 {
186            return Ok(DataValue::String("3".to_string()));
187        }
188
189        if places > 10000 {
190            anyhow::bail!("PI_DIGITS: Maximum 10,000 decimal places supported");
191        }
192
193        // PI_DIGITS contains "3." followed by digits
194        // We need to return "3." + first N digits after decimal point
195        let result = if places <= PI_DIGITS.len() - 2 {
196            // -2 for "3."
197            PI_DIGITS[..2 + places].to_string()
198        } else {
199            PI_DIGITS.to_string()
200        };
201
202        Ok(DataValue::String(result))
203    }
204}
205
206/// PI_DIGIT function - Returns the Nth decimal digit of π
207pub struct PiDigitFunction;
208
209impl SqlFunction for PiDigitFunction {
210    fn signature(&self) -> FunctionSignature {
211        FunctionSignature {
212            name: "PI_DIGIT",
213            category: FunctionCategory::Constant,
214            arg_count: ArgCount::Fixed(1),
215            description: "Returns the Nth decimal digit of π (1-indexed, up to 10,000 digits)",
216            returns: "INTEGER",
217            examples: vec![
218                "SELECT PI_DIGIT(1)",
219                "SELECT PI_DIGIT(10)",
220                "SELECT n, PI_DIGIT(n) FROM RANGE(1, 10)",
221            ],
222        }
223    }
224
225    fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
226        self.validate_args(args)?;
227
228        let position = match &args[0] {
229            DataValue::Integer(n) => *n as usize,
230            DataValue::Float(f) => *f as usize,
231            _ => anyhow::bail!("PI_DIGIT requires an integer argument"),
232        };
233
234        if position == 0 {
235            anyhow::bail!("PI_DIGIT: Position must be >= 1 (1-indexed)");
236        }
237
238        if position > 10000 {
239            anyhow::bail!("PI_DIGIT: Maximum position is 10,000");
240        }
241
242        // PI_DIGITS contains "3.1415926535..."
243        // Position 1 = first digit after decimal point = index 2 (after "3.")
244        let char_index = 1 + position; // Skip "3." (2 chars) but we want 1-indexed
245
246        if let Some(ch) = PI_DIGITS.chars().nth(char_index) {
247            if let Some(digit) = ch.to_digit(10) {
248                return Ok(DataValue::Integer(digit as i64));
249            }
250        }
251
252        anyhow::bail!("PI_DIGIT: Could not extract digit at position {}", position)
253    }
254}
255
256#[cfg(test)]
257mod tests {
258    use super::*;
259
260    #[test]
261    fn test_pi_function() {
262        let func = PiFunction;
263        let result = func.evaluate(&[]).unwrap();
264        match result {
265            DataValue::Float(val) => assert!((val - std::f64::consts::PI).abs() < 1e-10),
266            _ => panic!("Expected Float"),
267        }
268    }
269
270    #[test]
271    fn test_pi_with_args_fails() {
272        let func = PiFunction;
273        let result = func.evaluate(&[DataValue::Float(1.0)]);
274        assert!(result.is_err());
275    }
276
277    #[test]
278    fn test_e_function() {
279        let func = EFunction;
280        let result = func.evaluate(&[]).unwrap();
281        match result {
282            DataValue::Float(val) => assert!((val - std::f64::consts::E).abs() < 1e-10),
283            _ => panic!("Expected Float"),
284        }
285    }
286
287    #[test]
288    fn test_me_function() {
289        let func = MeFunction;
290        let result = func.evaluate(&[]).unwrap();
291        match result {
292            DataValue::Float(val) => assert!((val - 9.1093837015e-31).abs() < 1e-40),
293            _ => panic!("Expected Float"),
294        }
295    }
296}