module diophantine use constants, only: dp implicit none private public :: gcd, lcm, rational_approx contains function gcd(a, b) result(g) integer, intent(in) :: a, b integer :: g, aa, bb, tmp aa = abs(a); bb = abs(b) do while (bb /= 0) tmp = bb; bb = mod(aa, bb); aa = tmp end do g = aa end function gcd function lcm(a, b) result(l) integer, intent(in) :: a, b integer :: l l = abs(a/gcd(a, b)*b) ! divide first to avoid overflow end function lcm subroutine rational_approx(x, max_denom, p, q) ! Approximate x ~ p/q via continued fraction convergents, q <= max_denom real(dp), intent(in) :: x integer, intent(in) :: max_denom integer, intent(out) :: p, q integer :: a, p0, p1, q0, q1, pt, qt real(dp) :: r p0 = 0; q0 = 1 p1 = 1; q1 = 0 if (max_denom <= 0) then error stop "max_denom must be positive" end if r = abs(x) do a = int(r) pt = a*p1 + p0 qt = a*q1 + q0 if (qt > max_denom) exit p0 = p1; q0 = q1 p1 = pt; q1 = qt if (abs(r - a) < 1.0d-12) exit r = 1.0_dp/(r - a) end do p = merge(p1, -p1, x >= 0.0_dp); q = q1 end subroutine rational_approx end module diophantine