{-# LANGUAGE TupleSections #-}
module Opaleye.Internal.Aggregate where

import           Control.Applicative (Applicative, pure, (<*>))

import qualified Data.Profunctor as P
import qualified Data.Profunctor.Product as PP

import qualified Opaleye.Internal.PackMap as PM
import qualified Opaleye.Internal.PrimQuery as PQ
import qualified Opaleye.Internal.Tag as T
import qualified Opaleye.Internal.Column as C
import qualified Opaleye.Internal.Order as O

import qualified Opaleye.Internal.HaskellDB.PrimQuery as HPQ

{-|
An 'Aggregator' takes a collection of rows of type @a@, groups
them, and transforms each group into a single row of type @b@. This
corresponds to aggregators using @GROUP BY@ in SQL.

You should combine basic 'Aggregator's into 'Aggregator's on compound
types by using the operations in "Data.Profunctor.Product".

An 'Aggregator' corresponds closely to a 'Control.Foldl.Fold' from the
@foldl@ package.  Whereas an 'Aggregator' @a@ @b@ takes each group of
type @a@ to a single row of type @b@, a 'Control.Foldl.Fold' @a@ @b@
takes a list of @a@ and returns a single value of type @b@.
-}
newtype Aggregator a b =
  Aggregator (PM.PackMap (Maybe (HPQ.AggrOp, [HPQ.OrderExpr], HPQ.AggrDistinct),
                          HPQ.PrimExpr)
                         HPQ.PrimExpr
                         a b)

makeAggr' :: Maybe HPQ.AggrOp -> Aggregator (C.Column a) (C.Column b)
makeAggr' :: Maybe AggrOp -> Aggregator (Column a) (Column b)
makeAggr' Maybe AggrOp
mAggrOp = PackMap
  (Maybe (AggrOp, [OrderExpr], AggrDistinct), PrimExpr)
  PrimExpr
  (Column a)
  (Column b)
-> Aggregator (Column a) (Column b)
forall a b.
PackMap
  (Maybe (AggrOp, [OrderExpr], AggrDistinct), PrimExpr) PrimExpr a b
-> Aggregator a b
Aggregator ((forall (f :: * -> *).
 Applicative f =>
 ((Maybe (AggrOp, [OrderExpr], AggrDistinct), PrimExpr)
  -> f PrimExpr)
 -> Column a -> f (Column b))
-> PackMap
     (Maybe (AggrOp, [OrderExpr], AggrDistinct), PrimExpr)
     PrimExpr
     (Column a)
     (Column b)
forall a b s t.
(forall (f :: * -> *). Applicative f => (a -> f b) -> s -> f t)
-> PackMap a b s t
PM.PackMap
  (\(Maybe (AggrOp, [OrderExpr], AggrDistinct), PrimExpr) -> f PrimExpr
f (C.Column e) -> (PrimExpr -> Column b) -> f PrimExpr -> f (Column b)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap PrimExpr -> Column b
forall pgType. PrimExpr -> Column pgType
C.Column ((Maybe (AggrOp, [OrderExpr], AggrDistinct), PrimExpr) -> f PrimExpr
f ((AggrOp -> (AggrOp, [OrderExpr], AggrDistinct))
-> Maybe AggrOp -> Maybe (AggrOp, [OrderExpr], AggrDistinct)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (, [], AggrDistinct
HPQ.AggrAll) Maybe AggrOp
mAggrOp, PrimExpr
e))))

makeAggr :: HPQ.AggrOp -> Aggregator (C.Column a) (C.Column b)
makeAggr :: AggrOp -> Aggregator (Column a) (Column b)
makeAggr = Maybe AggrOp -> Aggregator (Column a) (Column b)
forall a b. Maybe AggrOp -> Aggregator (Column a) (Column b)
makeAggr' (Maybe AggrOp -> Aggregator (Column a) (Column b))
-> (AggrOp -> Maybe AggrOp)
-> AggrOp
-> Aggregator (Column a) (Column b)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. AggrOp -> Maybe AggrOp
forall a. a -> Maybe a
Just

-- | Order the values within each aggregation in `Aggregator` using
-- the given ordering. This is only relevant for aggregations that
-- depend on the order they get their elements, like
-- `Opaleye.Aggregate.arrayAgg` and `Opaleye.Aggregate.stringAgg`.
--
-- You can either apply it to an aggregation of multiple columns, in
-- which case it will apply to all aggregation functions in there, or you
-- can apply it to a single column, and then compose the aggregations
-- afterwards. Examples:
--
-- > x :: Aggregator (Column a, Column b) (Column (PGArray a), Column (PGArray a))
-- > x = (,) <$> orderAggregate (asc snd) (lmap fst arrayAggGrouped)
-- >         <*> orderAggregate (desc snd) (lmap fst arrayAggGrouped)
--
-- This will generate:
--
-- @
-- SELECT array_agg(a ORDER BY b ASC), array_agg(a ORDER BY b DESC)
-- FROM (SELECT a, b FROM ...)
-- @
--
-- Or:
--
-- > x :: Aggregator (Column a, Column b) (Column (PGArray a), Column (PGArray b))
-- > x = orderAggregate (asc snd) $ p2 (arrayAggGrouped, arrayAggGrouped)
--
-- This will generate:
--
-- @
-- SELECT array_agg(a ORDER BY b ASC), array_agg(b ORDER BY b ASC)
-- FROM (SELECT a, b FROM ...)
-- @

orderAggregate :: O.Order a -> Aggregator a b -> Aggregator a b
orderAggregate :: Order a -> Aggregator a b -> Aggregator a b
orderAggregate Order a
o (Aggregator (PM.PackMap forall (f :: * -> *).
Applicative f =>
((Maybe (AggrOp, [OrderExpr], AggrDistinct), PrimExpr)
 -> f PrimExpr)
-> a -> f b
pm)) = PackMap
  (Maybe (AggrOp, [OrderExpr], AggrDistinct), PrimExpr) PrimExpr a b
-> Aggregator a b
forall a b.
PackMap
  (Maybe (AggrOp, [OrderExpr], AggrDistinct), PrimExpr) PrimExpr a b
-> Aggregator a b
Aggregator ((forall (f :: * -> *).
 Applicative f =>
 ((Maybe (AggrOp, [OrderExpr], AggrDistinct), PrimExpr)
  -> f PrimExpr)
 -> a -> f b)
-> PackMap
     (Maybe (AggrOp, [OrderExpr], AggrDistinct), PrimExpr) PrimExpr a b
forall a b s t.
(forall (f :: * -> *). Applicative f => (a -> f b) -> s -> f t)
-> PackMap a b s t
PM.PackMap
  (\(Maybe (AggrOp, [OrderExpr], AggrDistinct), PrimExpr) -> f PrimExpr
f a
c -> ((Maybe (AggrOp, [OrderExpr], AggrDistinct), PrimExpr)
 -> f PrimExpr)
-> a -> f b
forall (f :: * -> *).
Applicative f =>
((Maybe (AggrOp, [OrderExpr], AggrDistinct), PrimExpr)
 -> f PrimExpr)
-> a -> f b
pm ((Maybe (AggrOp, [OrderExpr], AggrDistinct), PrimExpr) -> f PrimExpr
f ((Maybe (AggrOp, [OrderExpr], AggrDistinct), PrimExpr)
 -> f PrimExpr)
-> ((Maybe (AggrOp, [OrderExpr], AggrDistinct), PrimExpr)
    -> (Maybe (AggrOp, [OrderExpr], AggrDistinct), PrimExpr))
-> (Maybe (AggrOp, [OrderExpr], AggrDistinct), PrimExpr)
-> f PrimExpr
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Maybe (AggrOp, [OrderExpr], AggrDistinct)
 -> Maybe (AggrOp, [OrderExpr], AggrDistinct))
-> (Maybe (AggrOp, [OrderExpr], AggrDistinct), PrimExpr)
-> (Maybe (AggrOp, [OrderExpr], AggrDistinct), PrimExpr)
forall (p :: * -> * -> *) a b c.
Strong p =>
p a b -> p (a, c) (b, c)
P.first' (((AggrOp, [OrderExpr], AggrDistinct)
 -> (AggrOp, [OrderExpr], AggrDistinct))
-> Maybe (AggrOp, [OrderExpr], AggrDistinct)
-> Maybe (AggrOp, [OrderExpr], AggrDistinct)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((\[OrderExpr] -> [OrderExpr]
f' (AggrOp
a,[OrderExpr]
b,AggrDistinct
c') -> (AggrOp
a,[OrderExpr] -> [OrderExpr]
f' [OrderExpr]
b,AggrDistinct
c')) ([OrderExpr] -> [OrderExpr] -> [OrderExpr]
forall a b. a -> b -> a
const ([OrderExpr] -> [OrderExpr] -> [OrderExpr])
-> [OrderExpr] -> [OrderExpr] -> [OrderExpr]
forall a b. (a -> b) -> a -> b
$ a -> Order a -> [OrderExpr]
forall a. a -> Order a -> [OrderExpr]
O.orderExprs a
c Order a
o)))) a
c))

runAggregator
  :: Applicative f
  => Aggregator a b
  -> ((Maybe (HPQ.AggrOp, [HPQ.OrderExpr], HPQ.AggrDistinct), HPQ.PrimExpr)
     -> f HPQ.PrimExpr)
  -> a -> f b
runAggregator :: Aggregator a b
-> ((Maybe (AggrOp, [OrderExpr], AggrDistinct), PrimExpr)
    -> f PrimExpr)
-> a
-> f b
runAggregator (Aggregator PackMap
  (Maybe (AggrOp, [OrderExpr], AggrDistinct), PrimExpr) PrimExpr a b
a) = PackMap
  (Maybe (AggrOp, [OrderExpr], AggrDistinct), PrimExpr) PrimExpr a b
-> ((Maybe (AggrOp, [OrderExpr], AggrDistinct), PrimExpr)
    -> f PrimExpr)
-> a
-> f b
forall (f :: * -> *) a b s t.
Applicative f =>
PackMap a b s t -> (a -> f b) -> s -> f t
PM.traversePM PackMap
  (Maybe (AggrOp, [OrderExpr], AggrDistinct), PrimExpr) PrimExpr a b
a

-- In Postgres (and, I believe, standard SQL) "aggregate functions are
-- not allowed in FROM clause of their own query level".  There
-- doesn't seem to be any fundamental reason for this, but we are
-- stuck with it.  That means that in a lateral subquery containing an
-- aggregation over a field C from a previous subquery we have to
-- create a new field name for C before we are allowed to aggregate it!
-- For more information see
--
--     https://www.postgresql.org/message-id/20200513110251.GC24083%40cloudinit-builder
--
--     https://github.com/tomjaguarpaw/haskell-opaleye/pull/460#issuecomment-626716160
--
-- Instead of detecting when we are aggregating over a field from a
-- previous query we just create new names for all field before we
-- aggregate.  On the other hand, referring to a field from a previous
-- query in an ORDER BY expression is totally fine!
aggregateU :: Aggregator a b
           -> (a, PQ.PrimQuery, T.Tag) -> (b, PQ.PrimQuery, T.Tag)
aggregateU :: Aggregator a b -> (a, PrimQuery, Tag) -> (b, PrimQuery, Tag)
aggregateU Aggregator a b
agg (a
c0, PrimQuery
primQ, Tag
t0) = (b
c1, PrimQuery
primQ', Tag -> Tag
T.next Tag
t0)
  where (b
c1, [((Symbol, (Maybe (AggrOp, [OrderExpr], AggrDistinct), Symbol)),
  (Symbol, PrimExpr))]
projPEs_inners) =
          PM
  [((Symbol, (Maybe (AggrOp, [OrderExpr], AggrDistinct), Symbol)),
    (Symbol, PrimExpr))]
  b
-> (b,
    [((Symbol, (Maybe (AggrOp, [OrderExpr], AggrDistinct), Symbol)),
      (Symbol, PrimExpr))])
forall a r. PM [a] r -> (r, [a])
PM.run (Aggregator a b
-> ((Maybe (AggrOp, [OrderExpr], AggrDistinct), PrimExpr)
    -> StateT
         ([((Symbol, (Maybe (AggrOp, [OrderExpr], AggrDistinct), Symbol)),
            (Symbol, PrimExpr))],
          Int)
         Identity
         PrimExpr)
-> a
-> PM
     [((Symbol, (Maybe (AggrOp, [OrderExpr], AggrDistinct), Symbol)),
       (Symbol, PrimExpr))]
     b
forall (f :: * -> *) a b.
Applicative f =>
Aggregator a b
-> ((Maybe (AggrOp, [OrderExpr], AggrDistinct), PrimExpr)
    -> f PrimExpr)
-> a
-> f b
runAggregator Aggregator a b
agg (Tag
-> (Maybe (AggrOp, [OrderExpr], AggrDistinct), PrimExpr)
-> StateT
     ([((Symbol, (Maybe (AggrOp, [OrderExpr], AggrDistinct), Symbol)),
        (Symbol, PrimExpr))],
      Int)
     Identity
     PrimExpr
forall m.
Tag
-> (m, PrimExpr)
-> PM [((Symbol, (m, Symbol)), (Symbol, PrimExpr))] PrimExpr
extractAggregateFields Tag
t0) a
c0)

        projPEs :: [(Symbol, (Maybe (AggrOp, [OrderExpr], AggrDistinct), Symbol))]
projPEs = (((Symbol, (Maybe (AggrOp, [OrderExpr], AggrDistinct), Symbol)),
  (Symbol, PrimExpr))
 -> (Symbol, (Maybe (AggrOp, [OrderExpr], AggrDistinct), Symbol)))
-> [((Symbol, (Maybe (AggrOp, [OrderExpr], AggrDistinct), Symbol)),
     (Symbol, PrimExpr))]
-> [(Symbol, (Maybe (AggrOp, [OrderExpr], AggrDistinct), Symbol))]
forall a b. (a -> b) -> [a] -> [b]
map ((Symbol, (Maybe (AggrOp, [OrderExpr], AggrDistinct), Symbol)),
 (Symbol, PrimExpr))
-> (Symbol, (Maybe (AggrOp, [OrderExpr], AggrDistinct), Symbol))
forall a b. (a, b) -> a
fst [((Symbol, (Maybe (AggrOp, [OrderExpr], AggrDistinct), Symbol)),
  (Symbol, PrimExpr))]
projPEs_inners
        inners :: [(Symbol, PrimExpr)]
inners  = (((Symbol, (Maybe (AggrOp, [OrderExpr], AggrDistinct), Symbol)),
  (Symbol, PrimExpr))
 -> (Symbol, PrimExpr))
-> [((Symbol, (Maybe (AggrOp, [OrderExpr], AggrDistinct), Symbol)),
     (Symbol, PrimExpr))]
-> [(Symbol, PrimExpr)]
forall a b. (a -> b) -> [a] -> [b]
map ((Symbol, (Maybe (AggrOp, [OrderExpr], AggrDistinct), Symbol)),
 (Symbol, PrimExpr))
-> (Symbol, PrimExpr)
forall a b. (a, b) -> b
snd [((Symbol, (Maybe (AggrOp, [OrderExpr], AggrDistinct), Symbol)),
  (Symbol, PrimExpr))]
projPEs_inners

        primQ' :: PrimQuery
primQ' = [(Symbol, (Maybe (AggrOp, [OrderExpr], AggrDistinct), Symbol))]
-> PrimQuery -> PrimQuery
forall a.
[(Symbol, (Maybe (AggrOp, [OrderExpr], AggrDistinct), Symbol))]
-> PrimQuery' a -> PrimQuery' a
PQ.Aggregate [(Symbol, (Maybe (AggrOp, [OrderExpr], AggrDistinct), Symbol))]
projPEs (Bool -> [(Symbol, PrimExpr)] -> PrimQuery -> PrimQuery
forall a.
Bool -> [(Symbol, PrimExpr)] -> PrimQuery' a -> PrimQuery' a
PQ.Rebind Bool
True [(Symbol, PrimExpr)]
inners PrimQuery
primQ)

extractAggregateFields
  :: T.Tag
  -> (m, HPQ.PrimExpr)
  -> PM.PM [((HPQ.Symbol,
              (m, HPQ.Symbol)),
              (HPQ.Symbol, HPQ.PrimExpr))]
           HPQ.PrimExpr
extractAggregateFields :: Tag
-> (m, PrimExpr)
-> PM [((Symbol, (m, Symbol)), (Symbol, PrimExpr))] PrimExpr
extractAggregateFields Tag
tag (m
m, PrimExpr
pe) = do
  String
i <- PM [((Symbol, (m, Symbol)), (Symbol, PrimExpr))] String
forall a. PM a String
PM.new

  let souter :: Symbol
souter = String -> Tag -> Symbol
HPQ.Symbol (String
"result" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
i) Tag
tag
      sinner :: Symbol
sinner = String -> Tag -> Symbol
HPQ.Symbol (String
"inner" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
i) Tag
tag

  ((Symbol, (m, Symbol)), (Symbol, PrimExpr))
-> PM [((Symbol, (m, Symbol)), (Symbol, PrimExpr))] ()
forall a. a -> PM [a] ()
PM.write ((Symbol
souter, (m
m, Symbol
sinner)), (Symbol
sinner, PrimExpr
pe))

  PrimExpr
-> PM [((Symbol, (m, Symbol)), (Symbol, PrimExpr))] PrimExpr
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Symbol -> PrimExpr
HPQ.AttrExpr Symbol
souter)

-- { Boilerplate instances

instance Functor (Aggregator a) where
  fmap :: (a -> b) -> Aggregator a a -> Aggregator a b
fmap a -> b
f (Aggregator PackMap
  (Maybe (AggrOp, [OrderExpr], AggrDistinct), PrimExpr) PrimExpr a a
g) = PackMap
  (Maybe (AggrOp, [OrderExpr], AggrDistinct), PrimExpr) PrimExpr a b
-> Aggregator a b
forall a b.
PackMap
  (Maybe (AggrOp, [OrderExpr], AggrDistinct), PrimExpr) PrimExpr a b
-> Aggregator a b
Aggregator ((a -> b)
-> PackMap
     (Maybe (AggrOp, [OrderExpr], AggrDistinct), PrimExpr) PrimExpr a a
-> PackMap
     (Maybe (AggrOp, [OrderExpr], AggrDistinct), PrimExpr) PrimExpr a b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap a -> b
f PackMap
  (Maybe (AggrOp, [OrderExpr], AggrDistinct), PrimExpr) PrimExpr a a
g)

instance Applicative (Aggregator a) where
  pure :: a -> Aggregator a a
pure = PackMap
  (Maybe (AggrOp, [OrderExpr], AggrDistinct), PrimExpr) PrimExpr a a
-> Aggregator a a
forall a b.
PackMap
  (Maybe (AggrOp, [OrderExpr], AggrDistinct), PrimExpr) PrimExpr a b
-> Aggregator a b
Aggregator (PackMap
   (Maybe (AggrOp, [OrderExpr], AggrDistinct), PrimExpr) PrimExpr a a
 -> Aggregator a a)
-> (a
    -> PackMap
         (Maybe (AggrOp, [OrderExpr], AggrDistinct), PrimExpr) PrimExpr a a)
-> a
-> Aggregator a a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a
-> PackMap
     (Maybe (AggrOp, [OrderExpr], AggrDistinct), PrimExpr) PrimExpr a a
forall (f :: * -> *) a. Applicative f => a -> f a
pure
  Aggregator PackMap
  (Maybe (AggrOp, [OrderExpr], AggrDistinct), PrimExpr)
  PrimExpr
  a
  (a -> b)
f <*> :: Aggregator a (a -> b) -> Aggregator a a -> Aggregator a b
<*> Aggregator PackMap
  (Maybe (AggrOp, [OrderExpr], AggrDistinct), PrimExpr) PrimExpr a a
x = PackMap
  (Maybe (AggrOp, [OrderExpr], AggrDistinct), PrimExpr) PrimExpr a b
-> Aggregator a b
forall a b.
PackMap
  (Maybe (AggrOp, [OrderExpr], AggrDistinct), PrimExpr) PrimExpr a b
-> Aggregator a b
Aggregator (PackMap
  (Maybe (AggrOp, [OrderExpr], AggrDistinct), PrimExpr)
  PrimExpr
  a
  (a -> b)
f PackMap
  (Maybe (AggrOp, [OrderExpr], AggrDistinct), PrimExpr)
  PrimExpr
  a
  (a -> b)
-> PackMap
     (Maybe (AggrOp, [OrderExpr], AggrDistinct), PrimExpr) PrimExpr a a
-> PackMap
     (Maybe (AggrOp, [OrderExpr], AggrDistinct), PrimExpr) PrimExpr a b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> PackMap
  (Maybe (AggrOp, [OrderExpr], AggrDistinct), PrimExpr) PrimExpr a a
x)

instance P.Profunctor Aggregator where
  dimap :: (a -> b) -> (c -> d) -> Aggregator b c -> Aggregator a d
dimap a -> b
f c -> d
g (Aggregator PackMap
  (Maybe (AggrOp, [OrderExpr], AggrDistinct), PrimExpr) PrimExpr b c
q) = PackMap
  (Maybe (AggrOp, [OrderExpr], AggrDistinct), PrimExpr) PrimExpr a d
-> Aggregator a d
forall a b.
PackMap
  (Maybe (AggrOp, [OrderExpr], AggrDistinct), PrimExpr) PrimExpr a b
-> Aggregator a b
Aggregator ((a -> b)
-> (c -> d)
-> PackMap
     (Maybe (AggrOp, [OrderExpr], AggrDistinct), PrimExpr) PrimExpr b c
-> PackMap
     (Maybe (AggrOp, [OrderExpr], AggrDistinct), PrimExpr) PrimExpr a d
forall (p :: * -> * -> *) a b c d.
Profunctor p =>
(a -> b) -> (c -> d) -> p b c -> p a d
P.dimap a -> b
f c -> d
g PackMap
  (Maybe (AggrOp, [OrderExpr], AggrDistinct), PrimExpr) PrimExpr b c
q)

instance PP.ProductProfunctor Aggregator where
  purePP :: b -> Aggregator a b
purePP = b -> Aggregator a b
forall (f :: * -> *) a. Applicative f => a -> f a
pure
  **** :: Aggregator a (b -> c) -> Aggregator a b -> Aggregator a c
(****) = Aggregator a (b -> c) -> Aggregator a b -> Aggregator a c
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
(<*>)

instance PP.SumProfunctor Aggregator where
  Aggregator PackMap
  (Maybe (AggrOp, [OrderExpr], AggrDistinct), PrimExpr) PrimExpr a b
x1 +++! :: Aggregator a b
-> Aggregator a' b' -> Aggregator (Either a a') (Either b b')
+++! Aggregator PackMap
  (Maybe (AggrOp, [OrderExpr], AggrDistinct), PrimExpr)
  PrimExpr
  a'
  b'
x2 = PackMap
  (Maybe (AggrOp, [OrderExpr], AggrDistinct), PrimExpr)
  PrimExpr
  (Either a a')
  (Either b b')
-> Aggregator (Either a a') (Either b b')
forall a b.
PackMap
  (Maybe (AggrOp, [OrderExpr], AggrDistinct), PrimExpr) PrimExpr a b
-> Aggregator a b
Aggregator (PackMap
  (Maybe (AggrOp, [OrderExpr], AggrDistinct), PrimExpr) PrimExpr a b
x1 PackMap
  (Maybe (AggrOp, [OrderExpr], AggrDistinct), PrimExpr) PrimExpr a b
-> PackMap
     (Maybe (AggrOp, [OrderExpr], AggrDistinct), PrimExpr)
     PrimExpr
     a'
     b'
-> PackMap
     (Maybe (AggrOp, [OrderExpr], AggrDistinct), PrimExpr)
     PrimExpr
     (Either a a')
     (Either b b')
forall (p :: * -> * -> *) a b a' b'.
SumProfunctor p =>
p a b -> p a' b' -> p (Either a a') (Either b b')
PP.+++! PackMap
  (Maybe (AggrOp, [OrderExpr], AggrDistinct), PrimExpr)
  PrimExpr
  a'
  b'
x2)

-- }