[project @ 2001-09-26 15:12:33 by simonpj]
[ghc-hetmet.git] / ghc / lib / std / PrelEnum.lhs
1 % -----------------------------------------------------------------------------
2 % $Id: PrelEnum.lhs,v 1.17 2001/09/26 15:12:37 simonpj Exp $
3 %
4 % (c) The University of Glasgow, 1992-2001
5 %
6
7 Instances of Bounded and Enum for various datatypes.
8
9 \begin{code}
10 {-# OPTIONS -fno-implicit-prelude #-}
11
12 module PrelEnum(
13         Bounded(..), Enum(..),
14         boundedEnumFrom, boundedEnumFromThen,
15
16         -- Instances for Bounded and Eum: (), Char, Int
17
18    ) where
19
20 import {-# SOURCE #-} PrelErr ( error )
21 import PrelBase
22 import PrelTup  ()      -- To make sure we look for the .hi file
23
24 default ()              -- Double isn't available yet
25 \end{code}
26
27
28 %*********************************************************
29 %*                                                      *
30 \subsection{Class declarations}
31 %*                                                      *
32 %*********************************************************
33
34 \begin{code}
35 class  Bounded a  where
36     minBound, maxBound :: a
37
38 class  Enum a   where
39     succ, pred          :: a -> a
40     toEnum              :: Int -> a
41     fromEnum            :: a -> Int
42     enumFrom            :: a -> [a]             -- [n..]
43     enumFromThen        :: a -> a -> [a]        -- [n,n'..]
44     enumFromTo          :: a -> a -> [a]        -- [n..m]
45     enumFromThenTo      :: a -> a -> a -> [a]   -- [n,n'..m]
46
47     succ                   = toEnum . (`plusInt` oneInt)  . fromEnum
48     pred                   = toEnum . (`minusInt` oneInt) . fromEnum
49     enumFrom x             = map toEnum [fromEnum x ..]
50     enumFromThen x y       = map toEnum [fromEnum x, fromEnum y ..]
51     enumFromTo x y         = map toEnum [fromEnum x .. fromEnum y]
52     enumFromThenTo x1 x2 y = map toEnum [fromEnum x1, fromEnum x2 .. fromEnum y]
53
54 -- Default methods for bounded enumerations
55 boundedEnumFrom :: (Enum a, Bounded a) => a -> [a]
56 boundedEnumFrom n = map toEnum [fromEnum n .. fromEnum (maxBound `asTypeOf` n)]
57
58 boundedEnumFromThen :: (Enum a, Bounded a) => a -> a -> [a]
59 boundedEnumFromThen n1 n2 
60   | i_n2 >= i_n1  = map toEnum [i_n1, i_n2 .. fromEnum (maxBound `asTypeOf` n1)]
61   | otherwise     = map toEnum [i_n1, i_n2 .. fromEnum (minBound `asTypeOf` n1)]
62   where
63     i_n1 = fromEnum n1
64     i_n2 = fromEnum n2
65 \end{code}
66
67
68 %*********************************************************
69 %*                                                      *
70 \subsection{Tuples}
71 %*                                                      *
72 %*********************************************************
73
74 \begin{code}
75 instance Bounded () where
76     minBound = ()
77     maxBound = ()
78
79 instance Enum () where
80     succ _      = error "Prelude.Enum.().succ: bad argment"
81     pred _      = error "Prelude.Enum.().pred: bad argument"
82
83     toEnum x | x == zeroInt = ()
84              | otherwise    = error "Prelude.Enum.().toEnum: bad argument"
85
86     fromEnum () = zeroInt
87     enumFrom ()         = [()]
88     enumFromThen () ()  = [()]
89     enumFromTo () ()    = [()]
90     enumFromThenTo () () () = [()]
91 \end{code}
92
93 \begin{code}
94 instance (Bounded a, Bounded b) => Bounded (a,b) where
95    minBound = (minBound, minBound)
96    maxBound = (maxBound, maxBound)
97
98 instance (Bounded a, Bounded b, Bounded c) => Bounded (a,b,c) where
99    minBound = (minBound, minBound, minBound)
100    maxBound = (maxBound, maxBound, maxBound)
101
102 instance (Bounded a, Bounded b, Bounded c, Bounded d) => Bounded (a,b,c,d) where
103    minBound = (minBound, minBound, minBound, minBound)
104    maxBound = (maxBound, maxBound, maxBound, maxBound)
105 \end{code}
106
107
108 %*********************************************************
109 %*                                                      *
110 \subsection{Type @Bool@}
111 %*                                                      *
112 %*********************************************************
113
114 \begin{code}
115 instance Bounded Bool where
116   minBound = False
117   maxBound = True
118
119 instance Enum Bool where
120   succ False = True
121   succ True  = error "Prelude.Enum.Bool.succ: bad argment"
122
123   pred True  = False
124   pred False  = error "Prelude.Enum.Bool.pred: bad argment"
125
126   toEnum n | n == zeroInt = False
127            | n == oneInt  = True
128            | otherwise    = error "Prelude.Enum.Bool.toEnum: bad argment"
129
130   fromEnum False = zeroInt
131   fromEnum True  = oneInt
132
133   -- Use defaults for the rest
134   enumFrom     = boundedEnumFrom
135   enumFromThen = boundedEnumFromThen
136 \end{code}
137
138 %*********************************************************
139 %*                                                      *
140 \subsection{Type @Ordering@}
141 %*                                                      *
142 %*********************************************************
143
144 \begin{code}
145 instance Bounded Ordering where
146   minBound = LT
147   maxBound = GT
148
149 instance Enum Ordering where
150   succ LT = EQ
151   succ EQ = GT
152   succ GT = error "Prelude.Enum.Ordering.succ: bad argment"
153
154   pred GT = EQ
155   pred EQ = LT
156   pred LT = error "Prelude.Enum.Ordering.pred: bad argment"
157
158   toEnum n | n == zeroInt = LT
159            | n == oneInt  = EQ
160            | n == twoInt  = GT
161   toEnum _ = error "Prelude.Enum.Ordering.toEnum: bad argment"
162
163   fromEnum LT = zeroInt
164   fromEnum EQ = oneInt
165   fromEnum GT = twoInt
166
167   -- Use defaults for the rest
168   enumFrom     = boundedEnumFrom
169   enumFromThen = boundedEnumFromThen
170 \end{code}
171
172 %*********************************************************
173 %*                                                      *
174 \subsection{Type @Char@}
175 %*                                                      *
176 %*********************************************************
177
178 \begin{code}
179 instance  Bounded Char  where
180     minBound =  '\0'
181     maxBound =  '\x10FFFF'
182
183 instance  Enum Char  where
184     succ (C# c#)
185        | not (ord# c# ==# 0x10FFFF#) = C# (chr# (ord# c# +# 1#))
186        | otherwise              = error ("Prelude.Enum.Char.succ: bad argument")
187     pred (C# c#)
188        | not (ord# c# ==# 0#)   = C# (chr# (ord# c# -# 1#))
189        | otherwise              = error ("Prelude.Enum.Char.pred: bad argument")
190
191     toEnum   = chr
192     fromEnum = ord
193
194     {-# INLINE enumFrom #-}
195     enumFrom (C# x) = eftChar (ord# x) 0x10FFFF#
196         -- Blarg: technically I guess enumFrom isn't strict!
197
198     {-# INLINE enumFromTo #-}
199     enumFromTo (C# x) (C# y) = eftChar (ord# x) (ord# y)
200     
201     {-# INLINE enumFromThen #-}
202     enumFromThen (C# x1) (C# x2) = efdChar (ord# x1) (ord# x2)
203     
204     {-# INLINE enumFromThenTo #-}
205     enumFromThenTo (C# x1) (C# x2) (C# y) = efdtChar (ord# x1) (ord# x2) (ord# y)
206
207 {-# NOINLINE [1] eftChar #-}
208 {-# NOINLINE [1] efdChar #-}
209 {-# NOINLINE [1] efdtChar #-}
210 eftChar  = eftCharList
211 efdChar  = efdCharList
212 efdtChar = efdtCharList
213
214 {-# RULES
215 "eftChar"       forall x y.     eftChar x y       = build (\c n -> eftCharFB c n x y)
216 "efdChar"       forall x1 x2.   efdChar x1 x2     = build (\ c n -> efdCharFB c n x1 x2)
217 "efdtChar"      forall x1 x2 l. efdtChar x1 x2 l  = build (\ c n -> efdtCharFB c n x1 x2 l)
218 "eftCharList"   eftCharFB  (:) [] = eftCharList
219 "efdCharList"   efdCharFB  (:) [] = efdCharList
220 "efdtCharList"  efdtCharFB (:) [] = efdtCharList
221  #-}
222
223
224 -- We can do better than for Ints because we don't
225 -- have hassles about arithmetic overflow at maxBound
226 {-# INLINE [0] eftCharFB #-}
227 eftCharFB c n x y = go x
228                  where
229                     go x | x ># y    = n
230                          | otherwise = C# (chr# x) `c` go (x +# 1#)
231
232 eftCharList x y | x ># y    = [] 
233                 | otherwise = C# (chr# x) : eftCharList (x +# 1#) y
234
235
236 -- For enumFromThenTo we give up on inlining
237 {-# NOINLINE [0] efdCharFB #-}
238 efdCharFB c n x1 x2
239   | delta >=# 0# = go_up_char_fb c n x1 delta 0x10FFFF#
240   | otherwise    = go_dn_char_fb c n x1 delta 0#
241   where
242     delta = x2 -# x1
243
244 efdCharList x1 x2
245   | delta >=# 0# = go_up_char_list x1 delta 0x10FFFF#
246   | otherwise    = go_dn_char_list x1 delta 0#
247   where
248     delta = x2 -# x1
249
250 {-# NOINLINE [0] efdtCharFB #-}
251 efdtCharFB c n x1 x2 lim
252   | delta >=# 0# = go_up_char_fb c n x1 delta lim
253   | otherwise    = go_dn_char_fb c n x1 delta lim
254   where
255     delta = x2 -# x1
256
257 efdtCharList x1 x2 lim
258   | delta >=# 0# = go_up_char_list x1 delta lim
259   | otherwise    = go_dn_char_list x1 delta lim
260   where
261     delta = x2 -# x1
262
263 go_up_char_fb c n x delta lim
264   = go_up x
265   where
266     go_up x | x ># lim  = n
267             | otherwise = C# (chr# x) `c` go_up (x +# delta)
268
269 go_dn_char_fb c n x delta lim
270   = go_dn x
271   where
272     go_dn x | x <# lim  = n
273             | otherwise = C# (chr# x) `c` go_dn (x +# delta)
274
275 go_up_char_list x delta lim
276   = go_up x
277   where
278     go_up x | x ># lim  = []
279             | otherwise = C# (chr# x) : go_up (x +# delta)
280
281 go_dn_char_list x delta lim
282   = go_dn x
283   where
284     go_dn x | x <# lim  = []
285             | otherwise = C# (chr# x) : go_dn (x +# delta)
286 \end{code}
287
288
289 %*********************************************************
290 %*                                                      *
291 \subsection{Type @Int@}
292 %*                                                      *
293 %*********************************************************
294
295 Be careful about these instances.  
296         (a) remember that you have to count down as well as up e.g. [13,12..0]
297         (b) be careful of Int overflow
298         (c) remember that Int is bounded, so [1..] terminates at maxInt
299
300 Also NB that the Num class isn't available in this module.
301         
302 \begin{code}
303 instance  Bounded Int where
304     minBound =  minInt
305     maxBound =  maxInt
306
307 instance  Enum Int  where
308     succ x  
309        | x == maxBound  = error "Prelude.Enum.succ{Int}: tried to take `succ' of maxBound"
310        | otherwise      = x `plusInt` oneInt
311     pred x
312        | x == minBound  = error "Prelude.Enum.pred{Int}: tried to take `pred' of minBound"
313        | otherwise      = x `minusInt` oneInt
314
315     toEnum   x = x
316     fromEnum x = x
317
318     {-# INLINE enumFrom #-}
319     enumFrom (I# x) = eftInt x maxInt#
320         where I# maxInt# = maxInt
321         -- Blarg: technically I guess enumFrom isn't strict!
322
323     {-# INLINE enumFromTo #-}
324     enumFromTo (I# x) (I# y) = eftInt x y
325
326     {-# INLINE enumFromThen #-}
327     enumFromThen (I# x1) (I# x2) = efdInt x1 x2
328
329     {-# INLINE enumFromThenTo #-}
330     enumFromThenTo (I# x1) (I# x2) (I# y) = efdtInt x1 x2 y
331
332 {-# NOINLINE [1] eftInt #-}
333 {-# NOINLINE [1] efdInt #-}
334 {-# NOINLINE [1] efdtInt #-}
335 eftInt  = eftIntList
336 efdInt  = efdIntList
337 efdtInt = efdtIntList
338
339 {-# RULES
340 "eftInt"        forall x y.     eftInt x y       = build (\ c n -> eftIntFB c n x y)
341 "efdInt"        forall x1 x2.   efdInt x1 x2     = build (\ c n -> efdIntFB c n x1 x2)
342 "efdtInt"       forall x1 x2 l. efdtInt x1 x2 l  = build (\ c n -> efdtIntFB c n x1 x2 l)
343
344 "eftIntList"    eftIntFB  (:) [] = eftIntList
345 "efdIntList"    efdIntFB  (:) [] = efdIntList
346 "efdtIntList"   efdtIntFB (:) [] = efdtIntList
347  #-}
348
349
350 {-# INLINE [0] eftIntFB #-}
351 eftIntFB c n x y | x ># y    = n        
352                  | otherwise = go x
353                  where
354                    go x = I# x `c` if x ==# y then n else go (x +# 1#)
355                         -- Watch out for y=maxBound; hence ==, not >
356         -- Be very careful not to have more than one "c"
357         -- so that when eftInfFB is inlined we can inline
358         -- whatver is bound to "c"
359
360 eftIntList x y | x ># y    = []
361                | otherwise = go x
362                where
363                  go x = I# x : if x ==# y then [] else go (x +# 1#)
364
365
366 -- For enumFromThenTo we give up on inlining; so we don't worry
367 -- about duplicating occurrences of "c"
368 {-# NOINLINE [0] efdtIntFB #-}
369 efdtIntFB c n x1 x2 y
370   | delta >=# 0# = if x1 ># y then n else go_up_int_fb c n x1 delta lim
371   | otherwise    = if x1 <# y then n else go_dn_int_fb c n x1 delta lim 
372   where
373     delta = x2 -# x1
374     lim   = y -# delta
375
376 efdtIntList x1 x2 y
377   | delta >=# 0# = if x1 ># y then [] else go_up_int_list x1 delta lim
378   | otherwise    = if x1 <# y then [] else go_dn_int_list x1 delta lim
379   where
380     delta = x2 -# x1
381     lim   = y -# delta
382
383 {-# NOINLINE [0] efdIntFB #-}
384 efdIntFB c n x1 x2
385   | delta >=# 0# = case maxInt of I# y -> go_up_int_fb c n x1 delta (y -# delta)
386   | otherwise    = case minInt of I# y -> go_dn_int_fb c n x1 delta (y -# delta)
387   where
388     delta = x2 -# x1
389
390 efdIntList x1 x2
391   | delta >=# 0# = case maxInt of I# y -> go_up_int_list x1 delta (y -# delta)
392   | otherwise    = case minInt of I# y -> go_dn_int_list x1 delta (y -# delta)
393   where
394     delta = x2 -# x1
395
396 -- In all of these, the (x +# delta) is guaranteed not to overflow
397
398 go_up_int_fb c n x delta lim
399   = go_up x
400   where
401     go_up x | x ># lim  = I# x `c` n
402             | otherwise = I# x `c` go_up (x +# delta)
403
404 go_dn_int_fb c n x delta lim 
405   = go_dn x
406   where
407     go_dn x | x <# lim  = I# x `c` n
408             | otherwise = I# x `c` go_dn (x +# delta)
409
410 go_up_int_list x delta lim
411   = go_up x
412   where
413     go_up x | x ># lim  = [I# x]
414             | otherwise = I# x : go_up (x +# delta)
415
416 go_dn_int_list x delta lim 
417   = go_dn x
418   where
419     go_dn x | x <# lim  = [I# x]
420             | otherwise = I# x : go_dn (x +# delta)
421 \end{code}
422