1pub mod error;
2pub mod tests;
3
4pub mod str {
5 pub mod consts {
6 pub const OS_PATH_SEPARATOR: &str = if cfg!(windows) { r"\" } else { "/" };
7 pub const NUMS_CHARSET: &str = r"0123456789";
8 pub const FLOAT_CHARSET: &str = r"0.-123456789";
9 pub const ABS_FLOAT_CHARSET: &str = r".0123456789";
10 }
11 #[inline]
13 pub fn trunc(input: &str, length: u8) -> &str {
14 &input[..{
15 if length as usize > input.len() {
16 input.len()
17 } else {
18 length as usize
19 }
20 }]
21 }
22}
23
24pub mod usize {
25 pub mod consts {
26 pub const APPROX_CM_IN_ARC_SECOND: usize = 3087usize;
27 pub const ARC_SECONDS_IN_360_DEGREES: usize = 1296000usize;
28 pub const ARC_SECONDS_IN_360_DEGREES_INDEXED: usize = 1295999usize;
29 pub const ARC_SECONDS_IN_180_DEGREES: usize = 648000usize;
30 pub const ARC_SECONDS_IN_180_DEGREES_INDEXED: usize = 647999usize;
31 }
32}
33
34pub mod f64 {
35 use crate::error::{Message, Warning};
36
37 use self::consts::{DEG_TO_RAD, RAD_TO_DEG};
38
39 pub mod consts {
40 pub const LATITUDE_LIMIT: f64 = 90.0f64; pub const LONGITUDE_LIMIT: f64 = 180.0f64; pub const ARC_SECONDS_IN_360_DEGREES: f64 = 1296000.0f64;
43 pub const ARC_SECONDS_IN_180_DEGREES: f64 = 648000.0f64;
44 pub const EARTH_RADIUS_KM: f64 = 6378.137f64;
45 pub const EARTH_RADIUS_M: f64 = 6378137.0f64;
46 pub const DEG_TO_RAD: f64 = 0.017453292519943295f64;
47 pub const RAD_TO_DEG: f64 = 57.29577951308232f64;
48 }
49
50 #[inline]
52 pub fn rhs_exact(input: f64) -> f64 {
53 let input_string: String = input.to_string();
54 if let Some((_, rhs_str)) = input_string.split_once('.') {
55 format!("0.{}", rhs_str).parse::<f64>().unwrap()
56 } else {
57 0.0f64
58 }
59 }
60
61 #[inline]
63 pub fn split(input: f64) -> (f64, f64) {
64 (input.trunc(), input.fract())
65 }
66
67 #[inline]
69 pub fn split_abs(input: f64) -> (f64, f64) {
70 (input.abs().trunc(), input.abs().fract())
71 }
72
73 #[inline]
75 pub fn to_radians(input_degrees: f64) -> f64 {
76 input_degrees * DEG_TO_RAD
77 }
78
79 #[inline]
81 pub fn to_degrees(input_radians: f64) -> f64 {
82 input_radians * RAD_TO_DEG
83 }
84
85 #[inline]
87 pub fn normalise(input: f64, min: f64, max: f64) -> f64 {
88 (input - min) / (max - min)
89 }
90
91 #[inline]
93 pub fn normalised_to_index(input: f64, max: usize) -> usize {
94 (max as f64 * input) as usize
95 }
96
97 #[inline]
99 pub fn indexify_lat(lat: f64) -> f64 {
100 lat + 90.0f64
101 }
102
103 #[inline]
105 pub fn indexify_long(long: f64) -> f64 {
106 long + 180.0f64
107 }
108
109 #[inline]
111 pub fn indexify_lat_long(lat: f64, long: f64) -> (f64, f64) {
112 (indexify_lat(lat), indexify_long(long))
113 }
114
115 #[inline]
118 pub fn trunc(input: f64, decimal_places: u8) -> f64 {
119 let factor: f64 = 10usize.pow(decimal_places as u32) as f64;
120 let output_abs: f64 = (input.abs() * factor).floor() / factor;
121 output_abs.copysign(input)
122 }
123
124 #[inline]
125 #[allow(clippy::nonminimal_bool)]
126 pub fn trunc_safe(input: f64, decimal_places: u8) -> Result<f64, Warning> {
127 let mut safe: bool = true;
128 let factor: f64 = 10usize.pow({
129 if !(decimal_places > 19u8) {
130 decimal_places as u32
131 } else {
132 safe = false;
133 19u32
134 }
135 }) as f64;
136 let output_abs: f64 = (input.abs() * factor).floor() / factor;
137 let output: f64 = output_abs.copysign(input);
138 match safe {
139 true => Ok(output),
140 false => {
141 Err(Warning::F64(output, Message::Max19DecimalPlaces))
146 }
147 }
148 }
149
150 #[inline]
153 pub fn trunc_exact(input: f64, decimal_places: u8) -> f64 {
154 let input_string: String = input.to_string();
155 if let Some((lhs_str, rhs_str)) = input_string.split_once('.') {
156 let rhs_string: String = rhs_str
157 .chars()
158 .into_iter()
159 .take(decimal_places as usize)
160 .collect();
161 format!("{}.{}", lhs_str, rhs_string)
162 .parse::<f64>()
163 .unwrap()
164 } else {
165 input.trunc()
166 }
167 }
168
169 #[inline]
170 pub fn approx_equal_f64(a: f64, b: f64, decimal_places: u8) -> bool {
171 let factor: f64 = 10usize.pow(decimal_places as u32) as f64;
172 (a * factor).trunc() == (b * factor).trunc()
173 }
174
175 #[inline]
176 #[allow(clippy::unnecessary_unwrap)]
177 pub fn approx_equal_infallible_f64(a: f64, b: f64, decimal_places: u8) -> bool {
179 if a as isize != b as isize {
181 return false;
182 }
183
184 let a_string: String = a.to_string();
185 let b_string: String = b.to_string();
186
187 let a_string_split: Option<(&str, &str)> = a_string.split_once('.');
188 let b_string_split: Option<(&str, &str)> = b_string.split_once('.');
189 if a_string_split.is_some() && b_string_split.is_some() {
190 let (_, a_rhs) = a_string_split.unwrap();
191 let (_, b_rhs) = b_string_split.unwrap();
192 return crate::str::trunc(a_rhs, decimal_places)
193 == crate::str::trunc(b_rhs, decimal_places);
194 } else if a_string_split.is_none() && b_string_split.is_none() {
195 return true;
196 }
197
198 false
199 }
200}
201
202pub mod f32 {
203 use self::consts::{DEG_TO_RAD, RAD_TO_DEG};
204
205 pub mod consts {
206 pub const LATITUDE_LIMIT: f32 = 90.0f32; pub const LONGITUDE_LIMIT: f32 = 180.0f32; pub const ARC_SECONDS_IN_360_DEGREES: f32 = 1296000.0f32;
209 pub const ARC_SECONDS_IN_180_DEGREES: f32 = 648000.0f32;
210 pub const EARTH_RADIUS_KM: f32 = 6378.137f32;
211 pub const EARTH_RADIUS_M: f32 = 6378137.0f32;
212 pub const DEG_TO_RAD: f32 = 0.017453292f32;
213 pub const RAD_TO_DEG: f32 = 57.29578f32; }
215
216 #[inline]
218 pub fn rhs_exact(input: f32) -> f32 {
219 let input_string: String = input.to_string();
220 if let Some((_, rhs_str)) = input_string.split_once('.') {
221 format!("0.{}", rhs_str).parse::<f32>().unwrap()
222 } else {
223 0.0f32
224 }
225 }
226
227 #[inline]
229 pub fn split(input: f32) -> (f32, f32) {
230 (input.trunc(), input.fract())
231 }
232
233 #[inline]
235 pub fn split_abs(input: f32) -> (f32, f32) {
236 (input.abs().trunc(), input.abs().fract())
237 }
238
239 #[inline]
241 pub fn to_radians(input_degrees: f32) -> f32 {
242 input_degrees * DEG_TO_RAD
243 }
244
245 #[inline]
247 pub fn to_degrees(input_radians: f32) -> f32 {
248 input_radians * RAD_TO_DEG
249 }
250
251 #[inline]
253 pub fn normalise(input: f32, min: f32, max: f32) -> f32 {
254 (input - min) / (max - min)
255 }
256
257 #[inline]
259 pub fn normalised_to_index(input: f32, max: usize) -> usize {
260 (max as f32 * input) as usize
261 }
262
263 #[inline]
265 pub fn indexify_lat(lat: f32) -> f32 {
266 lat + 90.0
267 }
268
269 #[inline]
271 pub fn indexify_long(long: f32) -> f32 {
272 long + 180.0
273 }
274
275 #[inline]
277 pub fn indexify_lat_long(lat: f32, long: f32) -> (f32, f32) {
278 (indexify_lat(lat), indexify_long(long))
279 }
280
281 #[inline]
284 pub fn trunc(input: f32, decimal_places: u8) -> f32 {
285 let factor: f32 = 10usize.pow(decimal_places as u32) as f32;
286 let output_abs: f32 = (input.abs() * factor).floor() / factor;
287 output_abs.copysign(input)
288 }
289
290 #[inline]
291 #[allow(clippy::nonminimal_bool)]
292 pub fn trunc_safe(input: f32, decimal_places: u8) -> Result<f32, crate::error::Warning> {
293 let mut safe: bool = true;
294 let factor: f32 = 10usize.pow({
295 if !(decimal_places > 19u8) {
296 decimal_places as u32
297 } else {
298 safe = false;
299 19u32
300 }
301 }) as f32;
302 let output_abs: f32 = (input.abs() * factor).floor() / factor;
303 let output: f32 = output_abs.copysign(input);
304 match safe {
305 true => Ok(output),
306 false => {
307 Err(crate::error::Warning::F32(
312 output,
313 crate::error::Message::Max19DecimalPlaces,
314 ))
315 }
316 }
317 }
318
319 #[inline]
322 pub fn trunc_exact(input: f32, decimal_places: u8) -> f32 {
323 let input_string: String = input.to_string();
324 if let Some((lhs_str, rhs_str)) = input_string.split_once('.') {
325 let rhs_string: String = rhs_str
326 .chars()
327 .into_iter()
328 .take(decimal_places as usize)
329 .collect();
330 format!("{}.{}", lhs_str, rhs_string)
331 .parse::<f32>()
332 .unwrap()
333 } else {
334 input.trunc()
335 }
336 }
337
338 #[inline]
339 pub fn approx_equal_f32(a: f32, b: f32, decimal_places: u8) -> bool {
340 let factor: f32 = 10usize.pow(decimal_places as u32) as f32;
341 (a * factor).trunc() == (b * factor).trunc()
342 }
343
344 #[inline]
345 #[allow(clippy::unnecessary_unwrap)]
346 pub fn approx_equal_infallible_f32(a: f32, b: f32, decimal_places: u8) -> bool {
347 if a as isize != b as isize {
349 return false;
350 }
351
352 let a_string: String = a.to_string();
353 let b_string: String = b.to_string();
354
355 let a_string_split: Option<(&str, &str)> = a_string.split_once('.');
356 let b_string_split: Option<(&str, &str)> = b_string.split_once('.');
357 if a_string_split.is_some() && b_string_split.is_some() {
358 let (_, a_rhs) = a_string_split.unwrap();
359 let (_, b_rhs) = b_string_split.unwrap();
360 return crate::str::trunc(a_rhs, decimal_places)
361 == crate::str::trunc(b_rhs, decimal_places);
362 } else if a_string_split.is_none() && b_string_split.is_none() {
363 return true;
364 }
365
366 false
367 }
368}