rational.lisp 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. (import core/prelude ())
  2. (import data/format ())
  3. (import data/struct ())
  4. (import math (gcd))
  5. (import math/numerics ())
  6. (defstruct (rational rational rational?)
  7. "A rational number, represented as a tuple of numerator and denominator."
  8. (fields
  9. (immutable numerator numerator "The rational's numerator")
  10. (immutable denominator denominator "The rational's denominator"))
  11. (constructor new
  12. (lambda (n d)
  13. (unless (and (number? n)
  14. (= 0 (second (math/modf n))))
  15. (format 1 "(rational {} {}): numerator must be an integer" n d))
  16. (unless (and (number? d)
  17. (= 0 (second (math/modf d))))
  18. (format 1 "(rational {%d} {}): denominator must be an integer" n d))
  19. (when (= d 0)
  20. (format 1 "(rational {%d} {%d}): denominator is zero" n d))
  21. (when (< d 0)
  22. (set! d (* -1 d))
  23. (set! n (* -1 n)))
  24. (let* [(x (gcd n d))]
  25. (setmetatable
  26. (new (/ n x) (/ d x))
  27. *rational-mt*)))))
  28. (defun ->rat-components (y) :hidden
  29. (let* [((i f) (math/modf y))
  30. (f' (expt 10 (- (n (tostring f)) 2)))]
  31. (if (= 0 f) ;; it's an integer, so we just /1 i
  32. (values-list y 1)
  33. (let* [(n (* y f'))
  34. (g (gcd n f'))]
  35. (values-list (/ n g) (/ f' g))))))
  36. (defun normalised-rational-components (x) :hidden
  37. (if (number? x)
  38. (->rat-components x)
  39. (values-list (numerator x) (denominator x))))
  40. (defun ->rat (y)
  41. "Convert the floating-point number Y to a rational number.
  42. ### Example:
  43. ```cl
  44. > (->rat 3.14)
  45. out = 157/50
  46. > (/ 157 50)
  47. out = 3.14
  48. ```"
  49. (with ((n d) (->rat-components y))
  50. (rational n d)))
  51. (defun ->float (y)
  52. "Convert the rational number Y to a floating-point number.
  53. ### Example:
  54. ```cl
  55. > (->float (rational 3 2))
  56. out = 1.5
  57. ```"
  58. (/ (numerator y) (denominator y)))
  59. (defmethod (n+ rational rational) (x y)
  60. (let* [((xn xd) (normalised-rational-components x))
  61. ((yn yd) (normalised-rational-components y))]
  62. (rational (+ (* xn yd) (* yn xd))
  63. (* xd yd))))
  64. (defmethod (n- rational rational) (x y)
  65. (let* [((xn xd) (normalised-rational-components x))
  66. ((yn yd) (normalised-rational-components y))]
  67. (rational (- (* xn yd) (* yn xd))
  68. (* xd yd))))
  69. (defmethod (n* rational rational) (x y)
  70. (let* [((xn xd) (normalised-rational-components x))
  71. ((yn yd) (normalised-rational-components y))]
  72. (rational (* xn yn) (* xd yd))))
  73. (defmethod (n< rational rational) (x y)
  74. (let* [((xn xd) (normalised-rational-components x))
  75. ((yn yd) (normalised-rational-components y))]
  76. (< (* xn yd) (* yn xd))))
  77. (defalias (n< rational number) (n< rational rational))
  78. (defalias (n< number rational) (n< rational rational))
  79. (defmethod (n<= rational rational) (x y)
  80. (let* [((xn xd) (normalised-rational-components x))
  81. ((yn yd) (normalised-rational-components y))]
  82. (<= (* xn yd) (* yn xd))))
  83. (defalias (n<= rational number) (n<= rational rational))
  84. (defalias (n<= number rational) (n<= rational rational))
  85. (defmethod (nexpt rational number) (x y)
  86. (when (/= 0 (second (math/modf y)))
  87. (format 1 "(expt {#x} {#y}): exponent must be an integral number."))
  88. (if (>= y 0)
  89. (let* [((xn xd) (normalised-rational-components x))]
  90. (rational (expt xn y) (expt xd y)))
  91. (nrecip (nexpt x (nnegate y)))))
  92. (defmethod (nsqrt rational) (x)
  93. (let* [((xn xd) (normalised-rational-components x))]
  94. (rational (math/sqrt xn) (math/sqrt xd))))
  95. (define *rational-mt* :hidden
  96. { :__add n+
  97. :__sub n-
  98. :__mul n*
  99. :__div n/
  100. :__pow nexpt
  101. :__lt n<
  102. :__lte n<= })
  103. (defmethod (pretty rational) (x)
  104. (let* [((xn xd) (normalised-rational-components x))]
  105. (format nil "{%d}/{%d}" xn xd)))
  106. (defmethod (eq? rational rational) (x y)
  107. (let* [((xn xd) (normalised-rational-components x))
  108. ((yn yd) (normalised-rational-components y))]
  109. (and (= xn yn)
  110. (= xd yd))))
  111. (defalias (eq? number rational) (eq? rational rational))
  112. (defalias (eq? rational number) (eq? rational rational))
  113. (defmethod (nrecip rational) (x)
  114. (rational (denominator x) (numerator x)))
  115. (defmethod (nnegate rational) (x)
  116. (* x -1))
  117. (defmethod (nabs rational) (x)
  118. (rational (nabs (numerator x)) (nabs (denominator x))))
  119. (defmethod (nsign rational) (x)
  120. (* (nsign (numerator x)) (nsign (denominator x))))
  121. (defmethod (n/ rational rational) (x y) (n* x (nrecip y)))
  122. ,@(dolist [(op '(n+ n* n- n/))
  123. (at '(rational number))
  124. (bt '(rational number))]
  125. (when (neq? at bt)
  126. `(defalias (,op ,at ,bt) (,op ,'rational ,'rational))))