-- Integer division with periods
-- (c) Pastafarianist aka freidom 2010 lcme.ucoz.ru
import List (findIndex)
import Char (chr, ord)
-- Meaning: Integral part; non-periodic fractional part; period.
data DecimalFraction = Frac Int String String
instance Show DecimalFraction where
show (Frac n x y) = show n ++ "." ++ x ++ (if null y then "" else "[" ++ y ++ "]")
-- Takes a list of Int's which should be between 0 and 9 and returns its reversed numeric representation as a string.
toString l = foldl (\acc x -> (chr (x + ord '0')) : acc) "" l
-- This function is the most important one. It takes two integers (the first one should be less
-- than the second) and an empty list and returns a tuple which consists of a fractional part and
-- a period, both of which are represented as integers.
divide_aux a c acc =
let b = a * 10 in
let q = b `div` c in
let r = b `mod` c in
case findIndex (\(q1, r1) -> (q1 == q) && (r1 == r)) acc of
Nothing -> divide_aux r c ((q, r):acc)
Just n -> let (period', fraction) = splitAt (n + 1) (map fst acc) in
let period = if period' == [0] then [] else period' in
(toString fraction, toString period)
-- The divisor itself; retreives the quotient and calls divide_aux with proper arguments.
divide a c =
let integral = a `div` c in
let b = a `mod` c in
let (frac, period) = divide_aux b c [] in
Frac integral frac period
-- Let's DIVIDE!
main =
mapM (\n -> putStrLn $ "1 / " ++ show n ++ " = " ++ show (divide 1 n)) $ [1..20] ++ [70, 170]