## Programming Praxis – Big Numbers: Getting Started

In today’s Programming Praxis exercise, our task is to implement the basics of a library for big numbers. Let’s get started, shall we?

A quick import:

```import Data.List
```

We could represent a big number as a plain list like the Scheme version does. Using a custom data structure, however, has the advantage of being able to make it an instance of the standard numeric classes, which means shorter function names and easier literals. We also store the number of digits separately because I find it a bit cleaner.

```data BigNum = B Int [Int] deriving (Eq, Show)
```

We’re using 1000 as our base for now.

```base :: Integer
base = 1000
```

The Num class gives us some of the required functions. Since addition and multiplication haven’t been implemented yet the compiler will throw some warnings.

```instance Num BigNum where
negate (B l ds) = B (-l) ds
abs (B l ds)    = B (abs l) ds
signum (B l _)  = fromIntegral \$ signum l
fromInteger n | n < 0     = negate \$ fromInteger (-n)
| otherwise = B (length ds) (map fromIntegral ds)
where ds = tail \$ f (n,0)
f (0,m) = [m]
f (d,m) = m : f (divMod d base)
```

I personally don’t see much use for three separate functions for the sign of a number, since you can either use signum or the appropriate comparsion, but we’ll stick to the assignment.

```positive, negative, zero :: (Num a, Ord a) => a -> Bool
positive = (> 0)
negative = (< 0)
zero     = (== 0)
```

If we make BigNum an instance of the Integral class we could use the default even and odd functions, but this requires implementing modulo arithmetic. We’ll use these for now until we have to do so in a following exercise.

```bigOdd, bigEven :: BigNum -> Bool
bigEven (B l ds) = l == 0 || even (head ds)
bigOdd           = not . bigEven
```

The Integral class also has a toInteger function, but for now we’ll use a separate function.

```fromBig :: BigNum -> Integer
fromBig (B _ ds) = foldr (\x a -> fromIntegral x + base * a) 0 ds
```

Here’s another advantage of using the standard type classes: we only need to implement the compare function to get all the others for free.

```instance Ord BigNum where
compare (B l1 ds1) (B l2 ds2) = case compare l1 l2 of
EQ -> maybe EQ id . find (/= EQ) . reverse \$ zipWith compare ds1 ds2
c  -> c
```

Time for a whole bunch of tests.

```main :: IO ()
main = do let a = 12345678
let b = -87654321
print \$ 0               == B 0 []
print \$ 1               == B 1 [1]
print \$ (-1)            == B (-1) [1]
print \$ a               == B 3 [678,345,12]
print \$ b               == B (-3) [321,654,87]
print \$ fromBig a       == 12345678
print \$ fromBig (abs b) == 87654321
print \$ abs a           == a
print \$ positive a
print \$ negative b
print \$ zero (0 :: BigNum)
print \$ bigEven a
print \$ bigOdd b
print \$ b /= a
print \$ b < a
```

Looks like everything is working properly.

Tags: , , , , , , ,