primeFactors :: Int         -- Number to factor
             -> [FactorExp] -- prime factors of the number
primeFactors n = primeFactors' n (map fromIntegral primes) []
  where
    primeFactors' :: Int         -- number to factor
                  -> [Prime]     -- primes
                  -> [FactorExp] -- accumulator
                  -> [FactorExp] -- prime factors of the number
    primeFactors' n (p:ps) acc
        | n == 1       = acc
        | n < p*p      = (n, 1):acc
        | rem n p == 0 = primeFactors' n' ps ((p, exp):acc)
        | otherwise    = primeFactors' n ps acc
      where
        (n', exp) = reduce (n, 0)

        {- reduce (n, a) -> (m, b)  

        Reduces the value n*p^a with regards to the current prime p, i.e. that
        n * p^a = m * p^b, with b the greatest possible value.
        
        As a pecial case: reduce (n, 0) = (m, b) => n = m * p^b
        -}
        reduce :: (Int, Int) -> (Int, Int)
        reduce arg@(n, a) | rem n p == 0 = reduce (quot n p, a+1) 
                          | otherwise    = arg

-- Returns the divisors of a number
primeFactors :: Int         -- Number to factor
             -> [FactorExp]
primeFactors n = primeFactors' n (map fromIntegral primes) []
  where
    primeFactors' :: Int         -- number to factor
                  -> [Prime]     -- primes
                  -> [FactorExp] -- accumulator
                  -> [FactorExp] -- prime factors of the number
    primeFactors' n (p:ps) acc
        | n == 1       = acc
        | n < p*p      = (n, 1):acc
        | rem n p == 0 = primeFactors' n' ps ((p, exp):acc)
        | otherwise    = primeFactors' n ps acc
      where
        (n', exp) = reduce (n, 0)

        {- reduce (n, a) -> (m, b)  

        Reduces the value n*p^a with regards to the current prime p, i.e. that
        n * p^a = m * p^b, with b the greatest possible value.
        
        As a pecial case: reduce (n, 0) = (m, b) => n = m * p^b
        -}
        reduce :: (Int, Int) -> (Int, Int)
        reduce arg@(n, a) | rem n p == 0 = reduce (quot n p, a+1) 
                          | otherwise    = arg

-- Returns the divisors of a number
primeFactors n = primeFactors' n (map fromIntegral primes) []
  where
    primeFactors' :: Int         -- number to factor
                  -> [Prime]     -- primes
                  -> [FactorExp] -- accumulator
                  -> [FactorExp] -- prime factors of the number
    primeFactors' n (p:ps) acc
        | n == 1       = acc
        | n < p*p      = (n, 1):acc
        | rem n p == 0 = primeFactors' n' ps ((p, exp):acc)
        | otherwise    = primeFactors' n ps acc
      where
        (n', exp) = reduce (n, 0)

        {- reduce (n, a) -> (m, b)  

        Reduces the value n*p^a with regards to the current prime p, i.e. that
        n * p^a = m * p^b, with b the greatest possible value.
        
        As a pecial case: reduce (n, 0) = (m, b) => n = m * p^b
        -}
        reduce :: (Int, Int) -> (Int, Int)
        reduce arg@(n, a) | rem n p == 0 = reduce (quot n p, a+1) 
                          | otherwise    = arg

-- Returns the divisors of a number