; rational.lisp - prototype implementation of rational numbers
;
; Copyright (C) 2026 by Jef Poskanzer <jef@mail.acme.com>.
; All rights reserved.
;
; You may use this software however you like as long as you keep my
; name on it and don't sue me.
;
; For commentary on this license please see http://acme.com/license.html
;
;
; Rationals are represented by a CONS pair of two integer NUMs.

(defun rational-simp ("cons" r)
    (if (and (negative? (rational-n r)) (negative? (rational-d r)))
	(rational-simp (cons (abs (rational-n r)) (abs (rational-d r))))
	(let*
	    (gd (gcd (rational-n r) (rational-d r)))
	    (cons (/ (rational-n r) gd) (/ (rational-d r) gd))
	)
    )
)
(doc 'rational-simp "Simplifies a rational.")

(defun rational ("integer" n "integer" d)
    (rational-simp (cons n d))
)
(doc 'rational "Makes a rational.")

(defun rational-tostring ("cons" r)
    (string-concat (string (rational-n r)) "/" (string (rational-d r)))
)
(doc 'rational-tostring "Converts a rational to a string.")

; Add an sprintf formatter for rats as %r.
(sprintf-add 'r' cons? rational-tostring)

(defun rational-n ("cons" r) (car r))
(doc 'rational-n "Returns the numerator from a rational.")

(defun rational-d ("cons" r) (cdr r))
(doc 'rational-d "Returns the denominator from a rational.")

(defun rational-add ("cons" r1 "cons" r2)
    (rational
	(+ (* (rational-n r1) (rational-d r2)) (* (rational-n r2) (rational-d r1)))
	(* (rational-d r1) (rational-d r2))
    )
)
(doc 'rational-add "Adds two rationals.")

(defun rational-negate ("cons" r)
    (rational (negate (rational-n r)) (rational-d r))
)
(doc 'rational-negate "Returns the negative of a rational.")

(defun rational-subtract ("cons" r1 "cons" r2)
    (rational-add r1 (rational-negate r2))
)
(doc 'rational-subtract "Subtracts one rational from another.")

(defun rational-multiply ("cons" r1 "cons" r2)
    (rational
	(* (rational-n r1) (rational-n r2))
	(* (rational-d r1) (rational-d r2))
    )
)
(doc 'rational-multiply "Multiplies two rationals.")

(defun rational-reciprocal ("cons" r)
    (rational (rational-d r) (rational-n r))
)
(doc 'rational-reciprocal "Returns the reciprocal of a rational.")

(defun rational-divide ("cons" r1 "cons" r2)
    (rational-multiply r1 (rational-reciprocal r2))
)
(doc 'rational-divide "Divides one rational by another.")

(defun rational-compare ("cons" r1 "cons" r2)
    (compare
	(* (rational-n r1) (rational-d r2))
	(* (rational-n r2) (rational-d r1))
    )
)
(doc 'rational-compare "Compares two rationals, result is -1, 0, 1 for less-than, equal, greater-than.")
