Skip to content

Commit 219cc33

Browse files
authored
refactor: Enhance docs, code, add tests in `LinearDiophantineEquation… (#6744)
refactor: Enhance docs, code, add tests in `LinearDiophantineEquationsSolver`
1 parent ce6f731 commit 219cc33

File tree

2 files changed

+486
-1
lines changed

2 files changed

+486
-1
lines changed

src/main/java/com/thealgorithms/maths/LinearDiophantineEquationsSolver.java

Lines changed: 156 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,77 @@
22

33
import java.util.Objects;
44

5+
/**
6+
* A solver for linear Diophantine equations of the form ax + by = c.
7+
* <p>
8+
* A linear Diophantine equation is an equation in which only integer solutions
9+
* are allowed.
10+
* This solver uses the Extended Euclidean Algorithm to find integer solutions
11+
* (x, y)
12+
* for equations of the form ax + by = c, where a, b, and c are integers.
13+
* </p>
14+
* <p>
15+
* The equation has solutions if and only if gcd(a, b) divides c.
16+
* If solutions exist, this solver finds one particular solution.
17+
* </p>
18+
*
19+
* @see <a href="https://en.wikipedia.org/wiki/Diophantine_equation">Diophantine
20+
* Equation</a>
21+
* @see <a href=
22+
* "https://en.wikipedia.org/wiki/Extended_Euclidean_algorithm">Extended
23+
* Euclidean Algorithm</a>
24+
*/
525
public final class LinearDiophantineEquationsSolver {
626
private LinearDiophantineEquationsSolver() {
727
}
828

29+
/**
30+
* Demonstrates the solver with a sample equation: 3x + 4y = 7.
31+
*
32+
* @param args command line arguments (not used)
33+
*/
934
public static void main(String[] args) {
1035
// 3x + 4y = 7
1136
final var toSolve = new Equation(3, 4, 7);
1237
System.out.println(findAnySolution(toSolve));
1338
}
1439

40+
/**
41+
* Finds any integer solution to the linear Diophantine equation ax + by = c.
42+
* <p>
43+
* The method returns one of three types of solutions:
44+
* <ul>
45+
* <li>A specific solution (x, y) if solutions exist</li>
46+
* <li>{@link Solution#NO_SOLUTION} if no integer solutions exist</li>
47+
* <li>{@link Solution#INFINITE_SOLUTIONS} if the equation is 0x + 0y = 0</li>
48+
* </ul>
49+
* </p>
50+
*
51+
* @param equation the linear Diophantine equation to solve
52+
* @return a Solution object containing the result
53+
* @throws NullPointerException if equation is null
54+
*/
1555
public static Solution findAnySolution(final Equation equation) {
1656
if (equation.a() == 0 && equation.b() == 0 && equation.c() == 0) {
1757
return Solution.INFINITE_SOLUTIONS;
1858
}
59+
if (equation.a() == 0 && equation.b() == 0) {
60+
return Solution.NO_SOLUTION;
61+
}
62+
if (equation.a() == 0) {
63+
if (equation.c() % equation.b() == 0) {
64+
return new Solution(0, equation.c() / equation.b());
65+
} else {
66+
return Solution.NO_SOLUTION;
67+
}
68+
}
69+
if (equation.b() == 0) {
70+
if (equation.c() % equation.a() == 0) {
71+
return new Solution(equation.c() / equation.a(), 0);
72+
} else {
73+
return Solution.NO_SOLUTION;
74+
}
75+
}
1976
final var stub = new GcdSolutionWrapper(0, new Solution(0, 0));
2077
final var gcdSolution = gcd(equation.a(), equation.b(), stub);
2178
if (equation.c() % gcdSolution.getGcd() != 0) {
@@ -29,43 +86,100 @@ public static Solution findAnySolution(final Equation equation) {
2986
return toReturn;
3087
}
3188

89+
/**
90+
* Computes the GCD of two integers using the Extended Euclidean Algorithm.
91+
* <p>
92+
* This method also finds coefficients x and y such that ax + by = gcd(a, b).
93+
* The coefficients are stored in the 'previous' wrapper object.
94+
* </p>
95+
*
96+
* @param a the first integer
97+
* @param b the second integer
98+
* @param previous a wrapper to store the solution coefficients
99+
* @return a GcdSolutionWrapper containing the GCD and coefficients
100+
*/
32101
private static GcdSolutionWrapper gcd(final int a, final int b, final GcdSolutionWrapper previous) {
33102
if (b == 0) {
34103
return new GcdSolutionWrapper(a, new Solution(1, 0));
35104
}
36105
// stub wrapper becomes the `previous` of the next recursive call
37106
final var stubWrapper = new GcdSolutionWrapper(0, new Solution(0, 0));
38-
final var next = /* recursive call */ gcd(b, a % b, stubWrapper);
107+
final var next = gcd(b, a % b, stubWrapper);
39108
previous.getSolution().setX(next.getSolution().getY());
40109
previous.getSolution().setY(next.getSolution().getX() - (a / b) * (next.getSolution().getY()));
41110
previous.setGcd(next.getGcd());
42111
return new GcdSolutionWrapper(next.getGcd(), previous.getSolution());
43112
}
44113

114+
/**
115+
* Represents a solution (x, y) to a linear Diophantine equation.
116+
* <p>
117+
* Special instances:
118+
* <ul>
119+
* <li>{@link #NO_SOLUTION} - indicates no integer solutions exist</li>
120+
* <li>{@link #INFINITE_SOLUTIONS} - indicates infinitely many solutions
121+
* exist</li>
122+
* </ul>
123+
* </p>
124+
*/
45125
public static final class Solution {
46126

127+
/**
128+
* Singleton instance representing the case where no solution exists.
129+
*/
47130
public static final Solution NO_SOLUTION = new Solution(Integer.MAX_VALUE, Integer.MAX_VALUE);
131+
132+
/**
133+
* Singleton instance representing the case where infinite solutions exist.
134+
*/
48135
public static final Solution INFINITE_SOLUTIONS = new Solution(Integer.MIN_VALUE, Integer.MIN_VALUE);
136+
49137
private int x;
50138
private int y;
51139

140+
/**
141+
* Constructs a solution with the given x and y values.
142+
*
143+
* @param x the x coordinate of the solution
144+
* @param y the y coordinate of the solution
145+
*/
52146
public Solution(int x, int y) {
53147
this.x = x;
54148
this.y = y;
55149
}
56150

151+
/**
152+
* Gets the x value of this solution.
153+
*
154+
* @return the x value
155+
*/
57156
public int getX() {
58157
return x;
59158
}
60159

160+
/**
161+
* Gets the y value of this solution.
162+
*
163+
* @return the y value
164+
*/
61165
public int getY() {
62166
return y;
63167
}
64168

169+
/**
170+
* Sets the x value of this solution.
171+
*
172+
* @param x the new x value
173+
*/
65174
public void setX(int x) {
66175
this.x = x;
67176
}
68177

178+
/**
179+
* Sets the y value of this solution.
180+
*
181+
* @param y the new y value
182+
*/
69183
public void setY(int y) {
70184
this.y = y;
71185
}
@@ -95,14 +209,35 @@ public String toString() {
95209
}
96210
}
97211

212+
/**
213+
* Represents a linear Diophantine equation of the form ax + by = c.
214+
*
215+
* @param a the coefficient of x
216+
* @param b the coefficient of y
217+
* @param c the constant term
218+
*/
98219
public record Equation(int a, int b, int c) {
99220
}
100221

222+
/**
223+
* A wrapper class that holds both the GCD and the solution coefficients
224+
* from the Extended Euclidean Algorithm.
225+
* <p>
226+
* This class is used internally to pass results between recursive calls
227+
* of the GCD computation.
228+
* </p>
229+
*/
101230
public static final class GcdSolutionWrapper {
102231

103232
private int gcd;
104233
private Solution solution;
105234

235+
/**
236+
* Constructs a GcdSolutionWrapper with the given GCD and solution.
237+
*
238+
* @param gcd the greatest common divisor
239+
* @param solution the solution coefficients
240+
*/
106241
public GcdSolutionWrapper(int gcd, Solution solution) {
107242
this.gcd = gcd;
108243
this.solution = solution;
@@ -120,18 +255,38 @@ public boolean equals(Object obj) {
120255
return (this.gcd == that.gcd && Objects.equals(this.solution, that.solution));
121256
}
122257

258+
/**
259+
* Gets the GCD value.
260+
*
261+
* @return the GCD
262+
*/
123263
public int getGcd() {
124264
return gcd;
125265
}
126266

267+
/**
268+
* Sets the GCD value.
269+
*
270+
* @param gcd the new GCD value
271+
*/
127272
public void setGcd(int gcd) {
128273
this.gcd = gcd;
129274
}
130275

276+
/**
277+
* Gets the solution coefficients.
278+
*
279+
* @return the solution
280+
*/
131281
public Solution getSolution() {
132282
return solution;
133283
}
134284

285+
/**
286+
* Sets the solution coefficients.
287+
*
288+
* @param solution the new solution
289+
*/
135290
public void setSolution(Solution solution) {
136291
this.solution = solution;
137292
}

0 commit comments

Comments
 (0)