Benchmarks Game/Parallel/Fannkuch

From HaskellWiki
< Benchmarks Game‎ | Parallel
Revision as of 07:17, 5 October 2008 by Rwbarton (talk | contribs)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search
The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.
{-# OPTIONS -fglasgow-exts -O2 -optc-O3 #-}

-- The Great Computer Language Shootout
-- http://shootout.alioth.debian.org/
-- contributed by Don Stewart, translation from the C version
-- Parallelized by Reid Barton

import Control.Concurrent
import Control.Monad
import Data.List
import Foreign
import System
import GHC.Base
import GHC.Ptr
import GHC.IOBase

main = do
    n <- getArgs >>= return . read . head
    when (n <= 1) $ putStrLn "Pfannkuchen(1) = 0" >> exitWith ExitSuccess
    fannkuchSmall n
    chan <- newChan :: IO (Chan Int)
    -- The C version ignores permutations ending in n-1,
    -- so let's do so even more efficiently.
    forM_ [0..n-2] $ \l -> forkIO (fannkuch n l >>= writeChan chan)
    k <- (maximum . take (n-1)) `fmap` getChanContents chan
    putStrLn $ "Pfannkuchen(" ++ show n ++ ") = " ++ show (k - 1)

-- Separate loop to print the small values
fannkuchSmall n@(I# n#) = do
    perm            <- mallocArray n       :: IO (Ptr Int)
    (Ptr c#)        <- mallocArray n       :: IO (Ptr Int)
    perm1@(Ptr p1#) <- newArray [0 .. n-1] :: IO (Ptr Int)
    (Ptr rP)        <- newArray [n]        :: IO (Ptr Int)

    let go didpr = when (didpr < (30 :: Int)) $ do
            ppr 0 n perm1 >> putStr "\n"

            IO $ \s ->
                case readIntOffAddr# rP 0# s of
                    (# s, r# #) -> case setcount c# r# s of
                        (# s, _ #) -> case writeIntOffAddr# rP 0# 1# s of
                            s -> (# s, () #)

            done <- IO $ \s -> rot rP n# p1# c# s
            if done then return () else go (didpr+1)
    go 0

fannkuch n@(I# n#) l = do       -- Only consider permutations ending with l.
    let startArray = ([0..n-1] \\ [l]) ++ [l]
    perm            <- mallocArray n       :: IO (Ptr Int)
    (Ptr c#)        <- mallocArray n       :: IO (Ptr Int)
    perm1@(Ptr p1#) <- newArray startArray :: IO (Ptr Int)
    (Ptr rP)        <- newArray [n]        :: IO (Ptr Int)
    (Ptr flipsMaxP) <- newArray [0]        :: IO (Ptr Int)

    let go = do
            IO $ \s ->
                case readIntOffAddr# rP 0# s of
                    (# s, r# #) -> case setcount c# r# s of
                        (# s, _ #) -> case writeIntOffAddr# rP 0# 1# s of
                            s -> (# s, () #)

            t <- IO $ \s ->
                case readIntOffAddr# p1# 0# s of
                    (# s, p1 #) -> (# s, not (p1 ==# 0#) #)

            when t $ exchange n perm perm1 flipsMaxP

            fm   <- IO $ \s -> case readIntOffAddr# flipsMaxP 0# s of
                                (# s, x #) -> (# s, I# x #)
            done <- IO $ \s -> rot rP (n# -# 1#) p1# c# s
            if done then return fm else go
    go

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

exchange n p@(Ptr a) p1@(Ptr b) fm = do
    copyArray (p `advancePtr` 1) (p1 `advancePtr` 1) (n-1)
    IO $ \s ->
        case readIntOffAddr# b 0# s  of { (# s, k #) ->
        case doswap k a 0# s         of { (# s, f #) ->
        case readIntOffAddr# fm 0# s of { (# s, m #) ->
        if m <# f then case writeIntOffAddr# fm 0# f s of s -> (# s, () #)
                  else (# s, () #)
        } } }
{-# INLINE exchange #-}

doswap k a f s =
    case swap 1# (k -# 1#) a s    of { (# s, _ #) ->
    case readIntOffAddr# a k s    of { (# s, j #) ->
    case writeIntOffAddr# a k k s of { s          ->
    if k /=# 0# then doswap j a (f +# 1#) s else (# s, (f +# 1#) #)
    } } }
{-# INLINE doswap #-}

swap i j a s =
    if i <# j then case readIntOffAddr#  a i s   of { (# s, x #) ->
                   case readIntOffAddr#  a j s   of { (# s, y #) ->
                   case writeIntOffAddr# a j x s of { s          ->
                   case writeIntOffAddr# a i y s of { s          ->
                   swap (i +# 1#) (j -# 1#) a s
                   } } } }
              else (# s, () #)
{-# INLINE swap #-}

loop r i a s =
    if i <# r then case readIntOffAddr# a (i +# 1#) s of
                    (# s, x #) -> case writeIntOffAddr# a i x s of
                        s -> loop r (i +# 1#) a s
              else (# s, () #)
{-# INLINE loop #-}

setcount p r s =
    if r ==# 1# then (# s, () #)
                else case writeIntOffAddr# p (r -# 1#) r s of
                        s -> setcount p (r -# 1#) s
{-# INLINE setcount #-}

rot rP n a cp s =
  case readIntOffAddr# rP 0# s of { (# s, r #) ->
  if r ==# n then (# s, True #)
             else case readIntOffAddr# a 0# s             of { (# s, p0 #) ->
                  case loop r 0# a s                      of { (# s, _ #)  ->
                  case writeIntOffAddr# a r p0 s          of { s           ->
                  case readIntOffAddr# cp r s             of { (# s, cr #) ->
                  case writeIntOffAddr# cp r (cr -# 1#) s of { s           ->
                  if cr -# 1# ># 0# then (# s, False #)
                                    else case inc s of s -> rot rP n a cp s
  } } } } } }
  where inc s = case readIntOffAddr# rP 0# s of
                  (# s, x #) -> writeIntOffAddr# rP 0# (x +# 1#) s
{-# INLINE rot #-}

ppr i n p = when (i < n) $ do
    putStr . show . (+1) =<< peek (p `advancePtr` i)
    ppr (i+1) n p