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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
|
{-# OPTIONS_CYMAKE -Wno-incomplete-patterns #-}
module Data.Roman
( Roman(..), showRoman, showsRoman, toInt, fromInt, regular )
where
import Test.Prop
data Roman = I | V | X | L | C | D | M
| Minus Roman Roman
| Plus Roman Roman
deriving (Eq,Ord,Show)
regular :: Roman -> Bool
regular r = fromInt (toInt r) == r
showRoman :: Roman -> String
showRoman r = showsRoman r ""
showsRoman :: Roman -> ShowS
showsRoman r = case r of
Minus m n -> showsRoman m . showsRoman n
Plus n p -> showsRoman n . showsRoman p
_ -> shows r
toInt :: Roman -> Int
toInt I = 1
toInt V = 5
toInt X = 10
toInt L = 50
toInt C = 100
toInt D = 500
toInt M = 1000
toInt (Minus m r) = toInt r - toInt m
toInt (Plus r p) = toInt r + toInt p
fromInt :: Int -> Roman
fromInt i | 1 <= i && i < 4000 = foldr1 Plus (base i [M,C,X,I])
base :: Int -> [Roman] -> [Roman]
base _ [] = []
base i (r:rs)
| 9 <= dR = Minus (times (dR-8)) (ten r) : rest
| 6 <= dR = Plus (five r) (times (dR-5)) : rest
| 5 == dR = five r : rest
| 4 == dR = Minus r (five r) : rest
| 1 <= dR = times dR : rest
| otherwise = rest
where
(dR,mR) = divMod i (toInt r)
times t = foldr1 Plus $ replicate t r
rest = base mR rs
five :: Roman -> Roman
five C = D
five X = L
five I = V
ten :: Roman -> Roman
ten C = M
ten X = C
ten I = X
allRegular :: Int -> Prop
allRegular n = n>=1 && n<=4000 ==> toInt (fromInt n) -=- n
roman42 :: Prop
roman42 = showRoman (fromInt 42) -=- "XLII"
roman1963 :: Prop
roman1963 = showRoman (fromInt 1963) -=- "MCMLXIII"
|