{-# LANGUAGE ScopedTypeVariables #-}

{-
   Ping Pong Factorial.

   Two processes calculate the factorial of the input.

   Note: this is not a fast, nor sensible way to compute factorials.
   It is merely intended to be used to demonstrate point-to-point
   communications.
-}
module Main where

import Control.Monad (when)
import Control.Parallel.MPI.Simple
import Control.Applicative ((<$>))
import Data.Char (isDigit)

type Msg = Either (Integer, Integer, Integer) Integer

zero, one :: Rank
zero = 0
one = 1

main :: IO ()
main = mpiWorld $ \size rank -> do
   when (size == 2) $ do
      when (rank == zero) $ do
         n <- getNumber
         send commWorld one unitTag (Left (n, 0, 1) :: Msg)
      result <- factorial $ switch rank
      when (rank == zero) $ print result

factorial :: Rank -> IO Integer
factorial rank = do
   (msg :: Msg) <- fst <$> recv commWorld rank unitTag
   case msg of
      Right answer -> return answer
      Left (n, count, acc)
         | count == n -> do
              send commWorld rank unitTag (Right acc :: Msg)
              return acc
         | otherwise -> do
              let nextCount = count + 1
              send commWorld rank unitTag (Left (n, nextCount, nextCount * acc) :: Msg)
              factorial rank

switch :: Rank -> Rank
switch rank
   | rank == zero = one
   | otherwise = zero

getNumber :: IO Integer
getNumber = do
    line <- getLine
    if all isDigit line
       then return $ read line
       else return 0
