srmc/lib.rs
1//! # SRMC (Small Rust Math Crate)
2//!
3//! `srmc` is a collection of mathematical methods, functions, and other useful tools. The goal is to make a stand alone math focused crate that can be used for a wide variety of applications.
4//! Will be continuously updated and improved as I imporve with the Rust langauge.
5//!
6
7use core::panic;
8/// Creates a vector in numerical order from start to end with steps + 1 elements.
9/// NOTE: Elements of vector should be evenly spaced, but often times last two are not. Working on a fix for it, but if evenly spaced values is vital you may need to remove the final value or choose a different step size.
10///
11/// # Examples
12///
13/// ```
14/// let vec1: Vec<f32> = vec![0.0, 1.0, 2.0, 3.0, 4.0, 5.0];
15/// let vec2 = srmc::create_sequential_vec(0.0, 5.0, 5);
16///
17/// assert_eq!(vec1, vec2);
18/// ```
19/// ```
20/// let vec3: Vec<f32> = vec![0.0, 1.5, 3.0, 4.5, 6.0, 7.5, 9.0, 10.5, 12.0, 13.5, 15.0];
21/// let vec4 = srmc::create_sequential_vec(0.0, 15.0, 10);
22///
23/// assert_eq!(vec3, vec4);
24/// ```
25pub fn create_sequential_vec(start: f32, end: f32, steps: i32) -> Vec<f32> {
26 let mut sequential_vec = Vec::new();
27 let interval = (end - start) / steps as f32;
28 let mut current_num = start;
29 while current_num < end {
30 sequential_vec.push(current_num);
31 current_num = current_num + interval;
32 }
33 sequential_vec.push(end);
34 return sequential_vec;
35}
36
37/// Uses Simpson's Method to evaluate a definite integral. Function to be integrated must take on parameter of type f32, and return a value of type f32.
38/// See the example functions (sin_x and x_cubed) for examples on how to format input.
39///
40/// # Examples
41///
42/// ```
43/// let calculated_ans1 = srmc::simpsons(&srmc::x_cubed, 2.0, 10.0, 4);
44/// let known_ans1 = 2496.0;
45///
46/// assert_eq!(calculated_ans1, known_ans1);
47/// ```
48/// Since this method is not exact, in the following example we are checking the function against the expected output, not the mathematically correct value of 2.
49/// ```
50/// let calculated_ans2 = srmc::simpsons(&srmc::sin_x, 0.0, std::f32::consts::PI, 10);
51/// let known_ans2 = 2.0001094;
52///
53/// assert_eq!(calculated_ans2, known_ans2);
54///
55pub fn simpsons(f: &dyn Fn(f32) -> f32, lower: f32, upper: f32, steps: i32) -> f32 {
56 if lower > upper {
57 panic!("Upper bound must be greater than lower bound.");
58 }
59 if steps % 2 != 0 {
60 panic!("Steps must be an even number.")
61 }
62 let values = create_sequential_vec(lower, upper, steps);
63 let mut ans: f32 = 0.0;
64 let mut index = 0;
65 let max_index = values.len() - 1;
66 while index < values.len() {
67 if index == 0 || index == max_index {
68 ans = ans + f(values[index]);
69 index = index + 1;
70 } else if index % 2 != 0 {
71 ans = ans + f(values[index]) * 4.0;
72 index = index + 1;
73 } else if index % 2 == 0 {
74 ans = ans + f(values[index]) * 2.0;
75 index = index + 1;
76 }
77 }
78 ans = (((upper - lower) / steps as f32) * ans) / 3.0;
79 return ans;
80}
81
82/// Uses The Trapezoidal Rule to evaluate a definite integral. Function to be integrated must take on parameter of type f32, and return a value of type f32.
83/// See the example functions (sin_x and x_cubed) for examples on how to format input.
84///
85/// # Examples
86///
87/// Integrating the function x cubed from 2.0 to 10.0 with a step size of 10000.
88/// ```
89/// let calculated_val = srmc::trapzoidal_rule(&srmc::x_cubed, 2.0, 10.0, 10000);
90/// ```
91/// Integrating the function sin(x) from 2.0 to 10.0 with a step size of 10000.
92/// ```
93/// let calculated_val = srmc::trapzoidal_rule(&srmc::sin_x, 2.0, 10.0, 10000);
94/// ```
95pub fn trapzoidal_rule(f: &dyn Fn(f32) -> f32, lower: f32, upper: f32, steps: i32) -> f32 {
96 if lower > upper {
97 panic!("Upper bound must be greater than lower bound.");
98 }
99 if steps % 2 != 0 {
100 panic!("Steps must be an even number.")
101 }
102 let values = create_sequential_vec(lower, upper, steps);
103 let mut ans: f32 = 0.0;
104 let mut index = 0;
105 let max_index = values.len() - 1;
106 while index < values.len() {
107 if index == 0 || index == max_index {
108 ans = ans + f(values[index]);
109 index = index + 1;
110 } else {
111 ans = ans + f(values[index]) * 2.0;
112 index = index + 1;
113 }
114 }
115 ans = ((upper - lower) / (2.0 * steps as f32)) * ans;
116 return ans;
117}
118
119/// Function to calculate the relative error between two values
120///
121/// # Example
122///
123/// ```
124/// let known_ans = 2.0;
125/// let calculated_ans = srmc::simpsons(&srmc::sin_x, 0.0, std::f32::consts::PI, 10);
126/// let rel_error = srmc::relative_error(calculated_ans, known_ans);
127/// println!("Relative error between known and calculated value is: {}", rel_error);
128/// ```
129///
130pub fn relative_error(observed_val: f32, true_val: f32) -> f32 {
131 return ((observed_val - true_val).abs()) / true_val;
132}
133
134/// Function to calculate the percentage error between two values
135///
136/// # Example
137///
138/// ```
139/// let known_ans = 2.0;
140/// let calculated_ans = srmc::simpsons(&srmc::sin_x, 0.0, std::f32::consts::PI, 10);
141/// let percentage_error = srmc::percentage_error(calculated_ans, known_ans);
142/// println!("Percentage error between known and calculated value is: {}%", percentage_error);
143/// ```
144///
145pub fn percentage_error(observed_val: f32, true_val: f32) -> f32 {
146 return (((observed_val - true_val).abs()) / true_val) * 100.0;
147}
148
149/// Example input function for x cubed
150pub fn x_cubed(x: f32) -> f32 {
151 return f32::powf(x, 3.0);
152}
153
154/// Example input function for sin(x)
155pub fn sin_x(x: f32) -> f32 {
156 return x.sin();
157}
158
159/// SRMC Matrix struct with various implementations
160///
161/// Note: When indexing a value or row/column of a Matrix, the numbering starts at zero. So for a 3x3 Matrix,
162/// the top row is the 0th row and the top left value is at position (0, 0).
163///
164/// It is possible to create a matrix that is invalid by having rows of different lengths, there are no checks to ensure
165/// that this is not the case when creating a new Matrix. If you're creating a Matrix directly from a vector of vectors and manually
166/// entering the dimensions, ensure all values are entered properly. Alternatively, there is a "create_matrix" function and
167/// "change_row"/"change_column" implemenations with checks to ensure a matrix is created/changed correctly. To check if a Matrix
168/// was made correctly use the "is_valid" implementation.
169///
170/// Example of creating a Matrix:
171/// ```
172/// let input: Vec<Vec<f32>> = vec![vec![1.0, 2.0], vec![3.0, 4.0]];
173/// let matrix = srmc::Matrix {
174/// elements: input,
175/// rows: 2,
176/// columns: 2,
177///};
178/// assert_eq!(true, matrix.is_valid());
179/// ```
180pub struct Matrix {
181 pub elements: Vec<Vec<f32>>,
182 pub rows: i32,
183 pub columns: i32,
184}
185
186impl Matrix {
187 /// Prints matrix in terminal
188 pub fn print(&self) {
189 for item in &self.elements {
190 println!("{:?}", item);
191 }
192 }
193 /// Returns the dimensions of the matrix as a tuple (rows, columns)
194 pub fn get_dimensions(&self) -> (i32, i32) {
195 return (self.rows, self.columns);
196 }
197
198 /// Checks if a Matrix has rows of equal length and correct values for number of rows and columns
199 pub fn is_valid(&self) -> bool {
200 let row_length = self.elements[0].len();
201 let mut i: usize = 1;
202 while i < self.elements.len() {
203 if self.elements[i].len() != row_length {
204 return false;
205 }
206 i = i + 1;
207 }
208 if self.elements.len() != self.rows as usize {
209 return false;
210 }
211 if self.elements[0].len() != self.columns as usize {
212 return false;
213 }
214 return true;
215 }
216
217 /// Returns the element of the matrix at the given position
218 pub fn index_val(&self, row: i32, column: i32) -> f32 {
219 if &(row + 1) > (&self.rows) || &(column + 1) > (&self.columns) {
220 panic!("Row and Column position outside the bounds matrix. Please provide a value for rows from 0 to {} and a value for columns from 0 to {}", self.rows - 1, self.columns - 1);
221 }
222 let row_num = &self.elements[row as usize];
223 return row_num[column as usize];
224 }
225
226 /// Changes the element of the matrix at the given position
227 pub fn change_val(&mut self, val: f32, row: i32, column: i32) {
228 if &(row + 1) > (&self.rows) || &(column + 1) > (&self.columns) {
229 panic!("Row and Column position outside the bounds matrix. Please provide a value for rows from 0 to {} and a value for columns from 0 to {}", self.rows - 1, self.columns - 1);
230 }
231 self.elements[row as usize][column as usize] = val;
232 }
233
234 /// Multiplies entire matrix by some value
235 pub fn multiply_by_value(&mut self, val: f32) {
236 let mut i: usize = 0;
237 while i < self.rows as usize {
238 let mut j = 0;
239 while j < self.columns as usize {
240 self.elements[i][j] = self.elements[i][j] * val;
241 j = j + 1;
242 }
243 i = i + 1;
244 }
245 }
246
247 /// Changes given row to input vector, input vector must match dimensions of the matrix
248 ///
249 /// Example:
250 /// ```
251 /// let mut matrix = srmc::create_matrix(2, 3);
252 /// let new_row = vec![1.0, 2.0, 3.0];
253 /// matrix.change_row(0, new_row);
254 /// ```
255 pub fn change_row(&mut self, row: i32, new_row: Vec<f32>) {
256 if new_row.len() != self.elements[row as usize].len() {
257 panic!("Input vector not matching matrix dimensions.");
258 }
259 self.elements[row as usize] = new_row;
260 }
261
262 /// Changes given column to input vector, input vector must match dimensions of the matrix
263 ///
264 /// Example:
265 /// ```
266 /// let mut matrix = srmc::create_matrix(2, 3);
267 /// let new_column = vec![1.0, 2.0];
268 /// matrix.change_column(0, new_column);
269 /// ```
270 pub fn change_column(&mut self, column: i32, new_column: Vec<f32>) {
271 if new_column.len() != self.elements.len() {
272 panic!("Input vector not matching matrix dimensions.");
273 }
274 let mut i: usize = 0;
275 while i < self.elements.len() {
276 self.elements[i][column as usize] = new_column[i];
277 i = i + 1;
278 }
279 }
280}
281
282/// Returns an SRMC Matrix swith provided dimensions. All elements of matrix are initially set to zero.
283///
284/// Creates 3x3 Matrix
285/// ```
286/// let matrix = srmc::create_matrix(3, 3);
287/// ```
288/// Creates 1x5 Matrix
289/// ```
290/// let matrix = srmc::create_matrix(1, 5);
291/// ```
292pub fn create_matrix(rows: i32, columns: i32) -> Matrix {
293 return Matrix {
294 elements: vec![vec![0.0; columns as usize]; rows as usize],
295 rows,
296 columns,
297 };
298}
299
300/// Returns an SRMC Matrix with provided dimensions. Elements of the matrix start at 1.0 and are incremented by 1.
301/// ```
302/// let matrix = srmc::create_numbered_matrix(2, 4);
303/// matrix.print();
304/// // The following matrix is printed when .print() is called:
305/// //[1.0, 2.0, 3.0, 4.0]
306/// //[5.0, 6.0, 7.0, 8.0]
307/// ```
308pub fn create_numbered_matrix(rows: i32, columns: i32) -> Matrix {
309 let mut num_matrix = create_matrix(rows, columns);
310 let mut val: f32 = 0.0;
311 let mut i: usize = 0;
312 while i < rows as usize {
313 let mut j: usize = 0;
314 while j < columns as usize {
315 val = val + 1.0;
316 num_matrix.elements[i][j] = val;
317 j = j + 1;
318 }
319 i = i + 1;
320 }
321 return num_matrix;
322}
323
324/// Returns an SRMC Matrix with diagonal values set to one.
325///
326/// Crates 3x3 identity matrix
327/// ```
328/// let matrix = srmc::create_identity_matrix(3);
329/// ```
330/// Creates 10x10 identity matrix
331/// ```
332/// let matrix = srmc::create_identity_matrix(10);
333/// ```
334pub fn create_identity_matrix(dimension: i32) -> Matrix {
335 let mut res_mat = create_matrix(dimension, dimension);
336 let mut i: usize = 0;
337 while i < dimension as usize {
338 res_mat.elements[i][i] = 1.0;
339 i = i + 1;
340 }
341 return res_mat;
342}
343
344/// Multiplies two Matricies together and returns the result as an SRMC Matrix.
345/// ```
346/// let matrix1 = srmc::create_numbered_matrix(3, 2);
347/// let matrix2 = srmc::create_numbered_matrix(2, 2);
348/// let matrix3 = srmc::matrix_multiplication(&matrix1, &matrix2);
349/// ```
350/// NOTE: Running this function keeps input Matricies unchanged and creates a new Matrix.
351/// If you want to run without creating a new Matrix do the following:
352/// ```
353/// let mut matrix1 = srmc::create_numbered_matrix(3, 2);
354/// let matrix2 = srmc::create_numbered_matrix(2, 2);
355/// matrix1 = srmc::matrix_multiplication(&matrix1, &matrix2);
356/// ```
357/// In the above code, matrix2 could have also been mutable and set equal to the result.
358pub fn matrix_multiplication(mat1: &Matrix, mat2: &Matrix) -> Matrix {
359 if mat1.columns != mat2.rows {
360 panic!("Matricies have incompatible dimensions.");
361 }
362 let mut res_mat = create_matrix(mat1.rows, mat2.columns);
363 let mut i: usize = 0;
364 while i < mat1.rows as usize {
365 let mut j: usize = 0;
366 while j < mat2.columns as usize {
367 let mut k: usize = 0;
368 while k < mat2.rows as usize {
369 res_mat.elements[i][j] += mat1.elements[i][k] * mat2.elements[k][j];
370 k = k + 1;
371 }
372 j = j + 1;
373 }
374 i = i + 1;
375 }
376 return res_mat;
377}
378
379/// Returns the determinant of a 2x2 Matrix.
380/// ```
381/// let matrix = srmc::create_numbered_matrix(2, 2);
382/// let determinant = srmc::matrix_2x2det(&matrix);
383/// ```
384pub fn matrix_2x2det(mat: &Matrix) -> f32 {
385 if mat.rows != 2 || mat.columns != 2 {
386 panic!("Matrix must be of size 2x2.");
387 }
388 return (mat.elements[0][0] * mat.elements[1][1]) - (mat.elements[0][1] * mat.elements[1][0]);
389}
390
391/// Returns the determinant of a 3x3 Matrix.
392/// ```
393/// let matrix = srmc::create_numbered_matrix(3, 3);
394/// let determinant = srmc::matrix_3x3det(&matrix);
395/// ```
396pub fn matrix_3x3det(mat: &Matrix) -> f32 {
397 if mat.rows != 3 || mat.columns != 3 {
398 panic!("Matrix must be of size 3x3.");
399 }
400 let val1: f32 =
401 (mat.elements[1][1] * mat.elements[2][2]) - (mat.elements[1][2] * mat.elements[2][1]);
402 let val2: f32 =
403 (mat.elements[1][0] * mat.elements[2][2]) - (mat.elements[1][2] * mat.elements[2][0]);
404 let val3: f32 =
405 (mat.elements[1][0] * mat.elements[2][1]) - (mat.elements[1][1] * mat.elements[2][0]);
406 return (mat.elements[0][0] * val1) - (mat.elements[0][1] * val2) + (mat.elements[0][2] * val3);
407}
408
409/// Adds two Matricies together and returns the result as an SRMC Matrix
410/// ```
411/// let matrix1 = srmc::create_numbered_matrix(3,3);
412/// let matrix2 = srmc::create_numbered_matrix(3,3);
413/// let matrix3 = srmc::matrix_addition(&matrix1, &matrix2);
414/// ```
415/// NOTE: Running this function keeps input Matricies unchanged and creates a new Matrix.
416/// If you want to run without creating a new Matrix do the following:
417/// ```
418/// let mut matrix1 = srmc::create_numbered_matrix(3,3);
419/// let matrix2 = srmc::create_numbered_matrix(3,3);
420/// matrix1 = srmc::matrix_addition(&matrix1, &matrix2);
421/// ```
422/// In the above code, matrix2 could have also been mutable and set equal to the result.
423pub fn matrix_addition(mat1: &Matrix, mat2: &Matrix) -> Matrix {
424 if mat1.rows != mat2.rows || mat1.columns != mat2.columns {
425 panic!("Matricies must be of the same size to perform matrix addition.");
426 }
427 let mut res_mat = create_matrix(mat1.rows, mat1.columns);
428
429 let mut i: usize = 0;
430 while i < mat1.rows as usize {
431 let mut j = 0;
432 while j < mat1.columns as usize {
433 res_mat.elements[i][j] = mat1.elements[i][j] + mat2.elements[i][j];
434 j = j + 1;
435 }
436 i = i + 1;
437 }
438 return res_mat;
439}
440
441/// Subtracts two Matricies and returns the result as an SRMC Matrix.
442///
443/// As matricies do not commute, input order is important. For this the function
444/// the second argument is subtracted from the first. So in the following code
445/// matrix3 = matrix1 - matrix2.
446/// ```
447/// let matrix1 = srmc::create_numbered_matrix(3,3);
448/// let matrix2 = srmc::create_identity_matrix(3);
449/// let matrix3 = srmc::matrix_subtraction(&matrix1, &matrix2);
450/// ```
451/// NOTE: Running this function keeps input Matricies unchanged and creates a new Matrix.
452/// If you want to run without creating a new Matrix do the following:
453/// ```
454/// let mut matrix1 = srmc::create_numbered_matrix(3,3);
455/// let matrix2 = srmc::create_identity_matrix(3);
456/// matrix1 = srmc::matrix_subtraction(&matrix1, &matrix2);
457/// ```
458/// In the above code, matrix2 could have also been mutable and set equal to the result.
459pub fn matrix_subtraction(mat1: &Matrix, mat2: &Matrix) -> Matrix {
460 if mat1.rows != mat2.rows || mat1.columns != mat2.columns {
461 panic!("Matricies must be of the same size to perform matrix subtraction.");
462 }
463 let mut res_mat = create_matrix(mat1.rows, mat1.columns);
464
465 let mut i: usize = 0;
466 while i < mat1.rows as usize {
467 let mut j = 0;
468 while j < mat1.columns as usize {
469 res_mat.elements[i][j] = mat1.elements[i][j] - mat2.elements[i][j];
470 j = j + 1;
471 }
472 i = i + 1;
473 }
474 return res_mat;
475}
476
477/// Returns the tranpose of an SRMC Matrix
478///
479/// Example Code:
480/// ```
481/// let matrix = srmc::create_numbered_matrix(2,4);
482/// let matrix2 = srmc::matrix_transpose(&matrix);
483/// ```
484/// NOTE: Running this function keeps input Matrix unchanged and creates a new Matrix.
485/// If you want to change input Matrix without creating a new one do the following:
486/// ```
487/// let mut matrix = srmc::create_numbered_matrix(2,4);
488/// matrix = srmc::matrix_transpose(&matrix);
489///```
490pub fn matrix_transpose(mat: &Matrix) -> Matrix {
491 let mut res_mat = create_matrix(mat.columns, mat.rows);
492 let mut i: usize = 0;
493 while i < mat.columns as usize {
494 let mut j: usize = 0;
495 while j < mat.rows as usize {
496 res_mat.elements[i][j] = mat.elements[j][i];
497 j = j + 1;
498 }
499 i = i + 1;
500 }
501 return res_mat;
502}