+getIn (DiagramBox ptop pin q pout pbot) = pin
+getIn (DiagramBypassTop p f) = TT p (getIn f)
+getIn (DiagramBypassBot f p) = TT (getIn f) p
+
+-- | A BoxRenderer is just a routine that, given the dimensions of a
+-- boxes-and-wires box element, knows how to spit out a bunch of TikZ
+-- code that draws it
+type BoxRenderer =
+ TrackPositions -> -- resolves the TrackIdentifiers to actual y-coordinates
+ Float -> -- x1
+ Float -> -- y1
+ Float -> -- x2
+ Float -> -- y2
+ String -- TikZ code
+
+
+
+
+
+
+------------------------------------------------------------------------------
+-- Constraints
+
+-- | a constraint (to be dealt with by lp_solve) relates two track identifiers
+data Constraint = C TrackIdentifier Ordering TrackIdentifier {- plus -} Float
+ | EqualSpace TrackIdentifier TrackIdentifier TrackIdentifier TrackIdentifier
+
+-- instance Show Constraint where
+-- show (C t1 LT t2 k s) = "x"++(show t1)++" = x"++(show t2)++" + "++(show k) ++ ";\n"
+-- show (C t1 GT t2 k s) = "x"++(show t1)++" = x"++(show t2)++" + "++(show k) ++ ";\n"
+-- show (C t1 EQ t2 k s) = "x"++(show t1)++" = x"++(show t2)++" + "++(show k) ++ ";\n"
+
+instance Show Constraint where
+ show (C t1 LT t2 k) = "x"++(show t1)++" <= x"++(show t2)++" + "++(show k) ++ ";\n"
+ show (C t1 GT t2 k) = "x"++(show t1)++" >= x"++(show t2)++" + "++(show k) ++ ";\n"
+ show (C t1 EQ t2 k) = "x"++(show t1)++" = x"++(show t2)++" + "++(show k) ++ ";\n"
+ show (EqualSpace t1a t1b t2a t2b) = "x"++(show t1a)++" = x"++(show t1b)++
+ " + x"++(show t2a)++" - x"++(show t2b)++ ";\n"
+
+-- | a monad to accumulate constraints and track the largest TrackIdentifier allocated
+type ConstraintM a = State (TrackIdentifier,[Constraint]) a
+
+-- | pull the constraints out of the monad
+getConstraints :: ConstraintM [Constraint]
+getConstraints = do { (_,c) <- get ; return c }
+
+-- | add a constraint
+constrain :: TrackIdentifier -> Ordering -> TrackIdentifier {- plus -} -> Float -> ConstraintM ()
+constrain t1 ord t2 k = do { (t,c) <- get
+ ; put (t, (C t1 ord t2 k):c)
+ ; return ()
+ }
+
+constrainEqualSpace t1a t1b t2a t2b = do { (t,c) <- get
+ ; put (t, (EqualSpace t1a t1b t2a t2b):c)
+ ; return ()
+ }
+
+-- | simple form for equality constraints
+constrainEq (TT t1a t1b) (TT t2a t2b) = do { constrainEq t1a t2a ; constrainEq t1b t2b ; return () }
+constrainEq (T t1 ) (T t2 ) = constrain t1 EQ t2 0
+constrainEq (TU t1 ) (TU t2 ) = constrain t1 EQ t2 0
+constrainEq (TU t1 ) (T t2 ) = constrain t1 EQ t2 0
+constrainEq (T t1 ) (TU t2 ) = constrain t1 EQ t2 0
+constrainEq t1 t2 = error $ "constrainEq mismatch: " ++ show t1 ++ " and " ++ show t2
+
+-- | allocate a TrackIdentifier
+alloc1 :: ConstraintM Tracks
+alloc1 = do { (t,c) <- get
+ ; put (t+1,c)
+ ; return (T t)
+ }
+
+
+mkdiag :: GArrowPortShape m () a b -> ConstraintM Diagram
+mkdiag (GASPortPassthrough inp outp m) = error "not supported"
+mkdiag (GASPortShapeWrapper inp outp x) = mkdiag' x
+ where
+ mkdiag' :: GArrowSkeleton (GArrowPortShape m ()) a b -> ConstraintM Diagram
+
+ mkdiag' (GAS_comp f g) = do { f' <- mkdiag' f; g' <- mkdiag' g
+ ; constrainEq (getOut f') (getIn g') ; return $ DiagramComp f' g' }
+ mkdiag' (GAS_first f) = do { (top,(TT _ x),bot) <- alloc inp; f' <- mkdiag' f ; constrainBot f' 1 (uppermost x)
+ ; return $ DiagramBypassBot f' x }
+ mkdiag' (GAS_second f) = do { (top,(TT x _),bot) <- alloc inp; f' <- mkdiag' f ; constrainTop (lowermost x) 1 f'
+ ; return $ DiagramBypassTop x f' }
+ mkdiag' (GAS_id ) = do { (top, x ,bot) <- alloc inp ; simpleDiag "id" top x x bot [(x,x)] }
+ mkdiag' GAS_cancell = do { (top,(TT x y),bot) <- alloc inp
+ ; let r tp x1 y1 x2 y2 = drawBox x1 y1 x2 y2 "gray!50" "cancell" ++
+ drawWires tp x1 y x2 y "black" ++
+ drawLine x1 (tp!lowermost x) ((x1+x2)/2) (tp!uppermost y) "black" "dashed"
+ ; return $ DiagramBox top (TT x y) r y bot }
+ mkdiag' GAS_cancelr = do { (top,(TT x y),bot) <- alloc inp
+ ; let r tp x1 y1 x2 y2 = drawBox x1 y1 x2 y2 "gray!50" "cancelr" ++
+ drawWires tp x1 x x2 x "black" ++
+ drawLine x1 (tp!uppermost y) ((x1+x2)/2) (tp!lowermost x) "black" "dashed"
+ ; return $ DiagramBox top (TT x y) r x bot }
+ mkdiag' GAS_uncancell = do { (top,(TT x y),bot) <- alloc outp
+ ; let r tp x1 y1 x2 y2 = drawBox x1 y1 x2 y2 "gray!50" "uncancell" ++
+ drawWires tp x1 y x2 y "black" ++
+ drawLine ((x1+x2)/2) (tp!uppermost y) x2 (tp!lowermost x) "black" "dashed"
+ ; return $ DiagramBox top y r (TT x y) bot }
+ mkdiag' GAS_uncancelr = do { (top,(TT x y),bot) <- alloc outp
+ ; let r tp x1 y1 x2 y2 = drawBox x1 y1 x2 y2 "gray!50" "uncancelr" ++
+ drawWires tp x1 x x2 x "black" ++
+ drawLine ((x1+x2)/2) (tp!lowermost x) x2 (tp!uppermost y) "black" "dashed"
+ ; return $ DiagramBox top x r (TT x y) bot }
+ mkdiag' GAS_drop = do { (top, x ,bot) <- alloc inp ; simpleDiag "drop" top x x bot [] }
+ mkdiag' (GAS_const i) = do { (top, x ,bot) <- alloc inp
+ ; (_, y ,_) <- alloc outp
+ ; constrainEq x y
+ ; simpleDiag ("const " ++ show i) top x y bot [] }
+ mkdiag' GAS_copy = do { (top,(TT y z),bot) <- alloc outp
+ ; (_ , x ,_) <- alloc inp
+ ; constrainEqualSpace (lowermost y) (uppermost x) (lowermost x) (uppermost z)
+ ; let r tp x1 y1 x2 y2 = drawBox x1 y1 x2 y2 "gray!50" "copy" ++
+ drawWires tp x1 x ((x1+x2)/2) x "black" ++
+ drawWires tp ((x1+x2)/2) x x2 y "black" ++
+ drawWires tp ((x1+x2)/2) x x2 z "black"
+ ; return $ DiagramBox top x r (TT y z) bot
+ }
+ mkdiag' GAS_merge = do { (top,(TT x y),bot) <- alloc inp
+ ; simpleDiag "times" top (TT x y) x bot [] }
+ mkdiag' GAS_swap = do { (top,(TT x y),bot) <- alloc inp
+ ; (top,(TT x' y'),bot) <- alloc outp
+ ; constrainEq (T (lowermost x)) (T (lowermost x'))
+ ; constrainEq (T (uppermost y)) (T (uppermost y'))
+ ; simpleDiag' "swap" top (TT x y) (TT x' y') bot [(x,y'),(y,x')] "gray!50" }
+ mkdiag' GAS_assoc =
+ do { (top,(TT (TT x y) z),bot) <- alloc inp
+ ; let r tp x1 y1 x2 y2
+ = drawBox (x1+0.2*xscale) y1 (x2-0.2*xscale) y2 "white" "assoc" ++
+ drawLine x1 y1 x2 y1 "gray!50" "-" ++
+ drawLine x1 y2 x2 y2 "gray!50" "-" ++
+ drawLine x1 y1 x1 ((tp ! uppermost x) - 0.5) "gray!50" "-"++
+ drawLine x1 ((tp ! uppermost x) - 0.5) (x1+0.2) ((tp ! uppermost x) - 0.5) "gray!50" "-"++
+ drawLine (x1+0.2) ((tp ! uppermost x) - 0.5) (x1+0.2) ((tp ! lowermost y) + 0.5) "gray!50" "-"++
+ drawLine (x1+0.2) ((tp ! lowermost y) + 0.5) x1 ((tp ! lowermost y) + 0.5) "gray!50" "-"++
+ drawLine x1 ((tp ! lowermost y) + 0.5) x1 y2 "gray!50" "-"++
+ drawLine x2 y2 x2 ((tp ! lowermost z) + 0.5) "gray!50" "-"++
+ drawLine x2 ((tp ! lowermost z) + 0.5) (x2-0.2) ((tp ! lowermost z) + 0.5) "gray!50" "-"++
+ drawLine (x2-0.2) ((tp ! lowermost z) + 0.5) (x2-0.2) ((tp ! uppermost y) - 0.5) "gray!50" "-"++
+ drawLine (x2-0.2) ((tp ! uppermost y) - 0.5) x2 ((tp ! uppermost y) - 0.5) "gray!50" "-"++
+ drawLine x2 ((tp ! uppermost y) - 0.5) x2 y1 "gray!50" "-"++
+ drawWires tp x1 x x2 x "black" ++
+ drawWires tp x1 y x2 y "black" ++
+ drawWires tp x1 z x2 z "black"
+ ; return $ DiagramBox top (TT (TT x y) z) r (TT x (TT y z)) bot
+ }
+ mkdiag' GAS_unassoc =
+ do { (top,(TT x (TT y z)),bot) <- alloc inp
+ ; let r tp x1 y1 x2 y2
+ = drawBox (x1+0.2*xscale) y1 (x2-0.2*xscale) y2 "white" "unassoc" ++
+ drawLine x1 y1 x2 y1 "gray!50" "-" ++
+ drawLine x1 y2 x2 y2 "gray!50" "-" ++
+ drawLine x2 y1 x2 ((tp ! uppermost x) - 0.5) "gray!50" "-"++
+ drawLine x2 ((tp ! uppermost x) - 0.5) (x2-0.2) ((tp ! uppermost x) - 0.5) "gray!50" "-"++
+ drawLine (x2-0.2) ((tp ! uppermost x) - 0.5) (x2-0.2) ((tp ! lowermost y) + 0.5) "gray!50" "-"++
+ drawLine (x2-0.2) ((tp ! lowermost y) + 0.5) x2 ((tp ! lowermost y) + 0.5) "gray!50" "-"++
+ drawLine x2 ((tp ! lowermost y) + 0.5) x2 y2 "gray!50" "-"++
+ drawLine x1 y2 x1 ((tp ! lowermost z) + 0.5) "gray!50" "-"++
+ drawLine x1 ((tp ! lowermost z) + 0.5) (x1+0.2) ((tp ! lowermost z) + 0.5) "gray!50" "-"++
+ drawLine (x1+0.2) ((tp ! lowermost z) + 0.5) (x1+0.2) ((tp ! uppermost y) - 0.5) "gray!50" "-"++
+ drawLine (x1+0.2) ((tp ! uppermost y) - 0.5) x1 ((tp ! uppermost y) - 0.5) "gray!50" "-"++
+ drawLine x1 ((tp ! uppermost y) - 0.5) x1 y1 "gray!50" "-"++
+ drawWires tp x1 x x2 x "black" ++
+ drawWires tp x1 y x2 y "black" ++
+ drawWires tp x1 z x2 z "black"
+ ; return $ DiagramBox top (TT x (TT y z)) r (TT (TT x y) z) bot
+ }
+ mkdiag' (GAS_loopl f) = error "not implemented"
+ mkdiag' (GAS_loopr f) = error "not implemented"
+ mkdiag' (GAS_misc f ) = mkdiag f
+
+ diagramBox :: TrackIdentifier -> Tracks -> BoxRenderer -> Tracks -> TrackIdentifier -> ConstraintM Diagram
+ diagramBox ptop pin r pout pbot = do { constrain ptop LT (uppermost pin) (-1)
+ ; constrain pbot GT (lowermost pin) 1
+ ; constrain ptop LT (uppermost pout) (-1)
+ ; constrain pbot GT (lowermost pout) 1
+ ; constrain ptop LT pbot (-1)
+ ; return $ DiagramBox ptop pin r pout pbot
+ }
+ simpleDiag text ptop pin pout pbot conn = simpleDiag' text ptop pin pout pbot conn "black"
+ simpleDiag' text ptop pin pout pbot conn color = diagramBox ptop pin defren pout pbot
+ where
+ defren tp x1 y1 x2 y2 = drawBox x1 y1 x2 y2 color text ++
+ concat (map (\(x,y) -> drawWires tp x1 x x2 y "black") conn)
+ -- ++ wires (x-1) p1 x "green"
+ -- ++ wires (x+w) p2 (x+w+1) "red"