content: import Control.Monad.Memo (Memo, memo, startEvalMemo)
  import Data.List.Split (chunksOf)
  import System.Environment (getArgs)
  import Text.Tabular (Header(..), Properties(..), Table(..))
  import Text.Tabular.AsciiArt (render)
  
  type SudanArgs = (Int, Integer, Integer)
  
  -- Given argument (n, x, y) calculate Fₙ(x, y).  For performance reasons we do
  -- the calculation in a memoization monad.
  sudan :: SudanArgs -> Memo SudanArgs Integer Integer
  sudan (0, x, y) = return $ x + y
  sudan (_, x, 0) = return x
  sudan (n, x, y) = memo sudan (n, x, y-1) >>= \x2 -> sudan (n-1, x2, x2 + y)
  
  -- A table of Fₙ(x, y) values, where the rows are y values and the columns are
  -- x values.
  sudanTable :: Int -> [Integer] -> [Integer] -> String
  sudanTable n xs ys = render show show show
                     $ Table (Group NoLine $ map Header ys)
                             (Group NoLine $ map Header xs)
                     $ chunksOf (length xs)
                     $ startEvalMemo
                     $ sequence
                     $ [sudan (n, x, y) | y <- ys, x <- xs]
  
  main :: IO ()
  main = do
    args <- getArgs
    case args of
      [n, xlo, xhi, ylo, yhi] -> do
        putStrLn $ "Fₙ(x, y), where the rows are y values " ++
                   "and the columns are x values.\n"
        putStr $ sudanTable (read n)
                            [read xlo .. read xhi]
                            [read ylo .. read yhi]
      _ -> error "Usage: sudan n xmin xmax ymin ymax"
  