sql_cli/sql/functions/
geometry.rs

1use anyhow::{anyhow, Result};
2use std::f64::consts::PI;
3
4use crate::data::datatable::DataValue;
5use crate::sql::functions::{ArgCount, FunctionCategory, FunctionSignature, SqlFunction};
6
7/// Pythagorean theorem: c = sqrt(a^2 + b^2)
8pub struct PythagorasFunction;
9
10impl SqlFunction for PythagorasFunction {
11    fn signature(&self) -> FunctionSignature {
12        FunctionSignature {
13            name: "PYTHAGORAS",
14            category: FunctionCategory::Mathematical,
15            arg_count: ArgCount::Fixed(2),
16            description: "Calculate hypotenuse using Pythagorean theorem",
17            returns: "Float (hypotenuse length)",
18            examples: vec!["PYTHAGORAS(3, 4) = 5.0", "PYTHAGORAS(5, 12) = 13.0"],
19        }
20    }
21
22    fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
23        let a = match &args[0] {
24            DataValue::Integer(i) => *i as f64,
25            DataValue::Float(f) => *f,
26            DataValue::Null => return Ok(DataValue::Null),
27            _ => return Err(anyhow!("PYTHAGORAS requires numeric arguments")),
28        };
29
30        let b = match &args[1] {
31            DataValue::Integer(i) => *i as f64,
32            DataValue::Float(f) => *f,
33            DataValue::Null => return Ok(DataValue::Null),
34            _ => return Err(anyhow!("PYTHAGORAS requires numeric arguments")),
35        };
36
37        Ok(DataValue::Float((a * a + b * b).sqrt()))
38    }
39}
40
41/// Area of a circle: A = π * r^2
42pub struct CircleAreaFunction;
43
44impl SqlFunction for CircleAreaFunction {
45    fn signature(&self) -> FunctionSignature {
46        FunctionSignature {
47            name: "CIRCLE_AREA",
48            category: FunctionCategory::Mathematical,
49            arg_count: ArgCount::Fixed(1),
50            description: "Calculate area of a circle given radius",
51            returns: "Float (area)",
52            examples: vec!["CIRCLE_AREA(1) = 3.14159...", "CIRCLE_AREA(5) = 78.5398..."],
53        }
54    }
55
56    fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
57        let radius = match &args[0] {
58            DataValue::Integer(i) => *i as f64,
59            DataValue::Float(f) => *f,
60            DataValue::Null => return Ok(DataValue::Null),
61            _ => return Err(anyhow!("CIRCLE_AREA requires a numeric radius")),
62        };
63
64        Ok(DataValue::Float(PI * radius * radius))
65    }
66}
67
68/// Circumference of a circle: C = 2 * π * r
69pub struct CircleCircumferenceFunction;
70
71impl SqlFunction for CircleCircumferenceFunction {
72    fn signature(&self) -> FunctionSignature {
73        FunctionSignature {
74            name: "CIRCLE_CIRCUMFERENCE",
75            category: FunctionCategory::Mathematical,
76            arg_count: ArgCount::Fixed(1),
77            description: "Calculate circumference of a circle given radius",
78            returns: "Float (circumference)",
79            examples: vec![
80                "CIRCLE_CIRCUMFERENCE(1) = 6.28318...",
81                "CIRCLE_CIRCUMFERENCE(5) = 31.4159...",
82            ],
83        }
84    }
85
86    fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
87        let radius = match &args[0] {
88            DataValue::Integer(i) => *i as f64,
89            DataValue::Float(f) => *f,
90            DataValue::Null => return Ok(DataValue::Null),
91            _ => return Err(anyhow!("CIRCLE_CIRCUMFERENCE requires a numeric radius")),
92        };
93
94        Ok(DataValue::Float(2.0 * PI * radius))
95    }
96}
97
98/// Volume of a sphere: V = (4/3) * π * r^3
99pub struct SphereVolumeFunction;
100
101impl SqlFunction for SphereVolumeFunction {
102    fn signature(&self) -> FunctionSignature {
103        FunctionSignature {
104            name: "SPHERE_VOLUME",
105            category: FunctionCategory::Mathematical,
106            arg_count: ArgCount::Fixed(1),
107            description: "Calculate volume of a sphere given radius",
108            returns: "Float (volume)",
109            examples: vec![
110                "SPHERE_VOLUME(1) = 4.18879...",
111                "SPHERE_VOLUME(3) = 113.097...",
112            ],
113        }
114    }
115
116    fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
117        let radius = match &args[0] {
118            DataValue::Integer(i) => *i as f64,
119            DataValue::Float(f) => *f,
120            DataValue::Null => return Ok(DataValue::Null),
121            _ => return Err(anyhow!("SPHERE_VOLUME requires a numeric radius")),
122        };
123
124        Ok(DataValue::Float(
125            (4.0 / 3.0) * PI * radius * radius * radius,
126        ))
127    }
128}
129
130/// Surface area of a sphere: A = 4 * π * r^2
131pub struct SphereSurfaceAreaFunction;
132
133impl SqlFunction for SphereSurfaceAreaFunction {
134    fn signature(&self) -> FunctionSignature {
135        FunctionSignature {
136            name: "SPHERE_SURFACE_AREA",
137            category: FunctionCategory::Mathematical,
138            arg_count: ArgCount::Fixed(1),
139            description: "Calculate surface area of a sphere given radius",
140            returns: "Float (surface area)",
141            examples: vec![
142                "SPHERE_SURFACE_AREA(1) = 12.5663...",
143                "SPHERE_SURFACE_AREA(3) = 113.097...",
144            ],
145        }
146    }
147
148    fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
149        let radius = match &args[0] {
150            DataValue::Integer(i) => *i as f64,
151            DataValue::Float(f) => *f,
152            DataValue::Null => return Ok(DataValue::Null),
153            _ => return Err(anyhow!("SPHERE_SURFACE_AREA requires a numeric radius")),
154        };
155
156        Ok(DataValue::Float(4.0 * PI * radius * radius))
157    }
158}
159
160/// Area of a triangle using Heron's formula
161pub struct TriangleAreaFunction;
162
163impl SqlFunction for TriangleAreaFunction {
164    fn signature(&self) -> FunctionSignature {
165        FunctionSignature {
166            name: "TRIANGLE_AREA",
167            category: FunctionCategory::Mathematical,
168            arg_count: ArgCount::Fixed(3),
169            description: "Calculate area of a triangle given three side lengths (Heron's formula)",
170            returns: "Float (area)",
171            examples: vec![
172                "TRIANGLE_AREA(3, 4, 5) = 6.0",
173                "TRIANGLE_AREA(5, 5, 6) = 12.0",
174            ],
175        }
176    }
177
178    fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
179        let a = match &args[0] {
180            DataValue::Integer(i) => *i as f64,
181            DataValue::Float(f) => *f,
182            DataValue::Null => return Ok(DataValue::Null),
183            _ => return Err(anyhow!("TRIANGLE_AREA requires numeric side lengths")),
184        };
185
186        let b = match &args[1] {
187            DataValue::Integer(i) => *i as f64,
188            DataValue::Float(f) => *f,
189            DataValue::Null => return Ok(DataValue::Null),
190            _ => return Err(anyhow!("TRIANGLE_AREA requires numeric side lengths")),
191        };
192
193        let c = match &args[2] {
194            DataValue::Integer(i) => *i as f64,
195            DataValue::Float(f) => *f,
196            DataValue::Null => return Ok(DataValue::Null),
197            _ => return Err(anyhow!("TRIANGLE_AREA requires numeric side lengths")),
198        };
199
200        // Check triangle inequality
201        if a + b <= c || a + c <= b || b + c <= a {
202            return Err(anyhow!(
203                "Invalid triangle: sides do not satisfy triangle inequality"
204            ));
205        }
206
207        // Heron's formula: s = (a + b + c) / 2, Area = sqrt(s * (s-a) * (s-b) * (s-c))
208        let s = (a + b + c) / 2.0;
209        let area = (s * (s - a) * (s - b) * (s - c)).sqrt();
210
211        Ok(DataValue::Float(area))
212    }
213}
214
215/// Distance between two points in 2D space
216pub struct Distance2DFunction;
217
218impl SqlFunction for Distance2DFunction {
219    fn signature(&self) -> FunctionSignature {
220        FunctionSignature {
221            name: "DISTANCE_2D",
222            category: FunctionCategory::Mathematical,
223            arg_count: ArgCount::Fixed(4),
224            description: "Calculate distance between two points (x1, y1) and (x2, y2)",
225            returns: "Float (distance)",
226            examples: vec![
227                "DISTANCE_2D(0, 0, 3, 4) = 5.0",
228                "DISTANCE_2D(1, 1, 4, 5) = 5.0",
229            ],
230        }
231    }
232
233    fn evaluate(&self, args: &[DataValue]) -> Result<DataValue> {
234        let x1 = match &args[0] {
235            DataValue::Integer(i) => *i as f64,
236            DataValue::Float(f) => *f,
237            DataValue::Null => return Ok(DataValue::Null),
238            _ => return Err(anyhow!("DISTANCE_2D requires numeric coordinates")),
239        };
240
241        let y1 = match &args[1] {
242            DataValue::Integer(i) => *i as f64,
243            DataValue::Float(f) => *f,
244            DataValue::Null => return Ok(DataValue::Null),
245            _ => return Err(anyhow!("DISTANCE_2D requires numeric coordinates")),
246        };
247
248        let x2 = match &args[2] {
249            DataValue::Integer(i) => *i as f64,
250            DataValue::Float(f) => *f,
251            DataValue::Null => return Ok(DataValue::Null),
252            _ => return Err(anyhow!("DISTANCE_2D requires numeric coordinates")),
253        };
254
255        let y2 = match &args[3] {
256            DataValue::Integer(i) => *i as f64,
257            DataValue::Float(f) => *f,
258            DataValue::Null => return Ok(DataValue::Null),
259            _ => return Err(anyhow!("DISTANCE_2D requires numeric coordinates")),
260        };
261
262        let dx = x2 - x1;
263        let dy = y2 - y1;
264        Ok(DataValue::Float((dx * dx + dy * dy).sqrt()))
265    }
266}
267
268#[cfg(test)]
269mod tests {
270    use super::*;
271
272    #[test]
273    fn test_pythagoras() {
274        let func = PythagorasFunction;
275
276        // 3-4-5 triangle
277        let result = func
278            .evaluate(&[DataValue::Integer(3), DataValue::Integer(4)])
279            .unwrap();
280        assert_eq!(result, DataValue::Float(5.0));
281
282        // 5-12-13 triangle
283        let result = func
284            .evaluate(&[DataValue::Integer(5), DataValue::Integer(12)])
285            .unwrap();
286        assert_eq!(result, DataValue::Float(13.0));
287    }
288
289    #[test]
290    fn test_circle_area() {
291        let func = CircleAreaFunction;
292
293        // Unit circle
294        let result = func.evaluate(&[DataValue::Integer(1)]).unwrap();
295        if let DataValue::Float(area) = result {
296            assert!((area - PI).abs() < 1e-10);
297        } else {
298            panic!("Expected Float result");
299        }
300    }
301
302    #[test]
303    fn test_sphere_volume() {
304        let func = SphereVolumeFunction;
305
306        // Unit sphere volume = 4/3 * π
307        let result = func.evaluate(&[DataValue::Integer(1)]).unwrap();
308        if let DataValue::Float(volume) = result {
309            let expected = (4.0 / 3.0) * PI;
310            assert!((volume - expected).abs() < 1e-10);
311        } else {
312            panic!("Expected Float result");
313        }
314    }
315
316    #[test]
317    fn test_triangle_area() {
318        let func = TriangleAreaFunction;
319
320        // 3-4-5 right triangle has area 6
321        let result = func
322            .evaluate(&[
323                DataValue::Integer(3),
324                DataValue::Integer(4),
325                DataValue::Integer(5),
326            ])
327            .unwrap();
328        assert_eq!(result, DataValue::Float(6.0));
329
330        // Invalid triangle
331        let result = func.evaluate(&[
332            DataValue::Integer(1),
333            DataValue::Integer(2),
334            DataValue::Integer(10),
335        ]);
336        assert!(result.is_err());
337    }
338
339    #[test]
340    fn test_distance_2d() {
341        let func = Distance2DFunction;
342
343        // Distance from origin to (3, 4) is 5
344        let result = func
345            .evaluate(&[
346                DataValue::Integer(0),
347                DataValue::Integer(0),
348                DataValue::Integer(3),
349                DataValue::Integer(4),
350            ])
351            .unwrap();
352        assert_eq!(result, DataValue::Float(5.0));
353    }
354}