2. Numbers

Checking Whether a String Is a Valid Number

is_integer s = if s =~ "^[+-]?\\d+$" 
               then putStrLn "is an integer"
               else putStrLn "is not"
m0 s = do putStrLn "has non digits" `when`   (s =~ "[^\\d]") -- no \D
          putStrLn "not a natural"  `unless` (s =~ "^\\d+$")
          putStrLn "not an integer" `unless` (s =~ "^-?\\d+$")
          putStrLn "not an integer" `unless` (s =~ "^[+-]?\\d+$")
          putStrLn "not a decimal"  `unless` (s =~ "^-?\\d+\\.\\d*$")
          putStrLn "not a float"    `unless` (s =~ "^[+-]?\\d+((\\.\\d+([eE]-?\\d+)?)|([eE]-?\\d+))$")
-- in haskell, regexp are first class, then can be appended, sometimes easier to read
m0' s = let sign       = "-"
            opt_sign   = sign ++ "?"
            dec_number = "\\d+"
            integer    = opt_sign ++ dec_number
            exp        = "eE" ++ opt_sign ++ dec_number
            real       = "(" ++ opt_sign ++ dec_number ++ "\\." ++ dec_number ++ exp ++"?" ++ ")" ++
                         "|" ++ "(" ++ opt_sign ++ dec_number ++ exp ++ ")" in
            do putStrLn "not a natural"  `unless` (s ==~ dec_number)
               putStrLn "not an integer" `unless` (s ==~ integer)
               putStrLn "not a decimal"  `unless` (s ==~ (integer ++ "\\." ++ dec_number))
               putStrLn "not a float"    `unless` (s ==~ real)

m0test = m0 "124"      

Comparing Floating-Point Numbers

equal_num n1 n2 nbdecimal = (abs (n1 - n2)) < 1 / (10 ^^ nbdecimal)
eqnum = equal_num 10.001 10.002 4 -- with 3 give True cos 10.001 - 10.002 != 0.001 but 0.000999

wage = 536          -- $5.36/hour
week = 40 * wage    -- $214.40
weekwage = putStrLn ("One week's wage is: $"++show_float 2 (tonum week/100.0))

Rounding Floating-Point Numbers

i = 2.3.round
a2  = 0.255
sa2 = show_float 2 a2
mprinta = putStrLn ("Unrounded: "^a2^"\nRounded: "^sa2)

-- TODO, i dont exactly produce the good ouput, negative number are not centered
mprintfloat = do putStrLn "number\tint\tfloor\tceil"
                 [3.3 , 3.5 , 3.7, -3.3].each(\n -> putStrLn ((show_float 1 n)^"\t"^
                                                              (n.round)^"\t"^
                                                              (n.floor)^"\t"^
                                                              (n.ceiling)))

Converting Between Binary and Decimal

-- TODO? not exacly this, as 54 = 110 110 we dont see the pb of big endian
bin2i xs = aux 1 (reverse xs)
    where aux power []     = 0
          aux power (x:xs) = aux (2*power) xs+(if x == '0' then 0 else power)
bin2i' xs = xs.reverse.map (\c -> read [c]).zipWith (\a b -> b*(2^^a)) [0..].sum
i10 = bin2i' "0110110" -- 54

i2bin i = if i == 0 then "0" else aux i
          where aux i = if i == 0  then "" else aux (i `div` 2) ++ show (i `mod` 2)
bin10 = i2bin 54 -- "110110"

Operating on a Series of Integers

m1 = putStr "Infancy is: " >> [0..2].each(\e -> putStr (e^" "))
m2 = putStr "Infancy is: " >> [0..2].map show.unwords.putStr

Working with Roman Numerals

-- code from comp.lang.functionnal
inttorom n = (concat  ° reverse) (zipWith roman sets digits)  where
     digits = reverse (map (\x -> fromInt (read [x])) (show n))
     roman (i,v,x) n = case n of { 
         0 -> [];  1 -> [i];   2 -> [i,i];   3 -> [i,i,i];   4 -> [i,v]; 
         5 -> [v]; 6 -> [v,i]; 7 -> [v,i,i]; 8 -> [v,i,i,i]; 9 -> [i,x] }
     sets = [('I','V','X'), ('X','L','C'), ('C','D','M'), ('M','?','?')] 

mroman = putStrLn ("Roman for "^15^" is :"^inttorom 15)

Generating Random Numbers

ran = rand (25, 75) -- [25,75]

chars = concat [['A'..'Z'],['a'..'z'],['0'..'9'],"!@$%^&*"]

--password = [1..8].foldM (\a _ -> rand (0, chars.length -1) >>= (\i -> return (a++chars![i]))) "" 
--password = [1..8].mapM (\_ -> rand (0, chars.length -1) >>= (\i -> return (chars!!i)))
password = [1..8].mapM (\_ -> rand (0, chars.length -1) >>> (chars!)) -- no join, string = [Char] :) hihi section :)

Generating Different Random Numbers

randfixed = do srand 2
               rand(1,10)

Making Numbers Even More Random

-- uses a Perl module.., in haskell too you can provide your own random generator by
-- playing with the StdGen type

Generating Biased Random Numbers

gaussian_rand = do r1 <- stdrand
                   r2 <- stdrand
                   let u1 = 2 * r1 - 1
                       u2 = 2 * r2 - 1
                       w  = u1*u1 + u2*u2 in
                       if w >= 1 then gaussian_rand
                       else let w2 = sqrt ((-2 * log w)/w) in
                                return [u2*w2, u1*w2]

mean = 25
sdev = 2
salary = gaussian_rand >>> (\l -> l.head * (tonum sdev) + (tonum mean))
msalary = do r <- salary
             putStrLn ("You have been hired at $"^show_float 2 r)

Doing Trigonometry in Degrees, not Radians

deg2rad d = d * pi / 180
rad2deg r = r * 180 / pi

Calculating More Trigonometric Functions

sin_val = sin pi
cos_val = cos pi
tan_val = tan pi

asin_val = asin 1
acos_val = acos 1

Taking Logarithms

log_e = log 10
log_10 = log10 100 -- 2

-- in haskell we dont have by default log10 but we have logBase
log10 = logBase 10

answer = logBase 10 10000
mlog = putStrLn ("log10(10,000)="^answer)

Multiplying Matrices

matrixa = Matrix[[3, 2, 3], 
                 [5, 9, 8]]
matrixb = Matrix[[4, 7], 
                 [9, 3], 
                 [8, 1]]
matrixc = matrixa * matrixb

masize = matrixa.row_size
mbsize = matrixa.column_size

--TODO,transposed = matrixc.transpose
--determined = matrixa.det

Using Complex Numbers

ca = 3 :+ 5
cb = 2 :+ -2
mcomplex = putStrLn ("c = "^(ca*cb))
cc = ca*cb
cd = 3 + 4*complex_i

mcomplex2 = putStrLn ("sqrt("^cd^") = "^sqrt cd)

Converting Between Octal and Hexadecimal

i2 = hex "0x45"
i3 = oct "0o45"

m3 = do putStrLn "Gimme a number in decimal, octal, or hex: "
        s <- getLine
        let i = s.pattern_matches [("^0x", hex s),("^0o", oct s),("", read s)] in
                putStrLn (concat [show i," ",tohex i," ", tooct i,"\n"])
-- no printf :((

m3' = do putStrLn "Enter file permission in octal: "
         permissions <- getLine
         putStrLn ("The decimal value is "^permissions.oct)

Putting Commas in Numbers

commify s = if s.length <= 3 then s else 
            let (a,b) = splitAt (s.length -3) s in commify a ++ "," ++ b

Printing Correct Plurals

plurals i   = if i > 1 then "s" else ""
pluralies i = (i > 1) ? ("ies","y")

hours = 2
century = 2
flies = 3
mplural  = putStrLn ("It took "^hours^" hour"^(plurals hours))
mplural2 = putStrLn ("It took "^century^" centur"^(pluralies century))

-- TODO, the suffix program and prime factor

----------------------------------------------------------------------------------------------------

Program: Calculating Prime Factors