Repository: sympy/sympy

Issue:
gcd and lcm of similar type of irrationals
gcd for rational numbers is evaluated correctly:
```python
>>> gcd(S(4)/7, S(2)/3)
2/21
```
but the result for following irrational pair:
```python
>>> gcd(sqrt(5)*S(4)/7, sqrt(5)*S(2)/3)
sqrt(5)
```
Mathematically, it is possible to find gcd/lcm of 2 irrational numbers that are of similar type.
The latter is not able to produce the desired result because it is interpreted as a polynomial in `sqrt(5)`, which returns a monic result if polynomial is over a field as [explained](https://github.com/sympy/sympy/pull/1509#issuecomment-8027350) by @asmeurer .
```python
>>> gcd(x*S(4)/7,x*S(2)/3)
x
```
**Related Issues**
[#12794](https://github.com/sympy/sympy/issues/12794) gcd does not identify full gcd in all cases

Discussion:

Patch:
diff --git a/sympy/polys/polytools.py b/sympy/polys/polytools.py
index d5ac051f63..1014b85f6e 100644
--- a/sympy/polys/polytools.py
+++ b/sympy/polys/polytools.py
@@ -5202,6 +5202,17 @@ def try_non_polynomial_gcd(seq):
 
     try:
         polys, opt = parallel_poly_from_expr(seq, *gens, **args)
+
+        # gcd for domain Q[irrational] (purely algebraic irrational)
+        if len(seq) > 1 and all(elt.is_algebraic and elt.is_irrational for elt in seq):
+            a = seq[-1]
+            lst = [ (a/elt).ratsimp() for elt in seq[:-1] ]
+            if all(frc.is_rational for frc in lst):
+                lc = 1
+                for frc in lst:
+                    lc = lcm(lc, frc.as_numer_denom()[0])
+                return a/lc
+
     except PolificationFailed as exc:
         result = try_non_polynomial_gcd(exc.exprs)
 
@@ -5257,6 +5268,14 @@ def gcd(f, g=None, *gens, **args):
 
     try:
         (F, G), opt = parallel_poly_from_expr((f, g), *gens, **args)
+
+        # gcd for domain Q[irrational] (purely algebraic irrational)
+        a, b = map(sympify, (f, g))
+        if a.is_algebraic and a.is_irrational and b.is_algebraic and b.is_irrational:
+            frc = (a/b).ratsimp()
+            if frc.is_rational:
+                return a/frc.as_numer_denom()[0]
+
     except PolificationFailed as exc:
         domain, (a, b) = construct_domain(exc.exprs)
 
@@ -5315,6 +5334,17 @@ def try_non_polynomial_lcm(seq):
 
     try:
         polys, opt = parallel_poly_from_expr(seq, *gens, **args)
+
+        # lcm for domain Q[irrational] (purely algebraic irrational)
+        if len(seq) > 1 and all(elt.is_algebraic and elt.is_irrational for elt in seq):
+            a = seq[-1]
+            lst = [ (a/elt).ratsimp() for elt in seq[:-1] ]
+            if all(frc.is_rational for frc in lst):
+                lc = 1
+                for frc in lst:
+                    lc = lcm(lc, frc.as_numer_denom()[1])
+                return a*lc
+
     except PolificationFailed as exc:
         result = try_non_polynomial_lcm(exc.exprs)
 
@@ -5367,6 +5397,14 @@ def lcm(f, g=None, *gens, **args):
 
     try:
         (F, G), opt = parallel_poly_from_expr((f, g), *gens, **args)
+
+        # lcm for domain Q[irrational] (purely algebraic irrational)
+        a, b = map(sympify, (f, g))
+        if a.is_algebraic and a.is_irrational and b.is_algebraic and b.is_irrational:
+            frc = (a/b).ratsimp()
+            if frc.is_rational:
+                return a*frc.as_numer_denom()[1]
+
     except PolificationFailed as exc:
         domain, (a, b) = construct_domain(exc.exprs)
 


Test patch:
diff --git a/sympy/polys/tests/test_polytools.py b/sympy/polys/tests/test_polytools.py
index 1f889e9f18..c942072113 100644
--- a/sympy/polys/tests/test_polytools.py
+++ b/sympy/polys/tests/test_polytools.py
@@ -3243,3 +3243,20 @@ def test_issue_12400():
     # Correction of check for negative exponents
     assert poly(1/(1+sqrt(2)), x) == \
             Poly(1/(1+sqrt(2)), x , domain='EX')
+
+def test_issue_14364():
+    assert gcd(S(6)*(1 + sqrt(3))/5, S(3)*(1 + sqrt(3))/10) == S(3)/10 * (1 + sqrt(3))
+    assert gcd(sqrt(5)*S(4)/7, sqrt(5)*S(2)/3) == sqrt(5)*S(2)/21
+
+    assert lcm(S(2)/3*sqrt(3), S(5)/6*sqrt(3)) == S(10)*sqrt(3)/3
+    assert lcm(3*sqrt(3), S(4)/sqrt(3)) == 12*sqrt(3)
+    assert lcm(S(5)*(1 + 2**(S(1)/3))/6, S(3)*(1 + 2**(S(1)/3))/8) == S(15)/2 * (1 + 2**(S(1)/3))
+
+    assert gcd(S(2)/3*sqrt(3), S(5)/6/sqrt(3)) == sqrt(3)/18
+    assert gcd(S(4)*sqrt(13)/7, S(3)*sqrt(13)/14) == sqrt(13)/14
+
+    # gcd_list and lcm_list
+    assert gcd([S(2)*sqrt(47)/7, S(6)*sqrt(47)/5, S(8)*sqrt(47)/5]) == S(2)*sqrt(47)/35
+    assert gcd([S(6)*(1 + sqrt(7))/5, S(2)*(1 + sqrt(7))/7, S(4)*(1 + sqrt(7))/13]) == S(2)/455 * (1 + sqrt(7))
+    assert lcm((S(7)/sqrt(15)/2, S(5)/sqrt(15)/6, S(5)/sqrt(15)/8)) == S(35)/(2*sqrt(15))
+    assert lcm([S(5)*(2 + 2**(S(5)/7))/6, S(7)*(2 + 2**(S(5)/7))/2, S(13)*(2 + 2**(S(5)/7))/4]) == S(455)/2 * (2 + 2**(S(5)/7))