{-# LANGUAGE MagicHash #-}

-- !!! simple tests of primitive arrays
--
module Main ( main ) where

import GHC.Exts
import Data.Char        ( chr )

import Control.Monad.ST
import Data.Array.ST
import Data.Array.Unboxed

import Data.Ratio

main = putStr
         (test_chars    ++ "\n"  ++
          test_ints     ++ "\n"  ++
          test_addrs    ++ "\n"  ++
          test_floats   ++ "\n"  ++
          test_doubles  ++ "\n"  ++
          test_ptrs     ++ "\n")


-- Arr# Char# -------------------------------------------
-- (main effort is in packString#)

test_chars :: String
test_chars
  = let arr# = f 1000
    in
        shows (lookup_range arr# 42# 416#) "\n"
  where
    f :: Int -> UArray Int Char

    f size@(I# size#)
      = runST (
            -- allocate an array of the specified size
          newArray_ (0, (size-1))       >>= \ arr# ->

            -- fill in all elements; elem i has "i" put in it
          fill_in arr# 0# (size# -# 1#) >>

            -- freeze the puppy:
          freeze arr#
        )

    fill_in :: STUArray s Int Char -> Int# -> Int# -> ST s ()

    fill_in arr_in# first# last#
      = if isTrue# (first# ># last#)
        then return ()
        else writeArray arr_in# (I# first#) ((chr (I# first#))) >>
             fill_in arr_in# (first# +# 1#) last#

    lookup_range :: UArray Int Char -> Int# -> Int# -> [Char]
    lookup_range arr from# to#
      = if isTrue# (from# ># to#)
        then []
        else (arr ! (I# from#))
             : (lookup_range arr (from# +# 1#) to#)

-- Arr# Int# -------------------------------------------

test_ints :: String
test_ints
  = let arr# = f 1000
    in
        shows (lookup_range arr# 42# 416#) "\n"
  where
    f :: Int -> UArray Int Int

    f size@(I# size#)
      = runST (
            -- allocate an array of the specified size
          newArray_ (0, (size-1))       >>= \ arr# ->

            -- fill in all elements; elem i has i^2 put in it
          fill_in arr# 0# (size# -# 1#) >>

            -- freeze the puppy:
          freeze arr#
        )

    fill_in :: STUArray s Int Int -> Int# -> Int# -> ST s ()

    fill_in arr_in# first# last#
      = if isTrue# (first# ># last#)
        then return ()
        else writeArray arr_in# (I# first#) (I# (first# *# first#)) >>
             fill_in arr_in# (first# +# 1#) last#

    lookup_range :: UArray Int Int -> Int# -> Int# -> [Int]
    lookup_range arr from# to#
      = if isTrue# (from# ># to#)
        then []
        else (arr ! (I# from#))
             : (lookup_range arr (from# +# 1#) to#)

-- Arr# Addr# -------------------------------------------

test_addrs :: String
test_addrs
  = let arr# = f 1000
    in
        shows (lookup_range arr# 42# 416#) "\n"
  where
    f :: Int -> UArray Int (Ptr ())

    f size@(I# size#)
      = runST (
            -- allocate an array of the specified size
          newArray_ (0, (size-1))       >>= \ arr# ->

            -- fill in all elements; elem i has i^2 put in it
          fill_in arr# 0# (size# -# 1#) >>

            -- freeze the puppy:
          freeze arr#
        )

    fill_in :: STUArray s Int (Ptr ()) -> Int# -> Int# -> ST s ()

    fill_in arr_in# first# last#
      = if isTrue# (first# ># last#)
        then return ()
        else writeArray arr_in# (I# first#)
                            (Ptr (int2Addr# (first# *# first#))) >>
             fill_in arr_in# (first# +# 1#) last#

    lookup_range :: UArray Int (Ptr ()) -> Int# -> Int# -> [ Int ]
    lookup_range arr from# to#
      = let
            a2i (Ptr a#) = I# (addr2Int# a#)
        in
        if isTrue# (from# ># to#)
        then []
        else (a2i (arr ! (I# from#)))
             : (lookup_range arr (from# +# 1#) to#)

-- Arr# Float# -------------------------------------------

test_floats :: String
test_floats
  = let arr# = f 1000
    in
        shows (lookup_range arr# 42# 416#) "\n"
  where
    f :: Int -> UArray Int Float

    f size@(I# size#)
      = runST (
            -- allocate an array of the specified size
          newArray_ (0, (size-1))       >>= \ arr# ->

            -- fill in all elements; elem i has "i * pi" put in it
          fill_in arr# 0# (size# -# 1#) >>

            -- freeze the puppy:
          freeze arr#
        )

    fill_in :: STUArray s Int Float -> Int# -> Int# -> ST s ()

    fill_in arr_in# first# last#
      = if isTrue# (first# ># last#)
        then return ()
{-      else let e = ((fromIntegral (I# first#)) * pi)
             in trace (show e) $ writeFloatArray arr_in# (I# first#) e >>
             fill_in arr_in# (first# +# 1#) last#
-}
        else writeArray arr_in# (I# first#) ((fromIntegral (I# first#)) * pi) >>
             fill_in arr_in# (first# +# 1#) last#

    lookup_range :: UArray Int Float -> Int# -> Int# -> [Float]
    lookup_range arr from# to#
      = if isTrue# (from# ># to#)
        then []
        else (arr ! (I# from#))
             : (lookup_range arr (from# +# 1#) to#)

-- Arr# Double# -------------------------------------------

test_doubles :: String
test_doubles
  = let arr# = f 1000
    in
        shows (lookup_range arr# 42# 416#) "\n"
  where
    f :: Int -> UArray Int Double

    f size@(I# size#)
      = runST (
            -- allocate an array of the specified size
          newArray_ (0, (size-1))       >>= \ arr# ->

            -- fill in all elements; elem i has "i * pi" put in it
          fill_in arr# 0# (size# -# 1#) >>

            -- freeze the puppy:
          freeze arr#
        )

    fill_in :: STUArray s Int Double -> Int# -> Int# -> ST s ()

    fill_in arr_in# first# last#
      = if isTrue# (first# ># last#)
        then return ()
        else writeArray arr_in# (I# first#) ((fromIntegral (I# first#)) * pi) >>
             fill_in arr_in# (first# +# 1#) last#

    lookup_range :: UArray Int Double -> Int# -> Int# -> [Double]
    lookup_range arr from# to#
      = if isTrue# (from# ># to#)
        then []
        else (arr ! (I# from#))
             : (lookup_range arr (from# +# 1#) to#)

-- Arr# (Ratio Int) (ptrs) ---------------------------------
-- just like Int# test

test_ptrs :: String
test_ptrs
  = let arr# = f 1000
    in
        shows (lookup_range arr# 42 416) "\n"
  where
    f :: Int -> Array Int (Ratio Int)

    f size
      = runST (
          newArray (1, size) (3 % 5)    >>= \ arr# ->
          -- don't fill in the whole thing
          fill_in arr# 1 400            >>
          freeze arr#
        )

    fill_in :: STArray s Int (Ratio Int) -> Int -> Int -> ST s ()

    fill_in arr_in# first last
      = if (first > last)
        then return ()
        else writeArray arr_in# first (fromIntegral (first * first)) >>
             fill_in  arr_in# (first + 1) last

    lookup_range :: Array Int (Ratio Int) -> Int -> Int -> [Ratio Int]
    lookup_range array from too
      = if (from > too)
        then []
        else (array ! from) : (lookup_range array (from + 1) too)
