1use anyhow::{anyhow, Result};
2use std::f64::consts::PI;
3
4use crate::data::datatable::DataValue;
5use crate::sql::functions::{ArgCount, FunctionCategory, FunctionSignature, SqlFunction};
6
7pub 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
41pub 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
68pub 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
98pub 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
130pub 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
160pub 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 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 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
215pub 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 let result = func
278 .evaluate(&[DataValue::Integer(3), DataValue::Integer(4)])
279 .unwrap();
280 assert_eq!(result, DataValue::Float(5.0));
281
282 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 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 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 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 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 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}