Skip to content

Commit 0a5f5c6

Browse files
feat: added extended Euclidean algorithm (#1238)
* chore: made it so math directory gets built * feat: added extended Euclidean algorithm * docs: added details qualifier Co-authored-by: David Leal <[email protected]> * docs: added param qualifiers to functions that needed them * docs: added details qualifier Co-authored-by: David Leal <[email protected]> * docs: small cleanup --------- Co-authored-by: David Leal <[email protected]>
1 parent e1fcdd2 commit 0a5f5c6

File tree

1 file changed

+154
-0
lines changed

1 file changed

+154
-0
lines changed

math/euclidean_algorithm_extended.c

+154
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
/**
2+
* @{
3+
* @file
4+
* @brief Program to perform the [extended Euclidean
5+
* algorithm](https://en.wikipedia.org/wiki/Extended_Euclidean_algorithm)
6+
*
7+
* @details The extended Euclidean algorithm, on top of finding the GCD (greatest common
8+
* divisor) of two integers a and b, also finds the values x and y such that
9+
* ax+by = gcd(a, b)
10+
*/
11+
12+
#include <assert.h> /// for tests
13+
#include <stdio.h> /// for IO
14+
#include <stdlib.h> /// for div function and corresponding div_t struct
15+
16+
/**
17+
* @brief a structure holding the values resulting from the extended Euclidean
18+
* algorithm
19+
*/
20+
typedef struct euclidean_result
21+
{
22+
int gcd; ///< the greatest common divisor calculated with the Euclidean
23+
///< algorithm
24+
int x, y; ///< the values x and y such that ax + by = gcd(a, b)
25+
} euclidean_result_t;
26+
27+
/**
28+
* @brief gives queue-like behavior to an array of two ints, pushing an element
29+
* onto the end and pushing one off the front
30+
*
31+
* @param arr an array of ints acting as a queue
32+
* @param newval the value being pushed into arr
33+
*
34+
* @returns void
35+
*/
36+
static inline void xy_push(int arr[2], int newval)
37+
{
38+
arr[1] = arr[0];
39+
arr[0] = newval;
40+
}
41+
42+
/**
43+
* @brief calculates the value of x or y and push those into the small 'queues'
44+
*
45+
* @details Both x and y are found by taking their value from 2 iterations ago minus the
46+
* product of their value from 1 iteration ago and the most recent quotient.
47+
*
48+
* @param quotient the quotient from the latest iteration of the Euclidean
49+
* algorithm
50+
* @param prev the 'queue' holding the values of the two previous iterations
51+
*
52+
* @returns void
53+
*/
54+
static inline void calculate_next_xy(int quotient, int prev[2])
55+
{
56+
int next = prev[1] - (prev[0] * quotient);
57+
xy_push(prev, next);
58+
}
59+
60+
/**
61+
* @brief performs the extended Euclidean algorithm on integer inputs a and b
62+
*
63+
* @param a first integer input
64+
* @param b second integer input
65+
*
66+
* @returns euclidean_result_t containing the gcd, and values x and y such that
67+
* ax + by = gcd
68+
*/
69+
euclidean_result_t extended_euclidean_algorithm(int a, int b)
70+
{
71+
int previous_remainder = 1;
72+
int previous_x_values[2] = {0, 1};
73+
int previous_y_values[2] = {1, 0};
74+
div_t div_result;
75+
euclidean_result_t result;
76+
77+
/* swap values of a and b */
78+
if (abs(a) < abs(b))
79+
{
80+
a ^= b;
81+
b ^= a;
82+
a ^= b;
83+
}
84+
85+
div_result.rem = b;
86+
87+
while (div_result.rem > 0)
88+
{
89+
div_result = div(a, b);
90+
91+
previous_remainder = b;
92+
93+
a = b;
94+
b = div_result.rem;
95+
96+
calculate_next_xy(div_result.quot, previous_x_values);
97+
calculate_next_xy(div_result.quot, previous_y_values);
98+
}
99+
100+
result.gcd = previous_remainder;
101+
result.x = previous_x_values[1];
102+
result.y = previous_y_values[1];
103+
104+
return result;
105+
}
106+
107+
/** @} */
108+
109+
/**
110+
* @brief perform one single check on the result of the algorithm with provided
111+
* parameters and expected output
112+
*
113+
* @param a first paramater for Euclidean algorithm
114+
* @param b second parameter for Euclidean algorithm
115+
* @param gcd expected value of result.gcd
116+
* @param x expected value of result.x
117+
* @param y expected value of result.y
118+
*
119+
* @returns void
120+
*/
121+
static inline void single_test(int a, int b, int gcd, int x, int y)
122+
{
123+
euclidean_result_t result;
124+
125+
result = extended_euclidean_algorithm(a, b);
126+
assert(result.gcd == gcd);
127+
assert(result.x == x);
128+
assert(result.y == y);
129+
}
130+
131+
/**
132+
* @brief Perform tests on known results
133+
* @returns void
134+
*/
135+
static void test()
136+
{
137+
single_test(40, 27, 1, -2, 3);
138+
single_test(71, 41, 1, -15, 26);
139+
single_test(48, 18, 6, -1, 3);
140+
single_test(99, 303, 3, -16, 49);
141+
single_test(14005, 3507, 1, -305, 1218);
142+
143+
printf("All tests have successfully passed!\n");
144+
}
145+
146+
/**
147+
* @brief Main Function
148+
* @returns 0 upon successful program exit
149+
*/
150+
int main()
151+
{
152+
test(); // run self-test implementations
153+
return 0;
154+
}

0 commit comments

Comments
 (0)