content: {-# OPTIONS_GHC -fno-warn-duplicate-constraints #-}
  {-# LANGUAGE RankNTypes #-}
  
  import Data.Array (Array, Ix)
  import Data.Array.Base
  
  -- | Element-wise combine the values of two arrays 'a' and 'b' with 'f'.
  -- 'a' and 'b' must have the same bounds.
  zipWithA :: (IArray arr a, IArray arr b, IArray arr c, Ix i) =>
              (a -> b -> c) -> arr i a -> arr i b -> arr i c
  zipWithA f a b =
    case bounds a of
      ba ->
        if ba /= bounds b
        then error "elemwise: bounds mismatch"
        else
          let n = numElements a
          in unsafeArray ba [ (i, f (unsafeAt a i) (unsafeAt b i))
                            | i <- [0 .. n - 1]]
  
  -- Convenient aliases for matrix-matrix element-wise operations.
  type ElemOp a b c = (IArray arr a, IArray arr b, IArray arr c, Ix i) =>
                      arr i a -> arr i b -> arr i c
  type ElemOp1 a = ElemOp a a a
  
  infixl 6 +:, -:
  infixl 7 *:, /:, `divE`
  
  (+:), (-:), (*:) :: (Num a) => ElemOp1 a
  (+:) = zipWithA (+)
  (-:) = zipWithA (-)
  (*:) = zipWithA (*)
  
  divE :: (Integral a) => ElemOp1 a
  divE = zipWithA div
  
  (/:) :: (Fractional a) => ElemOp1 a
  (/:) = zipWithA (/)
  
  infixr 8 ^:, **:, ^^:
  
  (^:) :: (Num a, Integral b) => ElemOp a b a
  (^:) = zipWithA (^)
  
  (**:) :: (Floating a) => ElemOp1 a
  (**:) = zipWithA (**)
  
  (^^:) :: (Fractional a, Integral b) => ElemOp a b a
  (^^:) = zipWithA (^^)
  
  -- Convenient aliases for matrix-scalar element-wise operations.
  type ScalarOp a b c = (IArray arr a, IArray arr c, Ix i) =>
                        arr i a -> b -> arr i c
  type ScalarOp1 a = ScalarOp a a a
  
  samap :: (IArray arr a, IArray arr c, Ix i) =>
           (a -> b -> c) -> arr i a -> b -> arr i c
  samap f a s = amap (`f` s) a
  
  infixl 6 +., -.
  infixl 7 *., /., `divS`
  
  (+.), (-.), (*.) :: (Num a) => ScalarOp1 a
  (+.) = samap (+)
  (-.) = samap (-)
  (*.) = samap (*)
  
  divS :: (Integral a) => ScalarOp1 a
  divS = samap div
  
  (/.) :: (Fractional a) => ScalarOp1 a
  (/.) = samap (/)
  
  infixr 8 ^., **., ^^.
  
  (^.) :: (Num a, Integral b) => ScalarOp a b a
  (^.) = samap (^)
  
  (**.) :: (Floating a) => ScalarOp1 a
  (**.) = samap (**)
  
  (^^.) :: (Fractional a, Integral b) => ScalarOp a b a
  (^^.) = samap (^^)
  
  main :: IO ()
  main = do
    let m1, m2 :: (forall a. (Enum a, Num a) => Array (Int, Int) a)
        m1 = listArray ((0, 0), (2, 3)) [1..]
        m2 = listArray ((0, 0), (2, 3)) [10..]
        s :: (forall a. Num a => a)
        s = 99
    putStrLn "m1"
    print m1
    putStrLn "m2"
    print m2
    putStrLn "s"
    print s
    putStrLn "m1 + m2"
    print $ m1 +: m2
    putStrLn "m1 - m2"
    print $ m1 -: m2
    putStrLn "m1 * m2"
    print $ m1 *: m2
    putStrLn "m1 `div` m2"
    print $ m1 `divE` m2
    putStrLn "m1 / m2"
    print $ m1 /: m2
    putStrLn "m1 ^ m2"
    print $ m1 ^: m2
    putStrLn "m1 ** m2"
    print $ m1 **: m2
    putStrLn "m1 ^^ m2"
    print $ m1 ^^: m2
    putStrLn "m1 + s"
    print $ m1 +. s
    putStrLn "m1 - s"
    print $ m1 -. s
    putStrLn "m1 * s"
    print $ m1 *. s
    putStrLn "m1 `div` s"
    print $ m1 `divS` s
    putStrLn "m1 / s"
    print $ m1 /. s
    putStrLn "m1 ^ s"
    print $ m1 ^. s
    putStrLn "m1 ** s"
    print $ m1 **. s
    putStrLn "m1 ^^ s"
    print $ m1 ^^. s
  