git.fiddlerwoaroof.com
progs/prelude/PreludeArray.hs
4e987026
 module  PreludeArray ( Array, Assoc((:=)), array, listArray, (!), bounds,
 		     indices, elems, assocs, accumArray, (//), accum, amap,
 		     ixmap
 		   ) where
 
 {-#Prelude#-}  -- Indicates definitions of compiler prelude symbols
 
 -- This module uses some simple techniques with updatable vectors to
 -- avoid vector copying in loops where single threading is obvious.
 -- This is rather fragile and depends on the way the compiler handles
 -- strictness.
 
 import PreludeBltinArray
 
 infixl 9  !
 infixl 9  //
 infix  1  :=
 
 data  Assoc a b =  a := b  deriving (Eq, Ord, Ix, Text, Binary)
 data  (Ix a)    => Array a b = MkArray (a,a) {-#STRICT#-}
                                        (Vector (Box b)) {-#STRICT#-}
 				       deriving ()
 
 array		:: (Ix a) => (a,a) -> [Assoc a b] -> Array a b
 listArray	:: (Ix a) => (a,a) -> [b] -> Array a b
 (!)		:: (Ix a) => Array a b -> a -> b
 bounds		:: (Ix a) => Array a b -> (a,a)
 indices		:: (Ix a) => Array a b -> [a]
 elems		:: (Ix a) => Array a b -> [b]
 assocs		:: (Ix a) => Array a b -> [Assoc a b]
 accumArray	:: (Ix a) => (b -> c -> b) -> b -> (a,a) -> [Assoc a c]
 			     -> Array a b
 (//)		:: (Ix a) => Array a b -> [Assoc a b] -> Array a b
 accum		:: (Ix a) => (b -> c -> b) -> Array a b -> [Assoc a c]
 			     -> Array a b
 amap		:: (Ix a) => (b -> c) -> Array a b -> Array a c
 ixmap		:: (Ix a, Ix b) => (a,a) -> (a -> b) -> Array b c
 			     -> Array a c
 
 -- Arrays are a datatype containing a bounds pair and a vector of values.
 -- Uninitialized array elements contain an error value.
 
 -- Primitive vectors now contain only unboxed values.  This permits us to
 -- treat array indexing as an atomic operation without forcing the element
 -- being accessed.  The boxing and unboxing of array elements happens
 -- explicitly using these operations:
 
 data Box a = MkBox a
 unBox (MkBox x) = x
 {-# unBox :: Inline #-}
 
 
 -- Array construction and update using index/value associations share
 -- the same helper function.
 
 array b@(bmin, bmax) ivs =
   let size = (index b bmax) + 1
       v = primMakeVector size uninitializedArrayError
   in (MkArray b (updateArrayIvs b v ivs))
 {-# array :: Inline #-}
 
 a@(MkArray b v) // ivs =
   let v' = primCopyVector v
   in (MkArray b (updateArrayIvs b v' ivs))
 {-# (//) :: Inline #-}
 
 updateArrayIvs b v ivs = 
   let g (i := x) next =  strict1 (primVectorUpdate v (index b i) (MkBox x))
                                  next
   in foldr g v ivs
 {-# updateArrayIvs :: Inline #-}
 
 uninitializedArrayError = 
   MkBox (error "(!){PreludeArray}: uninitialized array element.")
 
 
 -- when mapping a list onto an array, be smart and don't do full index 
 -- computation
 
 listArray b@(bmin, bmax) vs =
   let size = (index b bmax) + 1
       v = primMakeVector size uninitializedArrayError
   in (MkArray b (updateArrayVs size v vs))
 {-# listArray :: Inline #-}
 
 updateArrayVs size v vs =
   let g x next j = if (j == size)
                      then v
 		     else strict1 (primVectorUpdate v j (MkBox x))
 		                  (next (j + 1))
   in foldr g (\ _ -> v) vs 0
 {-# updateArrayVs :: Inline #-}
 
 
 -- Array access
 
 a@(MkArray b v) ! i = unBox (primVectorSel v (index b i))
 {-# (!) :: Inline #-}
 
 bounds (MkArray b _)  = b
 
 indices		      = range . bounds
 
 
 -- Again, when mapping array elements into a list, be smart and don't do 
 -- the full index computation for every element.
 
 elems a@(MkArray b@(bmin, bmax) v) =
   build (\ c n -> 
           let size = (index b bmax) + 1
 	      g j  = if (j == size)
 	                then n
 			else c (unBox (primVectorSel v j)) (g (j + 1))
           -- This strict1 is so size doesn't get inlined and recomputed
 	  -- at every iteration.  It should also force the array argument
 	  -- to be strict.
           in strict1 size (g 0))
 {-# elems :: Inline #-}
 
 assocs a@(MkArray b@(bmin, bmax) v) =
   build (\ c n ->
           let g i next j = let y = unBox (primVectorSel v j)
                            in c (i := y) (next (j + 1))
 	  in foldr g (\ _ -> n) (range b) 0)
 {-# assocs :: Inline #-}
 
 
 -- accum and accumArray share the same helper function.  The difference is
 -- that accum makes a copy of an existing array and accumArray creates
 -- a new one with all elements initialized to the given value.
 
 accum f a@(MkArray b v) ivs =
   let v' = primCopyVector v
   in (MkArray b (accumArrayIvs f b v' ivs))
 {-# accum :: Inline #-}
 
 accumArray f z b@(bmin, bmax) ivs =
   let size = (index b bmax) + 1
       v = primMakeVector size (MkBox z)
   in (MkArray b (accumArrayIvs f b v ivs))
 {-# accumArray :: Inline #-}
 
 
 -- This is a bit tricky.  We need to force the access to the array element
 -- before the update, but not force the thunk that is the value of the
 -- array element unless f is strict.
 
 accumArrayIvs f b v ivs =
   let g (i := x) next = 
         let j = index b i
 	    y = primVectorSel v j
 	in strict1
 	     y
 	     (strict1 (primVectorUpdate v j (MkBox (f (unBox y) x)))
 	              next)
   in foldr g v ivs
 {-# accumArrayIvs :: Inline #-}
 
 
 -- again, be smart and bypass full array indexing on array mapping
 
 amap f a@(MkArray b@(bmin, bmax) v) =
   let size = (index b bmax) + 1
       v' = primMakeVector size uninitializedArrayError
       g j = if (j == size)
               then v'
 	      else let y = primVectorSel v j
 	           in strict1 (primVectorUpdate v' j (MkBox (f (unBox y))))
 	                      (g (j + 1))
   in (MkArray b (g 0))
 {-# amap :: Inline #-}
 
 
 -- can't bypass the index computation here since f needs it as an argument
 
 ixmap b f a           = array b [i := a ! f i | i <- range b]
 {-# ixmap :: Inline #-}
 
 
 -- random other stuff
 
 instance  (Ix a, Eq b)  => Eq (Array a b)  where
     a == a'  	        =  assocs a == assocs a'
 
 instance  (Ix a, Ord b) => Ord (Array a b)  where
     a <=  a'  	    	=  assocs a <=  assocs a'
 
 instance  (Ix a, Text a, Text b) => Text (Array a b)  where
     showsPrec p a = showParen (p > 9) (
 		    showString "array " .
 		    shows (bounds a) . showChar ' ' .
 		    shows (assocs a)                  )
 
     readsPrec p = readParen (p > 9)
 	   (\r -> [(array b as, u) | ("array",s) <- lex r,
 				     (b,t)       <- reads s,
 				     (as,u)      <- reads t   ]
 		  ++
 		  [(listArray b xs, u) | ("listArray",s) <- lex r,
 					 (b,t)           <- reads s,
 					 (xs,u)          <- reads t ])