-- |
-- Module      : Data.Array.Accelerate
-- Copyright   : [2008..2017] Manuel M T Chakravarty, Gabriele Keller
--               [2009..2017] Trevor L. McDonell
--               [2013..2017] Robert Clifton-Everest
--               [2014..2014] Frederik M. Madsen
-- License     : BSD3
--
-- Maintainer  : Trevor L. McDonell <tmcdonell@cse.unsw.edu.au>
-- Stability   : experimental
-- Portability : non-portable (GHC extensions)
--
-- @Data.Array.Accelerate@ defines an embedded language of array computations
-- for high-performance computing in Haskell. Computations on multi-dimensional,
-- regular arrays are expressed in the form of parameterised collective
-- operations such as maps, reductions, and permutations. These computations are
-- online compiled and can be executed on a range of architectures.
--
-- [/Abstract interface:/]
--
-- The types representing array computations are only exported abstractly;
-- client code can generate array computations and submit them for execution,
-- but it cannot inspect these computations. This is to allow for more
-- flexibility for future extensions of this library.
--
-- [/Stratified language:/]
--
-- Accelerate distinguishes the types of collective operations 'Acc' from the
-- type of scalar operations 'Exp' to achieve a stratified language. Collective
-- operations comprise many scalar computations that are executed in parallel,
-- but scalar computations /can not/ contain collective operations. This
-- separation excludes /nested, irregular/ data-parallelism statically; instead,
-- Accelerate is limited to /flat data-parallelism/ involving only regular,
-- multi-dimensional arrays.
--
-- [/Optimisations:/]
--
-- Accelerate uses a number of scalar and array optimisations, including
-- /array fusion/, in order to improve the performance of programs. Fusing
-- a program entails combining successive traversals (loops) over an array into
-- a single traversal, which reduces memory traffic and eliminates intermediate
-- arrays.
--
-- [/Code execution:/]
--
-- Several backends are available which can be used to evaluate accelerate
-- programs:
--
-- * "Data.Array.Accelerate.Interpreter": simple interpreter in Haskell as a
--   reference implementation defining the semantics of the Accelerate language
--
-- * <http://hackage.haskell.org/package/accelerate-llvm-native accelerate-llvm-native>:
--   implementation supporting parallel execution on multicore CPUs (e.g. x86).
--
-- * <http://hackage.haskell.org/package/accelerate-llvm-ptx accelerate-llvm-ptx>:
--   implementation supporting parallel execution on CUDA-capable NVIDIA GPUs.
--
-- [/Examples:/]
--
-- * The <http://hackage.haskell.org/package/accelerate-examples accelerate-examples>
--   package demonstrates a range of computational kernels and several complete
--   applications:
--
--      - Implementation of the <https://en.wikipedia.org/wiki/Canny_edge_detector canny edge detector>
--      - Interactive <https://en.wikipedia.org/wiki/Mandelbrot_set Mandelbrot set> generator
--      - <https://en.wikipedia.org/wiki/N-body N-body simulation> of gravitational attraction between large bodies
--      - Implementation of the <https://en.wikipedia.org/wiki/Pagerank PageRank> algorithm
--      - A simple, real-time, interactive <https://en.wikipedia.org/wiki/Ray_tracing ray tracer>.
--      - A particle based simulation of stable fluid flows
--      - A cellular automaton simulation
--      - A "password recovery" tool, for dictionary attacks on MD5 hashes.
--
--      <<http://i.imgur.com/5Tbsp1j.jpg accelerate-mandelbrot>>
--      <<http://i.imgur.com/7ohhKm9.jpg accelerate-ray>>
--
-- * <http://hackage.haskell.org/package/lulesh-accelerate lulesh-accelerate>
--   is an implementation of the Livermore Unstructured Lagrangian Explicit
--   Shock Hydrodynamics (LULESH) application. LULESH is representative of
--   typical hydrodynamics codes, although simplified and hard-coded to solve
--   the Sedov blast problem on an unstructured hexahedron mesh.
--
--      - For more information on LULESH: <https://codesign.llnl.gov/lulesh.php>.
--
--      <<https://codesign.llnl.gov/images/sedov-3d-LLNL.png>>
--
-- [/Additional components:/]
--
-- * <https://hackage.haskell.org/package/accelerate-io accelerate-io>: Fast
-- conversion between Accelerate arrays and other formats (e.g. Repa, Vector).
--
-- * <https://hackage.haskell.org/package/accelerate-fft accelerate-fft>: Fast
-- Fourier transform, with FFI bindings to optimised implementations.
--
-- * <https://hackage.haskell.org/package/accelerate-blas accelerate-blas>: BLAS
-- and LAPACK operations, with FFI bindings to optimised implementations.
--
-- * <https://hackage.haskell.org/package/accelerate-bignum accelerate-bignum>:
-- Fixed-width large integer arithmetic.
--
-- * <https://hackage.haskell.org/package/colour-accelerate colour-accelerate>:
-- Colour representations in Accelerate (RGB, sRGB, HSV, and HSL).
--
-- * <https://hackage.haskell.org/package/gloss-accelerate gloss-accelerate>:
-- Generate <https://hackage.haskell.org/package/gloss gloss> pictures from
-- Accelerate.
--
-- * <https://hackage.haskell.org/package/gloss-raster-accelerate gloss-raster-accelerate>:
-- Parallel rendering of raster images and animations.
--
-- * <https://hackage.haskell.org/package/lens-accelerate lens-accelerate>:
-- <https://hackage.haskell.org/package/lens Lens> operators for Accelerate
-- types.
--
-- * <https://hackage.haskell.org/package/linear-accelerate linear-accelerate>:
-- <https://hackage.haskell.org/package/linear Linear> vector space types for
-- Accelerate.
--
-- * <https://hackage.haskell.org/package/mwc-random-accelerate mwc-random-accelerate>:
-- Generate Accelerate arrays filled with high-quality pseudorandom numbers.
--
-- [/Contact:/]
--
-- * Mailing list for both use and development discussion:
--
--     * <mailto:accelerate-haskell@googlegroups.com>
--     * http://groups.google.com/group/accelerate-haskell
--
-- * Bug reports: https://github.com/AccelerateHS/accelerate/issues
--
-- * Maintainers:
--
--     * Trevor L. McDonell: <mailto:tmcdonell@cse.unsw.edu.au>
--     * Manuel M T Chakravarty: <mailto:chak@cse.unsw.edu.au>
--
-- [/Tip:/]
--
-- Accelerate tends to stress GHC's garbage collector, so it helps to increase
-- the default GC allocation sizes. This can be done when running an executable
-- by specifying RTS options on the command line, for example:
--
-- > ./foo +RTS -A64M -n2M -RTS
--
-- You can make these settings the default by adding the following @ghc-options@
-- to your @.cabal@ file or similar:
--
-- > ghc-options: -with-rtsopts=-n2M -with-rtsopts=-A64M
--
-- To specify RTS options you will also need to compile your program with @-rtsopts@.
--

module Data.Array.Accelerate (

  -- * The /Accelerate/ Array Language
  -- ** Embedded array computations
  Acc,

  -- *** Arrays
  Array, Arrays, Scalar, Vector, Segments,

  -- *** Array elements
  Elt,

  -- *** Array shapes & indices
  -- $shapes_and_indices
  --
  Z(..), (:.)(..),
  DIM0, DIM1, DIM2, DIM3, DIM4, DIM5, DIM6, DIM7, DIM8, DIM9,
  Shape, Slice(..), All(..), Any(..),
  -- Split(..), Divide(..), Division(..),

  -- ** Array access
  -- *** Element indexing
  (!), (!!), the,

  -- *** Shape information
  null, length, shape, size, shapeSize,

  -- ** Construction
  -- *** Introduction
  use, unit,

  -- *** Initialisation
  generate, fill,

  -- *** Enumeration
  enumFromN, enumFromStepN,

  -- *** Concatenation
  (++),

  -- ** Composition
  -- *** Flow control
  (?|), acond, awhile,
  IfThenElse(..),

  -- *** Controlling execution
  (>->),
  compute,

  -- ** Element-wise operations
  -- *** Indexing
  indexed,

  -- *** Mapping
  map, imap,

  -- *** Zipping
  zipWith, zipWith3, zipWith4, zipWith5, zipWith6, zipWith7, zipWith8, zipWith9,
  izipWith, izipWith3, izipWith4, izipWith5, izipWith6, izipWith7, izipWith8, izipWith9,
  zip, zip3, zip4, zip5, zip6, zip7, zip8, zip9,

  -- *** Unzipping
  unzip, unzip3, unzip4, unzip5, unzip6, unzip7, unzip8, unzip9,

  -- ** Modifying Arrays
  -- *** Shape manipulation
  reshape, flatten,

  -- *** Replication
  replicate,

  -- *** Extracting sub-arrays
  slice,
  init, tail, take, drop, slit,

  -- *** Permutations
  -- **** Forward permutation (scatter)
  permute,
  ignore,
  scatter,

  -- **** Backward permutation (gather)
  backpermute,
  gather,

  -- **** Specialised permutations
  reverse, transpose,

  -- *** Filtering
  filter,

  -- ** Folding
  fold, fold1, foldAll, fold1All,

  -- *** Segmented reductions
  foldSeg, fold1Seg,

  -- *** Specialised reductions
  all, any, and, or, sum, product, minimum, maximum,

  -- ** Scans (prefix sums)
  scanl, scanl1, scanl', scanr, scanr1, scanr',
  prescanl, postscanl, prescanr, postscanr,

  -- *** Segmented scans
  scanlSeg, scanl1Seg, scanl'Seg, prescanlSeg, postscanlSeg,
  scanrSeg, scanr1Seg, scanr'Seg, prescanrSeg, postscanrSeg,

  -- ** Stencils
  stencil, stencil2,

  -- *** Stencil specification
  Stencil, Boundary,
  clamp, mirror, wrap, function,

  -- *** Common stencil patterns
  Stencil3, Stencil5, Stencil7, Stencil9,
  Stencil3x3, Stencil5x3, Stencil3x5, Stencil5x5,
  Stencil3x3x3, Stencil5x3x3, Stencil3x5x3, Stencil3x3x5, Stencil5x5x3, Stencil5x3x5,
  Stencil3x5x5, Stencil5x5x5,

  -- -- ** Sequence operations
  -- collect,

  -- -- ** Sequence producers
  -- streamIn, toSeq, generateSeq,

  -- -- ** Sequence transducers
  -- mapSeq, zipWithSeq, scanSeq,

  -- -- ** Sequence consumers
  -- foldSeq, foldSeqFlatten, fromSeq, fromSeqElems, fromSeqShapes,
  -- toSeqInner, toSeqOuter2, toSeqOuter3,

  -- ---------------------------------------------------------------------------
  -- * The /Accelerate/ Expression Language
  -- ** Scalar data types
  Exp,

  -- ** Type classes
  -- *** Basic type classes
  Eq(..),
  Ord(..),
  -- Enum, -- vacuous
  Bounded, minBound, maxBound,

  -- *** Numeric type classes
  Num, (+), (-), (*), negate, abs, signum, fromInteger,
  -- Real, -- vacuous
  Integral, quot, rem, div, mod, quotRem, divMod,
  Fractional, (/), recip, fromRational,
  Floating, pi, sin, cos, tan, asin, acos, atan, sinh, cosh, tanh, asinh, acosh, atanh, exp, sqrt, log, (**), logBase,
  RealFrac(..), div', mod', divMod',
  RealFloat(..),

  -- *** Numeric conversion classes
  FromIntegral(..),
  ToFloating(..),

  -- ** Lifting and Unlifting
  -- $lifting_and_unlifting
  --
  Lift(..), Unlift(..),
  lift1, lift2, lift3,
  ilift1, ilift2, ilift3,

  -- ** Scalar operations
  -- *** Introduction
  constant,

  -- *** Tuples
  fst, afst, snd, asnd, curry, uncurry,

  -- *** Flow control
  (?), caseof, cond, while, iterate,

  -- *** Scalar reduction
  sfoldl,

  -- *** Logical operations
  (&&), (||), not,

  -- *** Numeric operations
  subtract, even, odd, gcd, lcm, (^), (^^),

  -- *** Shape manipulation
  index0, index1, unindex1, index2, unindex2, index3, unindex3,
  indexHead, indexTail,
  toIndex, fromIndex,
  intersect,

  -- *** Conversions
  ord, chr, boolToInt, bitcast,

  -- ---------------------------------------------------------------------------
  -- * Foreign Function Interface (FFI)
  foreignAcc,
  foreignExp,

  -- ---------------------------------------------------------------------------
  -- * Plain arrays
  -- ** Operations
  arrayRank, arrayShape, arraySize, indexArray,

  -- ** Getting data in
  -- $getting_data_in

  -- *** Function
  fromFunction,

  -- *** Lists
  fromList, toList,

  -- ---------------------------------------------------------------------------
  -- * Prelude re-exports
  (.), ($), error, undefined, const,

  -- ---------------------------------------------------------------------------
  -- Types
  Int, Int8, Int16, Int32, Int64,
  Word, Word8, Word16, Word32, Word64,
  Float, Double,
  Bool(..), Char,

  CFloat, CDouble,
  CShort, CUShort, CInt, CUInt, CLong, CULong, CLLong, CULLong,
  CChar, CSChar, CUChar,

  -- | Avoid using these in your own functions wherever possible.
  IsScalar, IsNum, IsBounded, IsIntegral, IsFloating, IsNonNum,

) where

-- friends
import Data.Array.Accelerate.Array.Sugar                            hiding ( (!), rank, shape, size, toIndex, fromIndex, intersect, ignore )
import Data.Array.Accelerate.Classes
import Data.Array.Accelerate.Language
import Data.Array.Accelerate.Prelude
import Data.Array.Accelerate.Trafo                                  () -- show instances
import Data.Array.Accelerate.Type
import qualified Data.Array.Accelerate.Array.Sugar                  as S

-- re-exported from D.A.A.Classes.Num but not found (GHC<8 bug)
import Prelude                                                      ( (.), ($), undefined, error, const, fromInteger )


-- Renamings
-- ---------
--
-- FIXME: these all need to go into a separate module for separate importing!

-- rename as '(!)' is already used by the EDSL for indexing

-- | Array indexing in plain Haskell code.
--
indexArray :: Array sh e -> sh -> e
indexArray = (S.!)

-- | Rank of an array.
--
arrayRank :: Shape sh => sh -> Int
arrayRank = S.rank

-- |Array shape in plain Haskell code.
--
arrayShape :: Shape sh => Array sh e -> sh
arrayShape = S.shape
-- rename as 'shape' is already used by the EDSL to query an array's shape

-- | Total number of elements in an array of the given 'Shape'.
--
arraySize :: Shape sh => sh -> Int
arraySize = S.size

-- Named documentation chunks
-- --------------------------

-- $shapes_and_indices
--
-- Operations in Accelerate take the form of collective operations over arrays
-- of the type @'Array' sh e@. Much like the
-- <https://hackage.haskell.org/package/repa repa> library, arrays in Accelerate
-- are parameterised by a type /sh/ which determines the dimensionality of the
-- array and the type of each index, as well as the type of each element of the
-- array /e/.
--
-- Shape types, and multidimensional array indices, are built like lists
-- (technically; a heterogeneous snoc-list) using 'Z' and (':.'):
--
-- > data Z = Z
-- > data tail :. head = tail :. head
--
-- Here, the constructor 'Z' corresponds to a shape with zero dimension (or
-- a 'Scalar' array, with one element) and is used to mark the end of the list.
-- The constructor (':.') adds additional dimensions to the shape on the
-- /right/. For example:
--
-- > Z :. Int
--
-- is the type of the shape of a one-dimensional array ('Vector') indexed by an
-- 'Int', while:
--
-- > Z :. Int :. Int
--
-- is the type of the shape of a two-dimensional array (a matrix) indexed by an
-- 'Int' in each dimension.
--
-- This style is used to construct both the /type/ and /value/ of the shape. For
-- example, to define the shape of a vector of ten elements:
--
-- > sh :: Z :. Int
-- > sh = Z :. 10
--
-- Note that the right-most index is the /innermost/ dimension. This is the
-- fastest-varying index, and corresponds to the elements of the array which are
-- adjacent in memory.
--

-- $lifting_and_unlifting
--
-- A value of type 'Int' is a plain Haskell value (unlifted), whereas an @Exp
-- Int@ is a /lifted/ value, that is, an integer lifted into the domain of
-- embedded expressions (an abstract syntax tree in disguise). Both 'Acc' and
-- 'Exp' are /surface types/ into which values may be lifted. Lifting plain
-- array and scalar surface types is equivalent to 'use' and 'constant'
-- respectively.
--
-- In general an @Exp Int@ cannot be unlifted into an 'Int', because the actual
-- number will not be available until a later stage of execution (e.g. during
-- GPU execution, when 'run' is called). Similarly an @Acc array@ can not be
-- unlifted to a vanilla 'array'; you should instead 'run' the expression with
-- a specific backend to evaluate it.
--
-- Lifting and unlifting are also used to pack and unpack an expression into and
-- out of constructors such as tuples, respectively. Those expressions, at
-- runtime, will become tuple dereferences. For example:
--
-- >>> let sh = constant (Z :. 4 :. 10)   :: Exp DIM2
-- >>> let Z :. x :. y = unlift sh        :: Z :. Exp Int :. Exp Int
-- >>> let t = lift (x,y)                 :: Exp (Int, Int)
--
-- >>> let r  = scanl' f z xs             :: (Acc (Vector Int), Acc (Scalar Int))
-- >>> let r' = lift r                    :: Acc (Vector Int, Scalar Int)
--
-- [/Note:/]
--
-- Use of 'lift' and 'unlift' is probably the most common source of type errors
-- when using Accelerate. GHC is not very good at determining the type the
-- [un]lifted expression should have, so it is often necessary to add an
-- explicit type signature.
--
-- For example, in the following GHC will complain that it can not determine the
-- type of 'y', even though we might expect that to be obvious (or for it to not
-- care):
--
-- > fst :: (Elt a, Elt b) => Exp (a,b) -> Exp a
-- > fst t = let (x,y) = unlift t in x
--
-- The fix is to instead add an explicit type signature. Note that this requires
-- the @ScopedTypeVariables@ extension and to bring the type variables @a@ and
-- @b@ into scope with @forall@:
--
-- > fst :: forall a b. (Elt a, Elt b) => Exp (a,b) -> Exp a
-- > fst t = let (x,y) = unlift t  :: (Exp a, Exp b)
-- >         in x
--

-- $getting_data_in
-- #getting_data_in#
--
-- We often need to generate or read data into an 'Array' so that it can be used
-- in Accelerate. The base @accelerate@ library includes basic conversions
-- routines, but for additional functionality see the
-- <http://hackage.haskell.org/package/accelerate-io accelerate-io> package,
-- which includes conversions between:
--
--  * <https://hackage.haskell.org/package/repa repa>: another Haskell library for high-performance parallel arrays
--  * <https://hackage.haskell.org/package/vector vector>: efficient boxed and unboxed one-dimensional arrays
--  * <https://hackage.haskell.org/package/array array>: immutable arrays
--  * <https://hackage.haskell.org/package/bmp BMP>: uncompressed BMP image files
--  * <https://hackage.haskell.org/package/bytestring bytestring> compact, immutable binary data
--  * As well as copying data directly from raw 'Foreign.Ptr.Ptr's
--