{-# LANGUAGE Unsafe #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TypeSynonymInstances #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FlexibleContexts #-}

{-# LANGUAGE CPP #-}
#if __GLASGOW_HASKELL__ < 710
  {-# LANGUAGE OverlappingInstances #-}
#endif

-- | This module provides functionality for labelling endpoints in
-- wires. The goal is to achieve two things:
-- 
-- * Label qubits individually. For example, we would like to label
-- three qubits (/a/, /b/, /c/) as \"a\", \"b\", and \"c\",
-- respectively.
-- 
-- * Label data structures all at once. For example, if
-- /a/=[/x/,/y/,/z/] is a piece of quantum data, and we label this
-- data structure \"a\", then the individual qubits will be labelled:
-- /x/ = /a/[0], /y/ = /a/[1], /z/ = /a/[2]. 
-- 
-- We can also combine both methods to arbitrary nesting levels. For
-- example, we can label (([x,y,z], t), [u,v,w]) as (\"a\", [\"u\",
-- \"v\", \"w\"]), to get the labelling /x/ = /a/[0,0], /y/ =
-- /a/[0,1], /z/ = /a/[0,2], /t/ = /a/[1], /u/ = /u/, /v/ = /v/, /w/ =
-- /w/.

module Quipper.Internal.Labels where

import Quipper.Internal.Circuit
import Quipper.Internal.Monad
import Quipper.Utils.Auxiliary
import Quipper.Utils.Tuple
import Quipper.Internal.Transformer

import qualified Data.Map as Map
import Data.Map (Map)

import Control.Applicative (Applicative(..))
import Control.Monad (liftM, ap)

-- ----------------------------------------------------------------------
-- * Helper functions

-- ** Indices

-- | An index list is something that can be appended to a string. We
-- consider subscript indices of the form \"[i]\", dotted indices of
-- the form \".i\", and perhaps arbitrary suffixes.  A complication is
-- that we want consecutive subscript indices to be combined, as in
-- \"[i,j,k]\". We therefore need a special data structure to hold an
-- index list \"under construction\".
-- 
-- An index list consists of a string and a list of current
-- subscripts. For efficiency, the list of subscripts is reversed,
-- i.e., the most recent subscript is at the head of the list.
type IndexList = (String, [String])

-- | Convert an index list to a string.
indexlist_format :: IndexList -> String
indexlist_format (s,idx) =
  s ++ string_of_list "[" "," "]" "" id (reverse idx)

-- | The empty index list.
indexlist_empty :: IndexList
indexlist_empty = ("", [])

-- | Append a subscript to an index list.
indexlist_subscript :: IndexList -> String -> IndexList
indexlist_subscript (s, idx) i = (s, i:idx)

-- | Append a dotted index to an index list.
indexlist_dotted :: IndexList -> String -> IndexList
indexlist_dotted idxl i = (indexlist_format idxl ++ "." ++ i, [])

-- ** The LabelMonad monad

-- | A monad to provide a convenient syntax for specifying 'Labelable'
-- instances. Computations in this monad have access to a read-only
-- \"current index list\", and they output a binding from wires to
-- strings.
newtype LabelMonad a = LabelMonad {
  getLabelMonad :: IndexList -> (Map Wire String, a)
  }

instance Monad LabelMonad where
  return a = LabelMonad (\idxl -> (Map.empty, a))
  f >>= g = LabelMonad h where
    h idxl = (Map.union m1 m2, z) where
      (m1, y) = getLabelMonad f idxl
      (m2, z) = getLabelMonad (g y) idxl

instance Applicative LabelMonad where
  pure = return
  (<*>) = ap

instance Functor LabelMonad where
  fmap = liftM

-- | Get the current string and index.
labelmonad_get_indexlist :: LabelMonad IndexList
labelmonad_get_indexlist = LabelMonad h where
  h idxl = (Map.empty, idxl)

-- | Output a binding for a label
labelmonad_put_binding :: Wire -> String -> LabelMonad ()
labelmonad_put_binding x label = LabelMonad h where
  h idxl = (Map.singleton x label, ())

-- | Run a subcomputation with a new current index list.
labelmonad_with_indexlist :: IndexList -> LabelMonad a -> LabelMonad a
labelmonad_with_indexlist idxl body = LabelMonad h where
  h idxl' = getLabelMonad body idxl

-- | Extract a labelling from a label monad computation. This is the
-- run function of the label monad.
labelmonad_run :: LabelMonad () -> Map Wire String
labelmonad_run body = bindings where
  (bindings, _) = getLabelMonad body indexlist_empty

-- ----------------------------------------------------------------------
-- ** Formatting of labels

-- | Label a wire with the given name, using the current index.
label_wire :: Wire -> String -> LabelMonad ()
label_wire x s = do
  idxl <- labelmonad_get_indexlist
  let label = s ++ indexlist_format idxl
  labelmonad_put_binding x label

-- | Run a subcomputation with a subscript index appended to the
-- current index list. Sample usage:
-- 
-- > with_index "0" $ do
-- >   <<<labelings>>>
with_index :: String -> LabelMonad () -> LabelMonad ()
with_index i body = do
  idxl <- labelmonad_get_indexlist
  labelmonad_with_indexlist (indexlist_subscript idxl i) body

-- | Run a subcomputation with a dotted index appended to the current
-- index list. Sample usage:                                                                  
-- 
-- > with_dotted_index "left" $ do
-- >   <<<labelings>>>

with_dotted_index :: String -> LabelMonad () -> LabelMonad ()
with_dotted_index i body = do
  idxl <- labelmonad_get_indexlist
  labelmonad_with_indexlist (indexlist_dotted idxl i) body

-- | Like 'with_index', except the order of the arguments is
-- reversed. This is intended to be used as an infix operator:
-- 
-- > <<<labeling>>> `indexed` "0"
indexed :: LabelMonad () -> String -> LabelMonad ()
indexed body i = with_index i body

-- | Like 'with_dotted_index', except the order of the arguments is
-- reversed. This is intended to be used as an infix operator:
-- 
-- > <<<labeling>>> `dotted_indexed` "left"
dotted_indexed :: LabelMonad () -> String -> LabelMonad ()
dotted_indexed body i = with_dotted_index i body

-- | Do nothing.
label_empty :: LabelMonad ()
label_empty = return ()

-- ----------------------------------------------------------------------
-- * The Labelable type class

-- | 'Labelable' /a/ /s/ means that /a/ is a data structure that can
-- be labelled with the format /s/. A \"format\" is a string, or a
-- data structure with strings at the leaves.

class Labelable a s where
  -- | Recursively label a data structure with the given format. 
  label_rec :: a -> s -> LabelMonad ()

-- | Given a data structure and a format, return a list of labelled
-- wires.
mklabel :: (Labelable a s) => a -> s -> [(Wire, String)]
mklabel a s = Map.toList bindings where
  bindings = labelmonad_run (label_rec a s)

instance Labelable Qubit String where
  label_rec a s = label_wire (wire_of_qubit a) s

instance Labelable Bit String where
  label_rec a s = label_wire (wire_of_bit a) s

instance (Labelable a String) => Labelable (Signed a) String where
  label_rec (Signed a b) s =
    label_rec a s `dotted_indexed` (if b then "+" else "-")

instance (Labelable a String) => Labelable (Signed a) (Signed String) where
  label_rec (Signed a b) (Signed s c)
    | b == c = label_rec a s
    | otherwise = return () -- fail silently

instance Labelable () String where
  label_rec a s = label_empty

instance Labelable () () where
  label_rec a s = label_empty

instance (Labelable a String, Labelable b String) => Labelable (a,b) String where
  label_rec (a,b) s = do
    label_rec a s `indexed` "0"
    label_rec b s `indexed` "1"

instance (Labelable a String, Labelable b String, Labelable c String) => Labelable (a,b,c) String where
  label_rec (a,b,c) s = do
    label_rec a s `indexed` "0"
    label_rec b s `indexed` "1"
    label_rec c s `indexed` "2"

instance (Labelable a String, Labelable b String, Labelable c String, Labelable d String) => Labelable (a,b,c,d) String where
  label_rec (a,b,c,d) s = do
    label_rec a s `indexed` "0"
    label_rec b s `indexed` "1"
    label_rec c s `indexed` "2"
    label_rec d s `indexed` "3"

instance (Labelable a String, Labelable b String, Labelable c String, Labelable d String, Labelable e String) => Labelable (a,b,c,d,e) String where
  label_rec (a,b,c,d,e) s = do
    label_rec a s `indexed` "0"
    label_rec b s `indexed` "1"
    label_rec c s `indexed` "2"
    label_rec d s `indexed` "3"
    label_rec e s `indexed` "4"

instance (Labelable a String, Labelable b String, Labelable c String, Labelable d String, Labelable e String, Labelable f String) => Labelable (a,b,c,d,e,f) String where
  label_rec (a,b,c,d,e,f) s = do
    label_rec a s `indexed` "0"
    label_rec b s `indexed` "1"
    label_rec c s `indexed` "2"
    label_rec d s `indexed` "3"
    label_rec e s `indexed` "4"
    label_rec f s `indexed` "5"

instance (Labelable a String, Labelable b String, Labelable c String, Labelable d String, Labelable e String, Labelable f String, Labelable g String) => Labelable (a,b,c,d,e,f,g) String where
  label_rec (a,b,c,d,e,f,g) s = do
    label_rec a s `indexed` "0"
    label_rec b s `indexed` "1"
    label_rec c s `indexed` "2"
    label_rec d s `indexed` "3"
    label_rec e s `indexed` "4"
    label_rec f s `indexed` "5"
    label_rec g s `indexed` "6"

instance (Labelable a String, Labelable b String, Labelable c String, Labelable d String, Labelable e String, Labelable f String, Labelable g String, Labelable h String) => Labelable (a,b,c,d,e,f,g,h) String where
  label_rec (a,b,c,d,e,f,g,h) s = do
    label_rec a s `indexed` "0"
    label_rec b s `indexed` "1"
    label_rec c s `indexed` "2"
    label_rec d s `indexed` "3"
    label_rec e s `indexed` "4"
    label_rec f s `indexed` "5"
    label_rec g s `indexed` "6"
    label_rec h s `indexed` "7"

instance (Labelable a String, Labelable b String, Labelable c String, Labelable d String, Labelable e String, Labelable f String, Labelable g String, Labelable h String, Labelable i String) => Labelable (a,b,c,d,e,f,g,h,i) String where
  label_rec (a,b,c,d,e,f,g,h,i) s = do
    label_rec a s `indexed` "0"
    label_rec b s `indexed` "1"
    label_rec c s `indexed` "2"
    label_rec d s `indexed` "3"
    label_rec e s `indexed` "4"
    label_rec f s `indexed` "5"
    label_rec g s `indexed` "6"
    label_rec h s `indexed` "7"
    label_rec i s `indexed` "8"

instance (Labelable a String, Labelable b String, Labelable c String, Labelable d String, Labelable e String, Labelable f String, Labelable g String, Labelable h String, Labelable i String, Labelable j String) => Labelable (a,b,c,d,e,f,g,h,i,j) String where
  label_rec (a,b,c,d,e,f,g,h,i,j) s = do
    label_rec a s `indexed` "0"
    label_rec b s `indexed` "1"
    label_rec c s `indexed` "2"
    label_rec d s `indexed` "3"
    label_rec e s `indexed` "4"
    label_rec f s `indexed` "5"
    label_rec g s `indexed` "6"
    label_rec h s `indexed` "7"
    label_rec i s `indexed` "8"
    label_rec j s `indexed` "9"

instance (Labelable a sa, Labelable b sb) => Labelable (a,b) (sa,sb) where
  label_rec (a,b) (sa,sb) = do
    label_rec a sa
    label_rec b sb

instance (Labelable a sa, Labelable b sb, Labelable c sc) => Labelable (a,b,c) (sa, sb, sc) where
  label_rec a s = label_rec (untuple a) (untuple s)

instance (Labelable a sa, Labelable b sb, Labelable c sc, Labelable d sd) => Labelable (a,b,c,d) (sa, sb, sc, sd) where
  label_rec a s = label_rec (untuple a) (untuple s)

instance (Labelable a sa, Labelable b sb, Labelable c sc, Labelable d sd, Labelable e se) => Labelable (a,b,c,d,e) (sa, sb, sc, sd, se) where
  label_rec a s = label_rec (untuple a) (untuple s)

instance (Labelable a sa, Labelable b sb, Labelable c sc, Labelable d sd, Labelable e se, Labelable f sf) => Labelable (a,b,c,d,e,f) (sa, sb, sc, sd, se, sf) where
  label_rec a s = label_rec (untuple a) (untuple s)

instance (Labelable a sa, Labelable b sb, Labelable c sc, Labelable d sd, Labelable e se, Labelable f sf, Labelable g sg) => Labelable (a,b,c,d,e,f,g) (sa, sb, sc, sd, se, sf, sg) where
  label_rec a s = label_rec (untuple a) (untuple s)

instance (Labelable a sa, Labelable b sb, Labelable c sc, Labelable d sd, Labelable e se, Labelable f sf, Labelable g sg, Labelable h sh) => Labelable (a,b,c,d,e,f,g,h) (sa, sb, sc, sd, se, sf, sg, sh) where
  label_rec a s = label_rec (untuple a) (untuple s)

instance (Labelable a sa, Labelable b sb, Labelable c sc, Labelable d sd, Labelable e se, Labelable f sf, Labelable g sg, Labelable h sh, Labelable i si) => Labelable (a,b,c,d,e,f,g,h,i) (sa, sb, sc, sd, se, sf, sg, sh, si) where
  label_rec a s = label_rec (untuple a) (untuple s)

instance (Labelable a sa, Labelable b sb, Labelable c sc, Labelable d sd, Labelable e se, Labelable f sf, Labelable g sg, Labelable h sh, Labelable i si, Labelable j sj) => Labelable (a,b,c,d,e,f,g,h,i,j) (sa, sb, sc, sd, se, sf, sg, sh, si, sj) where
  label_rec a s = label_rec (untuple a) (untuple s)

instance {-# OVERLAPPING #-} (Labelable a String) => Labelable [a] String where
  label_rec as s = do
    sequence_ [ label_rec a s `indexed` show i | (a,i) <- zip as [0..] ]

instance (Labelable a s) => Labelable [a] [s] where
  label_rec as ss = do
    sequence_ [ label_rec a s | (a,s) <- zip as ss ]

instance (Labelable a String, Labelable b String) => Labelable (B_Endpoint a b) String where
  label_rec (Endpoint_Qubit a) s = label_rec a s
  label_rec (Endpoint_Bit b) s = label_rec b s

instance (Labelable a s, Labelable b t) => Labelable (B_Endpoint a b) (B_Endpoint s t) where
  label_rec (Endpoint_Qubit a) (Endpoint_Qubit s) = label_rec a s
  label_rec (Endpoint_Bit b) (Endpoint_Bit t) = label_rec b t
  label_rec _ _ = return ()  -- fail silently

-- ----------------------------------------------------------------------
-- Parameters are labellable  

-- Since parameters (such as Integers) are 'Quipper.QCData', they must
-- also be labellable. However, they have no qubits, so the labels are
-- trivial (there are 0 labels on such a type).

instance Labelable Integer String where
  label_rec a s = label_empty

instance Labelable Int String where
  label_rec a s = label_empty

instance Labelable Double String where
  label_rec a s = label_empty

instance Labelable Float String where
  label_rec a s = label_empty

instance Labelable Char String where
  label_rec a s = label_empty

-- ======================================================================
-- * High-level functions

-- | Insert a comment in the circuit. This is not a gate, and has no
-- effect, except to mark a spot in the circuit. How the comment is
-- displayed depends on the printing backend.
comment :: String -> Circ ()
comment s = comment_with_label s () ()

-- | Label qubits in the circuit. This is not a gate, and has no
-- effect, except to make the circuit more readable. How the labels
-- are displayed depends on the printing backend. This can take
-- several different forms. Examples:
-- 
-- Label /q/ as @q@ and /r/ as @r@:
-- 
-- > label (q,r) ("q", "r")
-- 
-- Label /a/, /b/, and /c/ as @a@, @b@, and @c@, respectively:
-- 
-- > label [a,b,c] ["a", "b", "c"]
-- 
-- Label /q/ as @x[0]@ and /r/ as @x[1]@:
-- 
-- > label (q,r) "x"
-- 
-- Label /a/, /b/, and /c/ as @x[0]@, @x[1]@, @x[2]@:
-- 
-- > label [a,b,c] "x"
label :: (Labelable qa labels) => qa -> labels -> Circ ()
label qa labels = comment_with_label "" qa labels

-- | Combine 'comment' and 'label' in a single command.
comment_with_label :: (Labelable qa labels) => String -> qa -> labels -> Circ ()
comment_with_label comment qa labels =
  comment_label comment False (mklabel qa labels)

-- ======================================================================
-- * Defining new Labelable instances

-- $ A 'Labelable' instance should be defined for each new instance of
-- 'Quipper.QCData'. The general idea is that the structure of the label
-- should exactly follow the structure of the data being labeled,
-- except that at any level the label can be a string (which will then
-- be decorated with appropriate subscripts for each leaf in the
-- data structure).
-- 
-- In practice, there are two cases to consider: adding a new
-- 'Quipper.QCData' constructor, and adding a new atomic 'Quipper.QCData'.
-- 
-- [New 'Quipper.QCData' constructors]
-- 
-- Consider the case of a new 'Quipper.QCData' constructor:
-- 
-- > instance (QCData a) => QCData (Constructor a).
-- 
-- There are two required 'Labelable' instances. The first instance
-- deals with a label that is a string, and takes the following form:
-- 
-- > instance (Labelable a String) => Labelable (Constructor a) String where
-- >   label_rec as s = do
-- >     label_rec <<<a1>>> s `indexed` <<<index1>>>
-- >     ...
-- >     label_rec <<<an>>> s `indexed` <<<indexn>>>
-- 
-- Here, /a/[sub 1].../a/[sub /n/] are the components of the data
-- structure, and /index/[sub 1].../index[sub /n/] are the
-- corresponding subscripts. The function 'indexed' appends a
-- subscript of the form \"[i]\".  There is a similar function
-- 'dotted_indexed', which appends a subscript of the form \".i\".
-- 
-- The second required instance deals with a label that is a data
-- structure of the same shape as the data being labeled. It takes the
-- form:
-- 
-- > instance (Labelable a s) => Labelable (Constructor a) (Constructor s) where
-- >   label_rec as ss idx = do
-- >     label_rec <<<a1>>> <<<s1>>>
-- >     ...
-- >     label_rec <<<an>>> <<<sn>>>
-- 
-- Here, /a/[sub 1].../a/[sub /n/] are the components of the data
-- structure, and /s/[sub 1].../s/[sub /n/] are the corresponding
-- components of the label.
-- 
-- [Example]
-- 
-- As a concrete example, consider a constructor
-- 
-- > data MaybeTwo a = One a | Two a a
-- > instance (QCData a) => QCData (MaybeTwo a)
-- 
-- The following instance declarations would be appropriate:
-- 
-- > instance (Labelable a String) => Labelable (MaybeTwo a) String where
-- >    label_rec (One x) s =
-- >      with_dotted_index "one" $ do
-- >        label_rec x s
-- >    label_rec (Two x y) s =
-- >      with_dotted_index "two" $ do
-- >        label_rec x s `indexed` "0"
-- >        label_rec y s `indexed` "1"
-- >
-- > instance (Labelable a s) => Labelable (MaybeTwo a) (MaybeTwo s) where
-- >    label_rec (One x) (One s) = label_rec x s
-- >    label_rec (Two x y) (Two s t) = do
-- >      label_rec x s
-- >      label_rec y t
-- >    label_rec _ _ = return ()  -- fail silently
-- 
-- With this example, the commands
-- 
-- > mklabel (One x) "s"
-- > mklabel (Two y z) "s"
-- 
-- produce the respective labelings
-- 
-- > x -> s.one
-- > y -> s.two[0]
-- > z -> s.two[1]
-- 
-- [New atomic QCData]
-- 
-- Consider the case of a new atomic 'Quipper.QCData' instance:
-- 
-- > instance QCData (Atomic x).
-- 
-- We usually need a 'Labelable' instance for the cases /x/='Qubit'
-- and /x/='Bit'. This should be done uniformly, by using the 'Quipper.Internal.QData.QCLeaf'
-- type class. The instance takes the following form:
-- 
-- > instance QCLeaf x => Labelable (Atomic x) String where
-- >   label_rec a s = do
-- >     label_rec <<<a1>>> s `indexed` <<<index1>>>
-- >     ...
-- >     label_rec <<<an>>> s `indexed` <<<indexn>>>
-- 
-- Here, /a/[sub 1].../a/[sub /n/] are the components of the data
-- structure, and /index/[sub 1].../index[sub /n/] are the
-- corresponding subscripts. It is up to the designer of the data
-- structure to decide what are \"components\" and how they should be
-- labelled. On or more layers of string or numeric indices can be
-- added as appropriate.
-- 
-- [Example]
-- 
-- Consider the following sample atomic quantum data. A real number
-- consists of an exponent, a sign, and a list of digits.
-- 
-- > data MyReal x = MyReal Int x [x]
-- > instance QCLeaf x => QCData (MyReal x)
-- 
-- The following instance declaration would be appropriate:
-- 
-- > instance QCLeaf x => Labelable (MyReal x) String where
-- >    label_rec (MyReal exp sign digits) s = do
-- >      label_rec sign s `dotted_indexed` "sign"
-- >      with_dotted_index "digit" $ do
-- >        sequence_ [ label_rec d s `indexed` show i | (d,i) <- zip digits [-exp..] ]
-- 
-- With this example, the command
-- 
-- > mklabel (MyReal 2 x [y0, y1, y2, y3]) "s"
-- 
-- produces the labeling
-- 
-- > x  -> "s.sign"
-- > y0 -> "s.digit[-2]"
-- > y1 -> "s.digit[-1]"
-- > y2 -> "s.digit[0]"
-- > y3 -> "s.digit[1]"
-- 
-- Note that we could have also used the default labeling for the
-- members of a list, but in this case, it was convenient to use a
-- custom labeling.