summaryrefslogtreecommitdiff
path: root/exercism/emacs-lisp/roman-numerals/roman-numerals.el
blob: 9b556fd373018a54ed096979f6544a5b2cf9f322 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
;;; roman-numerals.el --- roman-numerals Exercise (exercism)

;;; Commentary:

;;; Code:

(require 'cl-macs)

(defun rn/unfold (fn x)
  (let ((res (funcall fn x)))
    (when (car res)
      (cons (car res) (rn/unfold fn (cdr res))))))

(defun rn/decimal-digits (number)
  "Return the decimal digits of NUMBER as a little-endian list."
  (rn/unfold (lambda (x)
            (when (> x 0)
              (cons (% x 10) (/ x 10)))) number))

(defun rn/zip (l1 l2)
  (when (and l1 l2)
    (cons (cons (car l1) (car l2))
          (rn/zip (cdr l1) (cdr l2)))))

(defun rn/roman-digit (number symbols)
  (cl-destructuring-bind (one five ten) symbols
    (cond
     ((eql  0 number) '())
     ((eql  1 number) (list one))
     ((eql  2 number) (list one one))
     ((eql  3 number) (list one one one))
     ((eql  4 number) (list one five))
     ((eql  5 number) (list five))
     ((eql  6 number) (list five one))
     ((eql  7 number) (list five one one))
     ((eql  8 number) (list five one one one))
     ((eql  9 number) (list one ten))
     ((eql 10 number) (list ten)))))

(defvar rn/roman-symbols
  '(("I" "V" "X")
    ("X" "L" "C")
    ("C" "D" "M")
    ("M" nil nil)))

(defun to-roman (number)
  (when (> number 3000)
    (error 'number-too-big))
  (let* ((decimal-digits (rn/decimal-digits number))
         (roman-digits
          (mapcar (lambda (digit_syms)
                    (rn/roman-digit (car digit_syms) (cdr digit_syms)))
                  (rn/zip decimal-digits rn/roman-symbols))))
    (apply 'concat (mapcan 'identity (reverse roman-digits)))))

(provide 'roman-numerals)
;;; roman-numerals.el ends here