content: -- | Surface language
  module Circuit.Lang
    ( c
    , add
    , sub
    , mul
    , and_
    , or_
    , xor_
    , not_
    , eq
    , deref
    , e
    , cond
    , ret
    , input
    ) where
  
  import Protolude
  
  import Circuit.Affine     (AffineCircuit(..))
  import Circuit.Arithmetic (Gate(..), Wire(..))
  import Circuit.Expr
  
  -- | Convert constant to expression
  c :: f -> Expr Wire f f
  c = EConst
  
  -- | Binary arithmetic operations on expressions
  add, sub, mul :: Expr Wire f f -> Expr Wire f f -> Expr Wire f f
  add = EBinOp BAdd
  sub = EBinOp BSub
  mul = EBinOp BMul
  
  -- | Binary logic operations on expressions
  -- Have to use underscore or similar to avoid shadowing @and@ and @or@
  -- from Prelude/Protolude.
  and_, or_, xor_ :: Expr Wire f Bool -> Expr Wire f Bool -> Expr Wire f Bool
  and_ = EBinOp BAnd
  or_ = EBinOp BOr
  xor_ = EBinOp BXor
  
  -- | Negate expression
  not_ :: Expr Wire f Bool -> Expr Wire f Bool
  not_ = EUnOp UNot
  
  -- | Compare two expressions
  eq :: Expr Wire f f -> Expr Wire f f -> Expr Wire f Bool
  eq = EEq
  
  -- | Convert wire to expression
  deref :: Wire -> Expr Wire f f
  deref = EVar
  
  -- | Return compilation of expression into an intermediate wire
  e :: Num f => Expr Wire f f -> ExprM f Wire
  e = compileWithWire imm
  
  -- | Conditional statement on expressions
  cond :: Expr Wire f Bool -> Expr Wire f ty -> Expr Wire f ty -> Expr Wire f ty
  cond = EIf
  
  -- | Return compilation of expression into an output wire
  ret :: Num f => Expr Wire f f -> ExprM f Wire
  ret = compileWithWire freshOutput
  
  compileWithWire :: Num f => ExprM f Wire -> Expr Wire f f -> ExprM f Wire
  compileWithWire freshWire expr = do
    compileOut <- compile expr
    case compileOut of
      Left wire -> pure wire
      Right circ -> do
             wire <- freshWire
             emit $ Mul (ConstGate 1) circ wire
             pure wire
  
  input :: ExprM f Wire
  input = freshInput
  