packages feed

LambdaINet 0.1.2.1 → 0.2.0.0

raw patch · 7 files changed

+1439/−813 lines, 7 filesdep +GLFW-taskdep +monad-taskdep +transformersdep ~containers

Dependencies added: GLFW-task, monad-task, transformers, vector

Dependency ranges changed: containers

Files

LambdaINet.cabal view
@@ -1,5 +1,5 @@ name:          LambdaINet-version:       0.1.2.1+version:       0.2.0.0 homepage:      not available maintainer:    Paul H. Liu <paul@thev.net> cabal-version: >= 1.6@@ -20,7 +20,8 @@  executable LambdaINet   Main-is:  Main.lhs-  Other-Modules:  Diagram INet Lambda-  Build-Depends:  base >= 3 && < 5, OpenGL, GLFW >= 0.5.0.0, containers, mtl+  Other-Modules:  Diagram INet Lambda Grid+  Build-Depends:  base >= 3 && < 5, OpenGL, GLFW >= 0.5.0.0, containers >= 0.5.0.0, mtl, transformers, monad-task, GLFW-task, vector   Hs-Source-Dirs: src+  ghc-options: -rtsopts 
README view
@@ -12,6 +12,9 @@ CHANGES ======= +*   Tue Sep 1  PDT 2014 - New GUI based on GLFW-task, new layout algorithm.+                          bump version to 0.2.0.0.+ *   Tue Sep 15 EDT 2009 - fix GUI on OS X; fix glitch when moving nodes.                            bump version to 0.1.2. 
src/Diagram.lhs view
@@ -6,40 +6,92 @@  Everything is aligned on a grid with a unit scale. -> module Diagram where+> {-# LANGUAGE NoMonomorphismRestriction #-}+> module Diagram +>   ( Atom(..)+>   , Port(..)+>   , Diagram(..)+>   , Direction(..) +>   , Dirty(..)+>   , HLSet+>   , Frame+>   , M+>   , unit, unit'+>   , margin+>   , initWindow+>   , setupEvents+>   , setDiagram+>   , modifyDiagram+>   , getDiagram+>   , getFont+>   , getFactor+>   , getDirty+>   , whenDirty+>   , setDirty+>   , renderString+>   , renderText+>   , renderFrame+>   , renderDiagram+>   , emptyFrame+>   , betweenFrame +>   , vector3+>   , color3+>   , drawApplicator+>   , drawAbstractor+>   , drawDelimiter+>   , drawDuplicator+>   , drawCycle+>   , drawEraser+>   , drawTwoPin+>   , drawSingle+>   , autoAdjust+>   ) where  > import qualified Graphics.UI.GLFW as GLFW > import qualified Graphics.Rendering.OpenGL as GL+> import qualified Graphics.Rendering.OpenGL.GL.Texturing.Objects as TO > import Graphics.Rendering.OpenGL (($=), GLclampf, GLfloat) -> import Data.IntMap as IntMap hiding (filter, map)-> import qualified Data.Set as Set +> import qualified Data.Vector.Unboxed as UV +> import qualified Data.Set as Set+> import qualified Data.Map as Map +> import qualified Data.IntMap as IntMap +> import Data.List (sortBy, elemIndex)+> import Data.Complex > import Data.IORef > import Data.Maybe (fromMaybe, fromJust)--> import System.IO.Unsafe+> import Control.Arrow (first, second)+> import Control.Monad+> import Control.Monad.Trans+> import Control.Monad.Trans.State+> import Control.Monad.IO.Class+> import Control.Monad.Task+> import Graphics.UI.GLFW.Task+> import Debug.Trace+> import Grid  > import Data.Bits ( (.&.) ) > import Foreign ( withArray ) > import Paths_LambdaINet (getDataFileName) -debugger +> type Font = TO.TextureObject -> debug = seq . unsafePerformIO . (putStrLn $!!)-> debug1 s v = seq (unsafePerformIO $ putStrLn $!! (s ++ show v)) v-> ($!!) f s = seq (length s) (f s)+> type Color = (GLfloat -> GL.Color4 GLfloat) -> IO ()  > data Atom = Atom {  >   atomID     :: Int, >   atomLabel  :: String, >   atomPorts  :: [Port], >   atomSize   :: Size,->   atomDraw   :: IO ()                 -- drawing procedure+>   atomDraw   :: Color -> Font -> IO ()                 -- drawing procedure >   }  > instance Eq Atom where >    a == b = atomID a == atomID b +> instance Ord Atom where+>    compare a b = atomID a `compare` atomID b+ > instance Show Atom where >    show a = "(id=" ++ show (atomID a) ++ ", label=" ++ atomLabel a ++ >             ", ports=" ++ show (atomPorts a) ++ ", size=" ++ @@ -55,23 +107,35 @@ > instance Eq Port where >   p == q = (owner p == owner q) && (portPos p == portPos q) +> instance Ord Port where+>   compare p q = case owner p `compare ` owner q of+>                   EQ -> portPos p `compare` portPos q+>                   r  -> r+ > instance Show Port where >   show p = "(" ++ show (atomID (owner p)) ++ "-" ++  >            show (atomID (owner (portEnd p))) ++ >            ", dir=" ++ show (portDir p) ++ ")" +> data Direction = N | W | S | E deriving (Show, Eq, Enum, Ord)+> type Position = (Float, Float)+> type Size = (Float, Float)                -- radius in X and Y direction++> portdir' :: Direction -> Port -> Direction > portdir' d p =  >   let a = owner p >   in toEnum ((fromEnum (portDir p) + fromEnum d) `mod` 4) >-> portdir posMap p = +> portdir :: Monad m => Port -> GridT Direction m Direction+> portdir p = do >   let a = owner p->       d = maybe N snd (IntMap.lookup (atomID a) posMap)->   in portdir' d p+>   d <- lookupNode (atomID a) >>= return . maybe N snd +>   return $ portdir' d p >+> portpos' :: Direction -> Position -> Position > portpos' dir (x, y) = ->   let r = sqrt $ fromIntegral (x * x + y * y)->       t = asin (fromIntegral y / r)+>   let r = sqrt (x * x + y * y)+>       t = asin (y / r) >       t1 = if x < 0 then pi - t else t >   in case dir of >     N -> (x, y)@@ -79,94 +143,78 @@ >     S -> vec r (t1 + pi) >     E -> vec r (t1 - pi / 2) >   where ->     vec r t = (round (r * cos t), round (r * sin t))+>     vec r t = (r * cos t, r * sin t) -> portpos posMap p = +> portpos :: Monad m => Port -> GridT Direction m Position+> portpos p = do >   let a = owner p->       d = maybe N snd (IntMap.lookup (atomID a) posMap)->    in portpos' d (portPos p)--> type Position = (Int, Int)-> type Positions = IntMap (Position, Direction)-> type Size = (Int, Int)                -- radius in X and Y direction--> data Direction = N | W | S | E deriving (Show, Eq, Enum, Ord)+>   d <- lookupNode (atomID a) >>= return . maybe N snd +>   return $ portpos' d (portPos p)  A graph consists of isolated components, which has a starting Atom.  > data Diagram = Diagram {  >   startAtoms :: [Atom],->   allAtoms :: IntMap Atom+>   allAtoms :: IntMap.IntMap Atom >   } deriving (Eq, Show) -A grid is a set containing all occupied positions.--> type Grid = Set.Set Position--> occupied :: Grid -> Position -> Size -> Bool-> occupied grid (x, y) (w, h) = ->   any (flip Set.member grid) [(x + i, y + j) | i <- [-(w + margin) .. (w + margin)], j <- [-(h + margin) .. (h + margin)]]--> occupy :: Grid -> Position -> Size ->  Grid-> occupy grid (x, y) (w, h) = ->   foldr Set.insert grid ->     [(x + i, y + j) | i <- [-w .. w], j <- [-h .. h]]--> position :: Grid -> Position -> Position -> Size -> Position-> position grid (x, y) (dx, dy) (w, h) = ->   if occupied grid (x, y) (w, h) ->     then position grid (x + dx * margin, y + dy * margin) (dx, dy) (w, h)->     else (x, y)+> emptyDiagram = Diagram [] IntMap.empty  The layout process maintains a list of ports to be checked, and for each port:    1. check its direction; -  2. if its connecting Atom is not layed out, put it along-     the port direction such that it doesn't overlap with anything.+  2. if its connecting Atom is not layed out, put it along the port direction.    3. put those unchecked ports of the connected Atom in the list;    4 repeat until nothing's left. -> layout :: [Int] -> (Positions, Grid) -> [Port] -> ->           (Positions, Grid)-> layout visited sol [] = sol-> layout visited (posMap, grid) (p:ps) = +> -- layoutByPort :: Monad m => [Port] -> GridT Direction m ()+> layoutByPort visited [] = return ()+> layoutByPort visited (p:ps) = do >   let a = owner p >       i = atomID a->       ((x, y), _) = posMap ! i >       q = portEnd p >       b = owner q >       j = atomID b->       d = maybe (autorotate (portdir posMap p) (portdir' N q)) snd ->             (IntMap.lookup j posMap)->       (xd, yd, dx, dy) = placement (portdir posMap p) (portdir' d q)->                                    (portpos posMap p) (portpos' d (portPos q))->       (aw, ah) = atomSize a->       (bw, bh) = atomSize b->       pos = position grid (x + xd * (aw + bw + margin) + dx, ->               y + yd * (ah + bh + margin) + dy) (xd, yd) (bw, bh)->       rs = filter (/= q) (atomPorts b)->       posMap' = insert j (pos, d) posMap ->   in if elem j visited->     then layout visited (posMap, grid) ps->     else case IntMap.lookup j posMap of->       Just (pos, _) -> layout (atomID a : visited) (posMap, occupy grid pos (bw, bh)) (rs ++ ps)->       Nothing  -> layout (atomID a : visited) (posMap', occupy grid pos (bw, bh)) (rs ++ ps)+>   case IntMap.lookup j visited of+>     Just _  -> layoutByPort visited ps+>     Nothing -> do+>       node <- lookupNode j +>       case node of+>         Just _ -> return ()+>         Nothing -> do +>           (((x, y), r), _) <- lookupNode i >>= return . fromMaybe (notFound "layoutByPort:" i)+>           pd <- portdir p+>           pp <- portpos p+>           let qd = autorotate pd (portdir' N q)+>               (xd, yd, dx, dy) = placement pd (portdir' qd q) pp (portpos' qd (portPos q))+>               (bw, bh) = atomSize b+>               rb = sqrt (bw * bw + bh * bh) / 2+>           -- traceShow ("newNode", j) $+>           newNode j (x + xd * (r + rb + realToFrac margin) + dx, y + yd * (r + rb + realToFrac margin) + dy) rb qd+>       let ht = filter (/= q) (atomPorts b)  +>       layoutByPort (IntMap.insert i () visited) $ ht ++ ps  +> interleave (x:xs) (y:ys) = x : y : interleave xs ys+> interleave [] ys = ys+> interleave xs [] = xs++> notFound msg i = error (msg ++ " atom " ++ show i ++ " not found")+ The placement returns the relative position and adjustment according to the line directions. -> placement N N (x1, y1) (x2, y2) = (signum x1, -1, x1 - x2, y1 - y2)-> placement N S (x1, y1) (x2, y2) = (signum x1,  1, x1 - x2, 0)-> placement S S (x1, y1) (x2, y2) = (signum x1,  1, x1 - x2, y1 - y2)-> placement S N (x1, y1) (x2, y2) = (signum x1, -1, x1 - x2, 0)-> placement E E (x1, y1) (x2, y2) = (-1, signum y1, x1 - x2, y1 - y2)-> placement E W (x1, y1) (x2, y2) = ( 1, signum y1, 0, y1 - y2)-> placement W W (x1, y1) (x2, y2) = ( 1, signum y1, x1 - x2, y1 - y2)-> placement W E (x1, y1) (x2, y2) = (-1, signum y1, 0, y1 - y2)+> placement N N (x1, y1) (x2, y2) = (signum x1, 0, x1 - x2, y1 - y2)+> placement N S (x1, y1) (x2, y2) = (0,  1, x1 - x2, 0)+> placement S S (x1, y1) (x2, y2) = (signum x1, 0, x1 - x2, y1 - y2)+> placement S N (x1, y1) (x2, y2) = (0, -1, x1 - x2, 0)+> placement E E (x1, y1) (x2, y2) = (0,  signum y1, x1 - x2, y1 - y2)+> placement E W (x1, y1) (x2, y2) = (1,  0, 0, y1 - y2)+> placement W W (x1, y1) (x2, y2) = (0,  signum y1, x1 - x2, y1 - y2)+> placement W E (x1, y1) (x2, y2) = (-1, 0, 0, y1 - y2) > placement _ _ _ _ = error "impossible placement: direction not match!"  The autorotate function returns a rotation (with respect to N) such that the@@ -189,15 +237,16 @@ > autorotate S W = E > autorotate S S = S -> margin = 2-> unit = 12 :: GLfloat         -- grid unit is 10 pixel+> margin = 3 +> unit = 12 :: Float         -- grid unit in pixels+> unit' = realToFrac unit+> speed = 0.95               -- how fast it zooms  > showDiagram = undefined  > initWindow w h = do >   let row = realToFrac h / unit / 2 >       col = realToFrac w / unit / 2->   writeIORef rowcolRef (row, col) >   GLFW.openWindow (GL.Size w h) [GLFW.DisplayAlphaBits 8] GLFW.Window >   GLFW.windowTitle $= "Diagram" >   GL.clearColor $= clearcolor@@ -209,299 +258,306 @@ >   GL.lineWidth $= 1.5 >   -- load font >   font <- loadFont->   writeIORef fontRef (Just font) ->   GLFW.windowSizeCallback $= reshape--too troublesome to carry this around, so make it IORef!+>   return (S0 font (row, col) (0,0,1) emptyDiagram Lightly) -> fontRef = unsafePerformIO (newIORef Nothing)-> rowcolRef = unsafePerformIO (newIORef (0, 0))+> setupEvents reduce = do+>   fork $ forever $ watch onRefresh >> setDirty Lightly+>   fork $ forever $ watch onSize >>= reshape+>   fork $ forever $ handleLeftButton reduce+>   fork $ forever $ handleZoom+>   fork $ forever $ handleKeys+>   fork $ watch (onKey >=> isPress >=> isKey GLFW.ESC) >> exit+>   fork $ watch onClose >> exit  > reshape (GL.Size w h) = do->   GL.viewport $= (GL.Position 0 0, GL.Size (fromIntegral w) (fromIntegral h))->   GL.matrixMode $= GL.Projection->   GL.loadIdentity+>   liftIO $ do+>     GL.viewport $= (GL.Position 0 0, GL.Size (fromIntegral w) (fromIntegral h))+>     GL.matrixMode $= GL.Projection+>     GL.loadIdentity >   let row = realToFrac h / unit / 2 >       col = realToFrac w / unit / 2 >       (r, c) = (realToFrac row, realToFrac col)->   GL.ortho2D (-c) c (-r) r->   writeIORef rowcolRef (row, col)+>   liftIO $ GL.ortho2D (-c) c (-r) r+>   modifyRowCol $ const (row, col) -> renderDiagram posMap d = ->   let starts = startAtoms d->       pos = map (\a -> (maybe Nothing (Just . fst) (IntMap.lookup (atomID a) posMap), a)) starts->       pg@(posMap', grid) = foldr (\ (p, a) (pm, gr) -> ->           let i = atomID a->               (wa, ha) = atomSize a->               mx = maximum (0 : map fst (Set.elems gr))->               p' = fromMaybe (mx + 4, 0) p->               dir = maybe N snd (IntMap.lookup i pm)->           in layout [] (insert i (p', dir) pm, occupy gr p' (wa, ha)) (atomPorts a)) (posMap, Set.empty) pos->       (io, grid') = render pg->   in ((posMap', grid'), io)->   where ->     render (posMap, grid) = foldWithKey (\i a (io, grid) -> ->       let ((x, y), d) = posMap ! i->           (lines, grid') = foldr (\ (p, q) (ls, grid) -> ->               let (l, grid') = runLine grid p q->               in (l:ls, grid')) ([], grid)->             [ ((x + px, y + py, portdir posMap p),->               (x' + qx, y' + qy, portdir posMap q))->             | p <- atomPorts a,->               let q = portEnd p, ->               let j = atomID (owner q),->               i < j || (i == j && portPos p <= portPos q),->               let ((x', y'), _) = posMap ! j,->               let (px, py) = portpos posMap p,->               let (qx, qy) = portpos posMap q ]->           action = do ->             GL.preservingMatrix (do->               GL.translate (vector3 (fromIntegral x) (fromIntegral y) 0) ->               let o = maybe N snd (IntMap.lookup i posMap)->               GL.rotate (realToFrac (90 * fromEnum o)) (vector3 0 0 1)->               atomDraw a) ->             GL.preservingMatrix $ mapM_ (\l -> do->               GL.color linecolor->               GL.renderPrimitive GL.LineStrip $ mapM_ (\ (x0, y0) ->->                 GL.vertex (vertex3 (fromIntegral x0) (fromIntegral y0) 0)) l) lines->             io->       in (action, grid')) (return (), grid) (allAtoms d)+> data Dirty = None | Lightly | Dirty deriving (Eq, Show, Enum, Ord) -The following is a very naive but fast line layout algorithm.+> data S = S0 { font    :: Font+>             , rowcol  :: (Float, Float)+>             , factor  :: (Float, Float, Float)+>             , diagram :: Diagram+>             , dirty   :: Dirty+>             }+>    +> type M m a = TaskT Event (GridT Direction (StateT S m)) a -> runLine grid (x0, y0, d0) (x1, y1, d1) = ->   let (var, f) = flexLine (x0, y0, d0) (x1, y1, d1)->       lines = map f (iterateList var)->       g l = let l' = mark l --(take (length l - 2) (tail l))->              in Set.fromList l' --Set.\\ Set.fromList (take 3 l' ++ drop (length l' - 3) l')->       LineSeg (l, s) _ = minimum $ take 10 $->                map (\x -> let s = g x in LineSeg (x, s) (Set.size (Set.intersection grid s))) lines->   in (l, Set.union grid s)->   where->     mark [] = []->     mark [(x0,y0)] = []->     mark l@((x0,y0):(x1,y1):rs) = Set.toList $ Set.fromList $->       [(x, y) | x <- segment x0 x1, y <- segment y0 y1] ++ mark (tail l)->     segment x0 x1 | x0 == x1 = [x0]->     segment x0 x1 = x0 : segment (x0 + signum (x1 - x0)) x1+> modifyRowCol f  = lift $ lift $ modify $ \s -> s { rowcol = f (rowcol s), dirty = Lightly } +> modifyFactor f  = lift $ lift $ modify $ \s -> s { factor = f (factor s), dirty = Lightly } +> modifyDiagram f = lift $ lift $ modify $ \s -> s { diagram = f (diagram s), dirty = Dirty } +> setDiagram = modifyDiagram . const+> getFont    = lift $ lift $ fmap font get +> getRowCol  = lift $ lift $ fmap rowcol get +> getFactor  = lift $ lift $ fmap factor get +> getDiagram = lift $ lift $ fmap diagram get +> getDirty   = lift $ lift $ fmap dirty get +> setDirty d = lift $ lift $ modify $ \s -> s { dirty = d }+> whenDirty f = do+>   d <- lift $ lift $ fmap dirty get+>   when (d /= None) f -> data LineSeg a b = LineSeg a b deriving (Eq, Show)-> instance (Eq a, Eq b, Ord b) => Ord (LineSeg a b) where->   compare (LineSeg _ x) (LineSeg _ y) = compare x y+To support animating the "morphism" between two diagrams, we render each diagram+into a Frame. -> running grid l = running' l+> data Frame = Frame +>   { objs  :: IntMap.IntMap (Atom, GLfloat, GLfloat, GLfloat, GLfloat)  -- (atom, x, y, rotation, alpha)+>   , wires :: Map.Map (Port, Port) (Wire, GLfloat)                      -- wire between ports+>   } deriving (Eq, Show)++> emptyFrame = Frame IntMap.empty Map.empty++> type Wire = UV.Vector (Float, Float)++> wirePoints = 15 -- number of line connecting points in every wire++We can morph a frame into another by compute a frame in between based on+a ratio between 0 (same as f0) and 1 (same as f1).++> betweenFrame (Frame objs wires) (Frame objs' wires') t = Frame obs wis >   where->     running' l@(p@(x0, y0):q@(x1, y1):r@(x2, y2):xs) = ->       (Set.member p grid && Set.member q grid && Set.member r grid && ->        (((x0 == x1) && (x1 == x2) && (y1 + y1 == y0 + y2)) || ->         ((y0 == y1) && (y1 == y2) && (x1 + x1 == x0 + x2)))) || running' (tail l)->     running' _ = False+>     obs = IntMap.mergeWithKey bothO leftO rightO objs objs'+>     wis = Map.mergeWithKey bothW leftW rightW wires wires'+>     bothO _ (i, x, y, r, a) (j, x', y', r', a') | atomPorts i == atomPorts j = Just (j, x %% x', y %% y', r %%% r', a %% a')+>                                                 | otherwise = Just (j { atomDraw = drawBoth }, x %% x', y %% y', r', t)+>       where drawBoth _ f = do+>               GL.preservingMatrix (GL.rotate (r - r') (vector3 0 0 1) >> +>                                    atomDraw i (GL.color . ($ (1 - t))) f)+>               GL.preservingMatrix (atomDraw j (GL.color . ($ t)) f) +>     leftO  = IntMap.map (\(i, x, y, r, _) -> (i, x, y, r, 1 - t)) +>     rightO = IntMap.map (\(i, x, y, r, _) -> (i, x, y, r, t)) +>     bothW _ (us, _) (vs, _) = Just (UV.zipWith (\(x, y) (x', y') -> (x %% x', y %% y')) us vs, 1)+>     leftW = Map.map (\(w, _) -> (w, 1 - t))+>     rightW =Map.map (\(w, _) -> (w, t))+>     x %% y = x * (1 - t') + y * t'+>     r %%% r' = if r' > r then if r' - r > 180 then r %% (r' - 360) else r %% r'+>                          else if r - r' > 180 then r %% (r' + 360) else r %% r'+>     t' = realToFrac t -> iterateList :: [[a]] -> [[a]]-> iterateList l = [ zipWith (!!) l idx | idx <- dia (length l) 0 ]+Sometimes it is useful to highlight a set of wires when we render the frame. -> dia d n = dia' d n ++ dia d (n + 1)+> type HLSet = Set.Set ((Int, Int), (Int, Int)) -- A pair of (atomID, portIndex) +> elemHLSet (p, q) s = Set.member (getIdx p, getIdx q) s+>   where getIdx p = (atomID a, u)+>           where a = owner p+>                 u = maybe (-1) id $ elemIndex p (atomPorts a)++> renderFrame font hlset (Frame objs wires) = do+>   IntMap.fold ((>>) . drawObj) (return ()) objs+>   Map.foldWithKey ((.) (>>) . drawWire) (return ()) wires >   where->     dia' 1 n = [[n]]->     dia' d n = [ u : v | u <- [0 .. n], v <- dia' (d - 1) (n - u)]+>     drawObj (a, x, y, d, c) = GL.preservingMatrix $ do+>       GL.translate (vector3 x y 0) +>       GL.rotate d (vector3 0 0 1)+>       atomDraw a (GL.color . ($ c)) font +>     drawWire pq@(p, q) (lines, c) = do+>       let i = atomID $ owner p+>           j = atomID $ owner q+>           (_, _, _, _, a) = objs IntMap.! i+>           (_, _, _, _, b) = objs IntMap.! j+>       GL.preservingMatrix $ GL.color (linecolor c) >> polyline lines +>       when (elemHLSet pq hlset) $ GL.preservingMatrix $+>         GL.translate (vector3 1 1 0) >> GL.color (linecolor c) >> polyline lines  -> flexLine :: (Int, Int, Direction) -> (Int, Int, Direction) -> ->             ([[Int]], [Int] -> [(Int, Int)])-> flexLine (x0, y0, N) (x1, y1, N) = ->   ([inc (max y0 y1)], \[y] -> [(x0, y0), (x0, y), (x1, y), (x1, y1)])-> flexLine (x0, y0, S) (x1, y1, S) = ->   ([dec (min y0 y1)], \[y] -> [(x0, y0), (x0, y), (x1, y), (x1, y1)])-> flexLine (x0, y0, E) (x1, y1, E) = ->   ([inc (max x0 x1)], \[x] -> [(x0, y0), (x, y0), (x, y1), (x1, y1)])-> flexLine (x0, y0, W) (x1, y1, W) = ->   ([dec (min x0 x1)], \[x] -> [(x0, y0), (x, y0), (x, y1), (x1, y1)])-> flexLine (x0, y0, N) (x1, y1, E) = ->   ([inc y0, inc x1], \[y, x] -> [(x0, y0), (x0, y), (x, y), (x, y1), (x1, y1)])-> flexLine (x0, y0, N) (x1, y1, W) = ->   ([inc y0, dec x1], \[y, x] -> [(x0, y0), (x0, y), (x, y), (x, y1), (x1, y1)])-> flexLine (x0, y0, S) (x1, y1, E) = ->   ([dec y0, inc x1], \[y, x] -> [(x0, y0), (x0, y), (x, y), (x, y1), (x1, y1)])-> flexLine (x0, y0, S) (x1, y1, W) = ->   ([dec y0, dec x1], \[y, x] -> [(x0, y0), (x0, y), (x, y), (x, y1), (x1, y1)])-> flexLine (x0, y0, N) (x1, y1, S) | y0 > y1 = ->   ([inc y0, alt ((x0 + x1) `div` 2), dec y1], ->   \[y, x, y'] -> [(x0, y0), (x0, y), (x, y), (x, y'), (x1, y'), (x1, y1)])-> flexLine (x0, y0, N) (x1, y1, S) = ->   ([alt ((y0 + y1) `div` 2)], ->   \[y] -> [(x0, y0), (x0, y), (x1, y), (x1, y1)])-> flexLine (x0, y0, E) (x1, y1, W) | x0 > x1 = ->   ([inc x0, alt ((y0 + y1) `div` 2), dec x1], ->   \[x, y, x'] -> [(x0, y0), (x, y0), (x, y), (x', y), (x', y1), (x1, y1)])-> flexLine (x0, y0, E) (x1, y1, W) = ->   ([alt ((x0 + x1) `div` 2)], ->   \[x] -> [(x0, y0), (x, y0), (x, y1), (x1, y1)])-> flexLine p q = flexLine q p+> renderDiagram :: MonadIO m => Diagram -> M m Frame +> renderDiagram d = lift $ do+>   let atoms = allAtoms d+>   existing <- getGrid+>   let removed = foldr IntMap.delete existing $ IntMap.keys (allAtoms d)+>   mapM_ removeNode $ IntMap.keys removed+>   mapM_ layoutAtoms (startAtoms d)+>   nodes <- getGrid+>   let newNodes = foldr IntMap.delete nodes $ IntMap.keys existing+>   adjustNodes atoms nodes+>   nodes <- getNodes+>   let nodes' = map (fst . snd) nodes+>   foldl ((. drawAtom nodes' atoms) . (>>=)) (return emptyFrame) nodes +>   where+>     layoutAtoms a = do+>        let i = atomID a+>            (wa, ha) = atomSize a+>            r = sqrt (wa * wa + ha * ha) / 2+>        mx <- getNodes >>= return . maximum . (0:) . map (fst . fst . fst . snd) +>        lookupNode i >>= maybe (newNode i (mx, 0) r N) (\_ -> return ())+>        layoutByPort IntMap.empty $ atomPorts a+>     drawAtom nodes atoms (i, (((x, y), _), o)) (Frame objs wires) = do+>       let a = fromMaybe (notFound "drawAtom:" i) $ IntMap.lookup i atoms+>       let objs' = IntMap.insert i (a, realToFrac x, realToFrac y, realToFrac (90 * fromEnum o), 1) objs+>       let cond i j p q = i < j || (i == j && portPos p <= portPos q)+>       lines <- linesFromAtom cond lineSeg (i, a, o, x, y)+>       let wires' = foldr (\(k, w) ws -> Map.insert k (polyBezier w, 1) ws) wires lines+>       return $ Frame objs' wires'+>       where+>         lineSeg id x y x' y' pd qd = (id, +>           (x, y) : navigate nodes (x + px', y + py') (x' + qx', y' + qy') ++ [(x', y')])+>           where (px', py') = extend pd+>                 (qx', qy') = extend qd -> inc x = [x..]-> dec x = [x, x-1 ..]-> alt x = alt' (x : inc x) (tail (dec x))->   where alt' (i:is) (j:js) = i : j : alt' is js+> linesFromAtom cond f (i, a, o, x, y) = sequence +>   [ do let pd = portdir' o p+>            (px, py) = portpos' o (portPos p)+>        qd <- portdir q+>        (qx, qy) <- portpos q+>        (x', y') <- lookupNode j >>= return . maybe (notFound "sequence line:" j) (fst . fst)+>        return $ f (p, q) (x + px) (y + py) (x' + qx) (y' + qy) pd qd+>   | p <- atomPorts a,+>     let q = portEnd p,+>     let j = atomID (owner q),+>     cond i j p q ] +Adjust the rotation of each node to make lines "more straight". -> data UserAction = UserAction (IO (UserAction, IO ()))+> adjustNodes atoms = sequence_ . map adjustAtom . IntMap.keys+>   where+>     adjustAtom i = do+>       let a = fromMaybe (notFound "adjustNodes:" i) $ IntMap.lookup i atoms+>           ds = [ N, W, S, E ]+>           cond _ _ _ _ = True+>       (x, y) <- lookupNode i >>= return . fst . fst . fromMaybe (notFound "impossible1" i)+>       weighted <- sequence [ linesFromAtom cond (const lineDis) (i, a, o, x, y) >>= return . sum | o <- ds ]+>       let sorted = sortBy (\u v -> compare (snd u) (snd v)) $ zip ds weighted +>           w = head $ sorted+>       modifyNodeVal i $ \d -> if fromMaybe (notFound "impossible2" d) (lookup d sorted) - snd w > 0.1 then fst w else d+>     lineDis x0 y0 x1 y1 pd qd = dis (x0, y0) (x1, y1)+>       -- dis (x0, y0) (x0', y0') + dis (x0', y0') (x1', y1') + dis (x1', y1') (x1, y1)+>       where (px', py') = extend pd+>             (qx', qy') = extend qd+>             x0' = x0 + px'+>             y0' = y0 + py'+>             x1' = x1 + qx'+>             y1' = y1 + qy'+>     dis (x0, y0) (x1, y1) = sqrt (dx * dx + dy * dy)+>       where dx = x0 - x1+>             dy = y0 - y1 -> lastKeyTime = unsafePerformIO (newIORef 0)-> readKeyPress l = do->   t <- GL.get GLFW.time->   t0 <- readIORef lastKeyTime->   k <- readKeyPress' l->   case k of->     Nothing -> return Nothing->     _ -> if t - t0 < 0.4->       then return Nothing->       else do->         writeIORef lastKeyTime t->         return k+> extend N = ( 0,  1)+> extend S = ( 0, -1)+> extend W = (-1,  0)+> extend E = ( 1,  0)++> navigate nodes (x0,y0) (x1, y1) = +>   case (backTo . sorted . collided . relative) nodes of+>     [] -> [(x0,y0),(x1,y1)]+>     node@((x2,y2),r):_ -> +>       if abs (x0 - x1) < 1e-4 && abs (y0 - y1) < 1e-4 +>         then [(x0, y0)]+>         else +>           case midPoint node (x0,y0) (x1,y1) of+>             Just (x3, y3) -> navigate nodes (x0,y0) (x3,y3) ++ navigate nodes (x3,y3) (x1,y1)+>             Nothing -> [(x0,y0), (x1,y1)] >   where ->     readKeyPress' :: Enum a => [a] -> IO (Maybe a)->     readKeyPress' [] = return Nothing->     readKeyPress' (k:ks) = do->       p <- GLFW.getKey k->       if p == GLFW.Press then return (Just k) else readKeyPress' ks+>     (p, a) = polar $ toC (x1, y1)+>     fromC (x :+ y) = (x + x0, y + y0)+>     toC (x, y) = (x - x0) :+ (y - y0)+>     backTo   = map (\((p, t), d) -> (fromC $ mkPolar p (t + a), d))+>     sorted   = sortBy (\u v -> compare (snd $ fst u) (snd $ fst v)) . +>                map (\(c, d) -> (polar c, d))+>     collided = filter (\(x :+ y, d) -> x >= 0 && x <= p && y >= -(d+tolerance) && y <= (d+tolerance))+>     relative = map ((\((r, b), d) -> (mkPolar r (b - a), d)) . first (polar . toC)) -> handleUserAction factor (keySet, keyHandle) reduce d = do->   let r@((_, grid), _) = renderDiagram empty d->   autoAdjust grid factor->   buttonReleased d r->   where->     buttonReleased d r@((posMap, grid), render) = do->       left <- GLFW.getMouseButton GLFW.ButtonLeft->       right <- GLFW.getMouseButton GLFW.ButtonRight->       zoom <- GLFW.getKey GLFW.LALT->       shift <- GLFW.getKey GLFW.LCTRL->       case (left == GLFW.Press, right == GLFW.Press, ->             zoom == GLFW.Press, shift == GLFW.Press) of->         (True, _, False, False) -> processButton GLFW.ButtonLeft->         (_, _, True, False) -> processZoom GLFW.LALT->         (_, _, False, True) -> processShift GLFW.LCTRL->         (_, True, _, _) -> processButton GLFW.ButtonRight->         _ -> do->             k <- readKeyPress (' ' : keySet)->             case k of ->               Just k' ->->                 if k' == ' '->                   then do->                     --writeIORef factor (0,0,1)->                     autoAdjust grid factor->                     return (UserAction $ buttonReleased d r, render)->                   else do->                     (d', r'@(_, render')) <- keyHandle k' d r->                     return (UserAction $ buttonReleased d' r', render')->               Nothing -> return (UserAction $ buttonReleased d r, render)->       where->         processButton but = do->           GL.Position mx my <- GL.get GLFW.mousePos->           (cx, cy, scale) <- readIORef factor->           (row, col) <- readIORef rowcolRef->           let (w, h) = (col * unit, row * unit) ->               atom = locateAtom (allAtoms d) posMap ->                     ((fromIntegral mx - w - cx) / scale / unit) ->                     ((h - fromIntegral my - cy) / scale / unit)->           t0 <- GL.get GLFW.time->           return (UserAction $ buttonPressed but->                   d r atom (mx, my) t0, render)->->         processZoom key = do->           GL.Position mx my <- GL.get GLFW.mousePos->           t0 <- GL.get GLFW.time->           return (UserAction $ mouseZoom key (mx, my, mx, my, t0) d r, render)+> tolerance = margin / 5 ->         processShift key = do->           GL.Position mx my <- GL.get GLFW.mousePos->           t0 <- GL.get GLFW.time->           return (UserAction $ mouseShift key (mx, my, mx, my, t0) d r, render)+> midPoint ((x0,y0),d) (x1,y1) (x2,y2) = +>   let (p, a) = polar $ toC (x1, y1)+>       (q, b) = polar $ toC (x2, y2)+>       d' = d + tolerance + tolerance+>       a' = acos (d'/p) `or` acos (d/p)+>       b' = acos (d'/q) `or` acos (d/q)+>       t  = ((a + b) - a' + b') / 2+>       t' = ((a + b) - b' + a') / 2+>       r  = d' / cos (t - b' - b)+>       r' = d' / cos (t' - a' - a)+>   in if isNaN a' || isNaN b' then Nothing+>      else case (isNaN r, isNaN r') of+>             (True, True)  -> Nothing+>             (False, True) -> Just $ fromC $ mkPolar r t+>             (True, False) -> Just $ fromC $ mkPolar r' t'+>             _             -> Just $ fromC $ if abs r + 0.01 < abs r' then mkPolar r t else mkPolar r' t'+>   where+>     fromC (x :+ y) = (x + x0, y + y0)+>     toC (x, y) = (x - x0) :+ (y - y0)+>     or x y = if isNaN x then y else x ->     buttonPressed but d r@((posMap, _), render) atom mp t0 = do->       status <- GLFW.getMouseButton but->       if status == GLFW.Release->         then case atom of->           Just a -> ->             if but == GLFW.ButtonRight->               then do->                 (ids, d') <- reduce (atomID a)->                 let posMap' = filterWithKey (\i _ -> elem i ids) posMap->                     r'@(_, render') = renderDiagram posMap' d'->                 return (UserAction $ buttonReleased d' r', render')->               else do->                 let (pos, o) = posMap ! atomID a->                     o' = toEnum ((fromEnum o + 1) `mod` 4)->                     posMap' = adjust (const (pos, o')) (atomID a) posMap->                     r'@(_, render') = renderDiagram posMap' d->                 return (UserAction $ buttonReleased d r', render')->           _ -> return (UserAction $ buttonReleased d r, render)->         else do->           t1 <- GL.get GLFW.time->           case (t1 - t0 > 0.4, atom) of->             (True, Just a) -> do->               let ((x, y), _) = posMap ! atomID a->               return (UserAction $ buttonHolding but d r a ((x, y), mp), render)->             _ -> return (UserAction $ buttonPressed but d r atom mp t0, render)->     buttonHolding but d r@((posMap, _), _) atom s@((ax, ay), (mx, my)) = do->       status <- GLFW.getMouseButton but->       GL.Position mx' my' <- GL.get GLFW.mousePos->       (_, _, scale) <- readIORef factor->       let ax' = ax + truncate (realToFrac (mx' - mx) / scale / unit)->           ay' = ay + truncate (realToFrac (my - my') / scale / unit)->           i = atomID atom->           posMap' = adjust (\ ((x, y), d) -> ((ax', ay'), d)) i posMap ->           r'@(_, render) = renderDiagram posMap' d->       if status == GLFW.Release->         then return (UserAction $ buttonReleased d r', render)->         else return (UserAction $ buttonHolding but d r' atom s, render)->->     mouseZoom key state d r = do->       status <- GLFW.getKey key->       if status == GLFW.Release ->         then return (UserAction $ buttonReleased d r, snd r)->         else do->           (dx, dy, state') <- relativeSpeed state->           (cx, cy, scale) <- readIORef factor->           let scale' = scale * (dx - dy) / 100->           writeIORef factor (cx, cy, scale + scale')->           return (UserAction $ mouseZoom key state' d r, snd r)+> onLocated f g = do+>   (GL.Position mx my) <- liftIO $ GL.get GLFW.mousePos+>   (cx, cy, scale) <- getFactor+>   (row, col) <- getRowCol +>   let (w, h) = (col * unit, row * unit) +>       x = (fromIntegral mx - w - cx) / scale / unit +>       y = (h - fromIntegral my - cy) / scale / unit+>   lift (locateNode (x, y)) >>= \i -> case i of +>     [] -> g +>     uid:_ -> f uid ->     mouseShift key state d r = do->       status <- GLFW.getKey key->       if status == GLFW.Release->         then return (UserAction $ buttonReleased d r, snd r)->         else do->           (dx, dy, state') <- relativeSpeed state->           (cx, cy, scale) <- readIORef factor->           writeIORef factor (cx - dx, cy - dy, scale)->           return (UserAction $ mouseShift key state' d r, snd r)+> handleLeftButton reduce = do+>   watch (onButton >=> isPress >=> isButton GLFW.ButtonLeft)+>   GL.Position mx my <- liftIO $ GL.get GLFW.mousePos+>   onLocated (moveOrReduce (mx, my)) (shift (mx, my))+>   where+>     shift (mx, my) = do+>       watch (onPos `orElse` (onButton >=> isRelease)) >>=+>         either track (\_ -> return ())+>       where+>         track (GL.Position x y) = do+>           let dx = 10 * fromIntegral (x - mx) / unit+>               dy = 10 * fromIntegral (my - y) / unit+>           modifyFactor $ \(cx, cy, scale) -> (cx - dx / scale, cy - dy / scale, scale)+>           shift (x, y)+>     moveOrReduce (mx, my) uid = do+>       src <- lift getGrid+>       (ax, ay) <- lift $ lookupNode uid >>= return . maybe (notFound "moveOrReduce:" uid) (fst . fst)+>       watch (onPos `orElse` (onButton >=> isRelease)) >>=+>         either (move src ax ay) (\_ -> reduce uid >> setDirty Dirty)+>       where+>         move src ax ay (GL.Position x y) = do+>           (_, _, scale) <- getFactor+>           let ax' = ax + (realToFrac (x - mx) / scale / unit)+>               ay' = ay + (realToFrac (my - y) / scale / unit)+>           lift $ setGrid src+>           lift $ modifyNodePos uid (\(_, r) -> ((ax', ay'), r))+>           setDirty Lightly+>           watch (onPos `orElse` (onButton >=> isRelease)) >>=+>             either (move src ax ay) (\_ -> return ())+>       +> handleKeys = do+>   watch (onKey >=> isRelease >=> isKey ' ')+>   autoAdjust +> +> handleZoom = do+>   oz <- liftIO $ GL.get GLFW.mouseWheel+>   z  <- watch onWheel+>   modifyFactor $ \(x, y, scale) -> (x, y, scale * (speed ** fromIntegral (oz - z))) ->     relativeSpeed (mx, my, x0, y0, t0) = do->       GL.Position x y <- GL.get GLFW.mousePos->       t1 <- GL.get GLFW.time->       let (mx', my') = if signum (x0 - mx) * signum (x - x0) < 0 ||->                           signum (y0 - my) * signum (y - y0) < 0->                          then (x0, y0) else (mx, my)->           dx = fromIntegral (x - mx') / realToFrac (t1 - t0) / 1000->           dy = fromIntegral (my' - y) / realToFrac (t1 - t0) / 1000->       return (dx, dy, (mx', my', x, y, t1))+> gridBounds :: Monad m => GridT Direction m (Float, Float, Float, Float)+> gridBounds = do+>   pos <- getNodes >>= return . map (fst . snd)+>   return $ foldr updateBounds (0,0,0,0) pos+>   where+>     updateBounds ((x, y), r) (x0, y0, x1, y1) = +>       (min x0 (x - r), min y0 (y - r), max x1 (x + r), max y1 (y + r)) -> autoAdjust grid factor = do->   GL.Size w h <- GL.get GLFW.windowSize ->   let (x0, y0, x1, y1) = gridBounds grid->       cx = unit * fromIntegral (x0 + x1) / 2->       cy = unit * fromIntegral (y0 + y1) / 2    ->       sx = fromIntegral (x1 - x0 + margin) * unit / fromIntegral w->       sy = fromIntegral (y1 - y0 + margin) * unit / fromIntegral h+> autoAdjust = do+>   GL.Size w h <- liftIO $ GL.get GLFW.windowSize +>   (x0, y0, x1, y1) <- lift $ gridBounds +>   let cx = unit * (x0 + x1) / 2+>       cy = unit * (y0 + y1) / 2    +>       sx = (x1 - x0 + margin) * unit / realToFrac w+>       sy = (y1 - y0 + margin) * unit / realToFrac h >       ms = max sx sy   >       s  = if ms < 1 then 1 else ms->   writeIORef factor (-cx / s, -cy / s, 1/s)+>   modifyFactor $ const (-cx / s, -cy / s, 1/s) -> gridBounds grid = ->   let (xs, ys) = unzip (Set.elems grid)->   in (minimum xs, minimum ys, maximum xs, maximum ys)->   +> {- > renderGrid grid = do ->   let (c0, r0, c1, r1) = gridBounds grid       +>   (c0, r0, c1, r1) <- gridBounds  >       l1 = [(x, r0, x, r1) | x <- [c0 .. c1]]  >       l2 = [(c0, y, c1, y) | y <- [r0 .. r1]] >   GL.color gridcolor @@ -513,6 +569,7 @@ >     line (x1, y1, x2, y2) = do >       GL.vertex (vertex3 (fromIntegral x1) (fromIntegral y1) 0) >       GL.vertex (vertex3 (fromIntegral x2) (fromIntegral y2) 0)+> -}  > circle :: GLfloat -> GLfloat -> GLfloat -> IO () > circle r1 r2 step =@@ -521,17 +578,11 @@ >       vs = [ (r1 * cos i, r2 * sin i) | i <- is ] >   in mapM_ (\(x, y) -> GL.vertex (GL.Vertex3 x y 0)) vs -> locateAtom atoms posMap mx my = locate (toList posMap)->   where->     locate [] = Nothing->     locate ((i, ((x, y), _)):rs) =  ->       let a = atoms ! i->       in if inside (x, y) (atomSize a) then Just a else locate rs->     inside (x, y) (w, h) =->       (realToFrac (x - w) <= mx) &&->       (realToFrac (x + w) > mx) &&->       (realToFrac (y - w) <= my) &&->       (realToFrac (y + w) > my)+> locateAtom atoms mx my = do+>   uids <- locateNode (mx, my) +>   case uids of+>     [] -> return Nothing+>     i:_ -> return $ atoms IntMap.! i   Some primilinary font support @@ -549,8 +600,8 @@ >       x = fromIntegral (fromEnum c `quot` 16 * 8) / 128 >       dx = 8 / 128 >       dy = 16 / 256->       h = 16 / unit ->       w = 8 / unit+>       h = 16 / unit' +>       w = 8 / unit' >   GL.preservingMatrix $ GL.renderPrimitive GL.Quads (do >     GL.texCoord (texCoord2 x y) >     GL.vertex (vertex3 0 h 0)@@ -562,32 +613,32 @@ >     GL.vertex (vertex3 w h 0)) >   GL.translate (vector3 w 0 0) -> renderString s = do->   Just font <- readIORef fontRef+> renderString font s = do >   GL.texture GL.Texture2D $= GL.Enabled >   GL.textureBinding GL.Texture2D $= Just font >   GL.preservingMatrix $ mapM_ (renderChar font) s >   GL.texture GL.Texture2D $= GL.Disabled -> renderText = mapM_ out . lines->   where out s = renderString s >> GL.translate (vector3 0 (-1.25) 0)+> renderText font = mapM_ out . lines+>   where out s = renderString font s >> GL.translate (vector3 0 (-1.25) 0) +> color4 = GL.Color4 :: GLfloat -> GLfloat -> GLfloat -> GLfloat -> GL.Color4 GLfloat > color3 = GL.Color3 :: GLfloat -> GLfloat -> GLfloat -> GL.Color3 GLfloat > vector3 = GL.Vector3 :: GLfloat -> GLfloat -> GLfloat -> GL.Vector3 GLfloat > vertex3 = GL.Vertex3 :: GLfloat -> GLfloat -> GLfloat -> GL.Vertex3 GLfloat > texCoord2 = GL.TexCoord2 :: GLfloat -> GLfloat -> GL.TexCoord2 GLfloat  > clearcolor = GL.Color4 1 1 1 1-> linecolor = color3 0 0 0-> gridcolor = color3 0.9 0.9 0.9-> unitcolor = color3 0 0 0-> textcolor = color3 0 0 1-> portcolor = color3 1 0 0+> linecolor = color4 0 0 0 +> gridcolor = color4 0.9 0.9 0.9+> unitcolor = color4 0 0 0+> textcolor = color4 0 0 1+> portcolor = color4 1 0 0  The drawing routings for Nodes -> drawApplicator label = do->   GL.color unitcolor+> drawApplicator label color font = do+>   color unitcolor >   GL.renderPrimitive GL.LineStrip (circle 1.5 1.5 20) >   GL.translate (vector3 0 1.5 0) >   GL.renderPrimitive GL.LineStrip (circle 0.2 0.2 10)@@ -598,27 +649,27 @@ >   GL.renderPrimitive GL.Lines (do >      GL.vertex (vertex3 0 0 0) >      GL.vertex (vertex3 0 (-0.5) 0))->   GL.color portcolor+>   color portcolor >   GL.renderPrimitive GL.Polygon (circle 0.2 0.2 10)->   GL.color unitcolor+>   color unitcolor >   GL.translate (vector3 1.5 1.5 0) >   GL.renderPrimitive GL.LineStrip (circle 0.2 0.2 10) >   GL.renderPrimitive GL.Lines (do >      GL.vertex (vertex3 0 0 0) >      GL.vertex (vertex3 0.5 0 0))->   GL.translate (vector3 (-1.5 - 4 / unit) (-8 / unit) 0)->   renderString label+>   GL.translate (vector3 (-1.5 - 4 / unit') (-8 / unit') 0)+>   renderString font label -> drawAbstractor label = do->   GL.color unitcolor+> drawAbstractor label color font = do+>   color unitcolor >   GL.renderPrimitive GL.LineStrip (circle 1.5 1.5 20) >   GL.translate (vector3 0 1.5 0) >   GL.renderPrimitive GL.Lines (do >      GL.vertex (vertex3 0 0 0) >      GL.vertex (vertex3 0 0.5 0))->   GL.color portcolor+>   color portcolor >   GL.renderPrimitive GL.Polygon (circle 0.2 0.2 10)->   GL.color unitcolor+>   color unitcolor >   GL.translate (vector3 0 (-3) 0) >   GL.renderPrimitive GL.Lines (do >      GL.vertex (vertex3 0 0 0)@@ -629,11 +680,11 @@ >   GL.renderPrimitive GL.Lines (do >      GL.vertex (vertex3 0 0 0) >      GL.vertex (vertex3 0.5 0 0))->   GL.translate (vector3 (-1.5 - 4 / unit) (- 8 / unit) 0)->   renderString label+>   GL.translate (vector3 (-1.5 - 4 / unit') (- 8 / unit') 0)+>   renderString font label -> drawDelimiter label = do->   GL.color unitcolor+> drawDelimiter label color font = do+>   color unitcolor >   GL.renderPrimitive GL.LineStrip (do >     GL.vertex (vertex3 (-1) 0.2 0) >     GL.vertex (vertex3 (-1) (-0.2) 0)@@ -647,14 +698,18 @@ >   GL.translate (vector3 0 1 0) >   GL.renderPrimitive GL.LineStrip (circle 0.2 0.2 10) >   GL.translate (vector3 0 (-2) 0)->   GL.color portcolor+>   color portcolor >   GL.renderPrimitive GL.Polygon (circle 0.2 0.2 10)->   GL.color unitcolor->   GL.translate (vector3 1.2 (1 - 8 / unit) 0)->   renderString label+>   color unitcolor+>   GL.translate (vector3 1.2 (1 - 8 / unit') 0)+>   renderString font label -> drawDuplicator label = do->   GL.color unitcolor+> drawDuplicator = drawDuplicator' unitcolor+> drawCycle True  = drawDuplicator' (color4 0 1 0)+> drawCycle False = drawDuplicator' (color4 0.5 1 0.5)++> drawDuplicator' labelColor label color font = do+>   color unitcolor >   GL.renderPrimitive GL.LineStrip (do >     GL.vertex (vertex3 (-1.5) 1 0) >     GL.vertex (vertex3 0 (-1) 0)@@ -674,36 +729,37 @@ >   GL.renderPrimitive GL.Lines (do >     GL.vertex (vertex3 0 0 0) >     GL.vertex (vertex3 0 (-1) 0))->   GL.color portcolor+>   color portcolor >   GL.renderPrimitive GL.Polygon (circle 0.2 0.2 10)->   GL.color unitcolor->   GL.translate (vector3 (-4 / unit) (1 - 8 / unit) 0)->   renderString label+>   color unitcolor+>   GL.translate (vector3 (-4 / unit') (1 - 8 / unit') 0)+>   color labelColor+>   renderString font label -> drawEraser label = do->   GL.color unitcolor+> drawEraser label color font = do+>   color unitcolor >   GL.renderPrimitive GL.Lines (do >     GL.vertex (vertex3 0 (-1.2) 0) >     GL.vertex (vertex3 0 (-2) 0)) >   GL.renderPrimitive GL.LineStrip (circle 1.2 1.2 20) >   GL.renderPrimitive GL.LineStrip (circle 0.8 0.8 20) >   GL.translate (vector3 0 (-1.2) 0)->   GL.color portcolor+>   color portcolor >   GL.renderPrimitive GL.Polygon (circle 0.2 0.2 10)->   GL.color unitcolor->   GL.translate (vector3 (- fromIntegral (length label * 4) / unit) (1.2 - 8 / unit) 0)->   renderString label+>   color unitcolor+>   GL.translate (vector3 (- fromIntegral (length label * 4) / unit') (1.2 - 8 / unit') 0)+>   renderString font label -> drawTwoPin label = do->   GL.color unitcolor+> drawTwoPin label color font = do+>   color unitcolor >   GL.renderPrimitive GL.LineStrip (circle 1.5 1.5 20) >   GL.translate (vector3 0 1.5 0) >   GL.renderPrimitive GL.Lines (do >      GL.vertex (vertex3 0 0 0) >      GL.vertex (vertex3 0 0.5 0))->   GL.color portcolor+>   color portcolor >   GL.renderPrimitive GL.Polygon (circle 0.2 0.2 10)->   GL.color unitcolor+>   color unitcolor >   GL.translate (vector3 0 (-3) 0) >   GL.renderPrimitive GL.Lines (do >      GL.vertex (vertex3 0 0 0)@@ -712,20 +768,39 @@ >   GL.renderPrimitive GL.Lines (do >      GL.vertex (vertex3 0 0 0) >      GL.vertex (vertex3 0.5 0 0))->   GL.color textcolor->   GL.translate (vector3 (- fromIntegral (length label * 4) / unit) (1.5 - 8 / unit) 0)->   renderString label+>   color textcolor+>   GL.translate (vector3 (- fromIntegral (length label * 4) / unit') (1.5 - 8 / unit') 0)+>   renderString font label -> drawSingle label = do->   GL.color unitcolor+> drawSingle label color font = do+>   color unitcolor >   GL.renderPrimitive GL.LineStrip (circle 1.5 1.5 20) >   GL.translate (vector3 0 1.5 0) >   GL.renderPrimitive GL.Lines (do >      GL.vertex (vertex3 0 0 0) >      GL.vertex (vertex3 0 0.5 0))->   GL.color portcolor+>   color portcolor >   GL.renderPrimitive GL.Polygon (circle 0.2 0.2 10)->   GL.color textcolor->   GL.translate (vector3 (- fromIntegral (length label * 4) / unit) (- 1.5 - 8 / unit) 0)->   renderString label+>   color textcolor+>   GL.translate (vector3 (- fromIntegral (length label * 4) / unit') (- 1.5 - 8 / unit') 0)+>   renderString font label +> polyline = GL.renderPrimitive GL.LineStrip . UV.mapM_ draw+>   where+>     draw (x, y) = GL.vertex (vertex3 (realToFrac x) (realToFrac y) 0)+> +> polyBezier [] = UV.empty+> polyBezier ps = UV.map (bezier ps) segs+>   where+>     size = wirePoints - 1 -- floor (lineLength ps / margin * 10)+>     segs = UV.generate (size + 1) $ \i -> if i < size then fromIntegral i / fromIntegral size else 1+>     lineLength ((x1,y1):(x2,y2):ps) = +>       let dx = x2 - x1+>           dy = y2 - y1+>       in sqrt (dx * dx + dy * dy) + lineLength ((x2,y2):ps)+>     lineLength _ = 0+>+> bezier [(x1,y1)] t = (x1, y1)+> bezier [(x1,y1),(x2,y2)] t = (x1 + ((x2 - x1) * t), +>                               y1 + ((y2 - y1) * t))+> bezier ps t = bezier (map (\ (p, q) -> bezier [p,q] t) (zip ps (tail ps))) t
+ src/Grid.lhs view
@@ -0,0 +1,143 @@+Grid Layout+====++We implement a simple and robust layout algorithm for drawing interaction nets.+We guarantee a minimum distance between any two nodes, and dynamic addition and+deletion of nodes should bring minimal changes to existing layout. All nodes+and lines are aligned to grids.++Layout computation is encapsulated in a monad transformer, and thus is easy+to compose with other parts of the program.++> {-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, TypeSynonymInstances, +>              StandaloneDeriving, GeneralizedNewtypeDeriving, TupleSections #-}+> module Grid +>   ( GridT+>   , GridMap+>   , Pos+>   , Radius+>   , evalGridT+>   , evalGridT_+>   , getGrid+>   , setGrid+>   , modifyGrid+>   , clearGrid+>   , getNodes+>   , lookupNode+>   , locateNode+>   , modifyNodeVal+>   , modifyNodePos+>   , newNode+>   , removeNode+>   ) where++> import qualified Data.IntMap as M+> import Data.Complex+> import Control.Arrow (first, second, (***))+> import Control.Monad.State +> import Data.List (sortBy)++> type Uid  = M.Key+> type Radius = Float+> type Pos  = (Float, Float)+> type GridMap v = M.IntMap ((Pos, Radius), v)++> data S a = S { spacing :: Float, grid :: a }+> +> newtype GridT v m a +>   = GridT (StateT (S (GridMap v)) m a)+>   deriving (Functor, Monad, MonadTrans) +>+> deriving instance Monad m => MonadState (S (GridMap v)) (GridT v m)+> deriving instance MonadIO m => MonadIO (GridT v m)++> clearGrid :: Monad m => GridT v m ()+> clearGrid = modify $ \s -> s { grid = M.empty }++> getGrid :: Monad m => GridT v m (GridMap v)+> getGrid = get >>= return . grid+> modifyGrid f  = modify $ \s -> s { grid = f (grid s) }+> modifyGrid' f = modify $ \s -> s { grid = f (spacing s) (grid s) }+> setGrid :: Monad m => GridMap v -> GridT v m ()+> setGrid = modifyGrid . const++> evalGridT :: Monad m => GridT v m a -> Float -> GridMap v -> m a+> evalGridT (GridT m) space grid = evalStateT m $ S space grid++> evalGridT_ :: Monad m => GridT v m a -> Float -> m a+> evalGridT_ m space = evalGridT m space M.empty ++> getNodes :: Monad m => GridT v m [(Uid, ((Pos, Radius), v))]+> getNodes = getGrid >>= return . M.toList ++> lookupNode :: Monad m => Uid -> GridT v m (Maybe ((Pos, Radius), v))+> lookupNode uid = getGrid >>= return . M.lookup uid ++> locateNode :: Monad m => Pos -> GridT v m [Uid]+> locateNode (x0, y0) = getGrid >>= return . M.keys . M.filter (inside . fst)+>   where +>     inside ((x, y), r) = sqrt (dx * dx + dy * dy) < r+>       where +>         dx = x - x0 +>         dy = y - y0++> modifyNodeVal :: Monad m => Uid -> (v -> v) -> GridT v m ()+> modifyNodeVal uid f = modifyGrid (M.alter (fmap (second f)) uid)++> modifyNodePos :: Monad m => Uid -> ((Pos, Radius) -> (Pos, Radius)) -> GridT v m Bool+> modifyNodePos uid f = do+>   modifyGrid (M.alter (fmap (first f)) uid)+>   m <- getGrid+>   maybe (return False) (\n -> modifyGrid' (`adjust` (fst n)) >> return True)+>         (M.lookup uid m)+>   where+>     adjust spacing ((x0, y0), r0) = +>       M.fromList . map (fromPolar x0 y0) . +>       balanceList spacing . sortByDistance . +>       map (toPolar x0 y0) . M.toList ++> newNode :: Monad m => Uid -> Pos -> Radius -> v -> GridT v m ()+> newNode uid (x0, y0) rad v = modifyGrid' insert+>   where+>     newNode = (uid, (((0, 0), rad), v))+>     insert spacing = M.fromList . map (fromPolar x0 y0) . +>       foldl (balance spacing) [newNode] . sortByDistance . +>       map (toPolar x0 y0) . M.toList ++> removeNode :: Monad m => Uid -> GridT v m Bool+> removeNode uid = getGrid >>=+>   maybe (return False) (\_ -> modifyGrid (M.delete uid) >> return True) +>   -- maybe (return False) (\n -> modifyGrid' (`delete` (fst n)) >> return True) +>   . M.lookup uid +>   where+>     delete spacing ((x0, y0), r0) = M.fromList . map (fromPolar x0 y0) . +>       balanceList spacing . tail . sortByDistance .  +>       map (shorten . toPolar x0 y0) . M.toList+>       where +>         shorten = modPos $ \d -> d - r0 - spacing / 2++> modPos = second . first . first . first++> balanceList spacing (x:xs) = foldl (balance spacing) [x] xs+> balanceList spacing [] = []++> balance spacing l p = foldl improve p l : l+>   where +>     improve u@(i, (((r, a), r0), _)) v@(_, (((r', a'), r1), _)) = +>       if d > minD then u else modPos (const r'') u+>       where +>         d = r' * sin (a' - a)+>         s = sqrt (minD * minD - d * d)+>         t = r' * cos (a' - a)+>         st = s + t +>         r'' = if st > r then st else r+>         minD = spacing + r0 + r1+>+> sortByDistance = sortBy $ \u v -> compare (p u) (p v)+>   where p = fst . fst . fst . snd++> toPolar x0 y0 = second $ first $ first (polar . toC (x0, y0))+> toC (x0, y0) (x, y) = (x - x0) :+ (y - y0)++> fromPolar x0 y0 = second $ first $ first (fromC (x0, y0) . uncurry mkPolar)+> fromC (x0, y0) c = (x0 + realPart c, y0 + imagPart c)
src/INet.lhs view
@@ -1,19 +1,26 @@ Interactive Net for Lambdascope implmenetation +> {-# LANGUAGE TupleSections #-} > module INet where  > import Diagram -> import Data.IntMap as IntMap hiding (filter, map)-> import Data.Maybe (fromJust)+> import Data.IntMap as IntMap hiding (filter, map, foldl, foldr)+> import Data.Monoid (Monoid, mappend, mconcat, mempty)+> import Data.Maybe (fromJust, fromMaybe)+> import Data.List (mapAccumR) > import Control.Monad.State+> import Control.Arrow (first)+> import Debug.Trace  Interactive Net is defined to be a table, that maps node IDs to a triple that contains the node type, a list of ports (indexed by the list index), and value.  > type INetV = (NodeType, [(NodeID, Int)], Maybe Int)-> type INet = IntMap INetV+> type INetMap = IntMap INetV+> type INet = (INetMap, Int) -- map of ID to INetV, and maxID+> emptyINet = (IntMap.empty, 0) :: INet  A port is a pair of the other end's NodeID and port index. @@ -25,17 +32,17 @@ >               | Abstractor  >               | Delimiter  >               | Delimiter'                              -- flip of Delimiter->               | Duplicator->               | SuperDup                                -- dup everything+>               | Duplicator DupKind >               | Eraser >               | Dummy                                   -- dummy for self-loop >               | Initiator                               -- never destroyed->               | Constructor String                      -- data constructor+>               | Constructor String                      -- tuple constructor >               | TwoPin Int String | TwoPin' Int String  -- for meta function >               | Single String | Single' String          -- for meta value- >   deriving (Eq, Show) +> data DupKind = Dup | Cycle Bool | Sup Bool deriving (Eq, Show) -- Normal, Recursive, Super+ > findInitiator [] = Nothing > findInitiator (a@(i, (Initiator, [(b, u)], Nothing)):as) = Just a > findInitiator (a:as) = findInitiator as@@ -43,13 +50,13 @@  netToDiagram :: Net -> Diagram  > netToDiagram net = ->   let list = toList net->       Just (init, _) = findInitiator list+>   let list = toList $ fst net+>       adjust = maybe id (\(init, _) -> (init:).filter(/=init)) $ findInitiator list >       atoms = map toAtom list >       d = fromList (map (\a -> (atomID a, a)) atoms) >       heads = map (d!) $ foldl (\h i ->  >                   if any (\j -> reachable net [] [j] i) h then h else i : h) [] $->                 ((init:) . filter (/=init)) $ map fst list+>                 adjust $ map fst list >    in Diagram heads d >   where >     toAtom (i, (t, p, v)) = @@ -62,8 +69,7 @@ >             Applicator' -> ("@", drawAbstractor) >             Abstractor -> ("\x80", drawAbstractor) >             Delimiter -> (maybe "" show (v::Maybe Int), drawDelimiter)->             Duplicator -> (maybe "" show (v::Maybe Int), drawDuplicator)->             SuperDup -> ("*", drawDuplicator)+>             Duplicator k -> drawDup (maybe "" show (v::Maybe Int)) k >             Eraser -> ("", drawEraser) >             Initiator -> ("I", drawEraser) >             Delimiter' -> ("S", drawTwoPin)@@ -72,16 +78,20 @@ >             TwoPin' _ l -> (l, drawTwoPin) >             Single l -> (l, drawSingle) >             Single' l -> (l, drawSingle)+>         drawDup l Dup         = (l, drawDuplicator)+>         drawDup l (Cycle k)    = (l, drawCycle k)+>         drawDup l (Sup False) = (" ", drawDuplicator)+>         drawDup l (Sup True)  = ("*", drawDuplicator)  >     toPort a t (m, (j, n)) =  >       let (d, p) = pos t m->           b = toAtom (j, (net ! j))+>           b = toAtom (j, (fst net ! j)) >       in Port a (atomPorts b !! n) d p  >     reachable :: INet -> [Int] -> [Int] -> Int -> Bool >     reachable d visited [] i = False >     reachable d visited (x:xs) i = ->       let (_, ps, _) = (d ! x)+>       let (_, ps, _) = (fst d ! x) >           js = map fst ps >           ks = filter (flip notElem visited) js >        in (x == i) || reachable d (x : visited) (xs ++ ks) i@@ -95,10 +105,9 @@ >     pos Abstractor 2 = (E, (2, 0)) >     pos Delimiter 0 = (S, (0, -2)) >     pos Delimiter 1 = (N, (0, 2))->     pos Duplicator 0 = (S, (0, -2))->     pos Duplicator 1 = (N, (1, 2))->     pos Duplicator 2 = (N, (-1, 2))->     pos SuperDup i = pos Duplicator i+>     pos (Duplicator _) 0 = (S, (0, -2))+>     pos (Duplicator _) 1 = (N, (1, 2))+>     pos (Duplicator _) 2 = (N, (-1, 2)) >     pos Eraser 0 = (S, (0, -2)) >     pos Initiator 0 = (S, (0, -2)) >     pos Delimiter' 0 = (N, (0, 2))@@ -109,136 +118,244 @@ >     pos (Single _) 0 = (N, (0, 2)) >     pos (Single' _) 0 = (N, (0, 2)) -> type LocalRule = INet -> (Int, INetV) -> (Int, INetV) -> Maybe INet+> type LocalRule = (Int, INetV) -> (Int, INetV) -> INet -> Maybe INet+> type LocalRuleM m = (Int, INetV) -> (Int, INetV) -> INet -> m (Maybe INet) > type Rule = INet -> (INet, Int)+> type RuleM m = INet -> m (INet, Int)  Make a local rule global by applying it once everywhere. -> applyRule :: LocalRule -> Rule-> applyRule rule net = applyRule' 0 rule (keys net) net+> ruleM :: Monad m => LocalRule -> LocalRuleM m+> ruleM = (((return .) .) .) -> applyRule' :: Int -> LocalRule -> [Int] -> Rule-> applyRule' num rule [] net = (net, num)-> applyRule' num rule (i:rs) net = ->   let a@(at, ap, av) = net ! i+> applyRule :: Monad m => LocalRuleM m -> RuleM m+> applyRule rule net = applyRule' 0 rule (keys $ fst net) net++> applyRule' :: Monad m => Int -> LocalRuleM m -> [Int] -> RuleM m+> applyRule' num rule [] net = return (net, num)+> applyRule' num rule (i:rs) net = do+>   let a@(at, ap, av) = fst net ! i >       (j, n) = head ap->       b@(bt, bp, bv) = net ! j->       r = rule net (i, a) (j, b)->   in if member i net ->     then maybe (applyRule' num rule rs net) (applyRule' (num + 1) rule (filter (j/=) rs)) r+>       b@(bt, bp, bv) = fst net ! j+>   net' <- rule (i, a) (j, b) net+>   let rs' = filter (`member` (fst $ fromJust net')) rs +>   if member i (fst net)+>     then maybe (applyRule' num rule rs net) (applyRule' (num + 1) rule (filter (j/=) rs')) net' >     else applyRule' num rule rs net  repeatedly apply a rule until it is no longer applicable. +> repeatRule :: Monad m => RuleM m -> RuleM m > repeatRule = repeatRule' 0  -> repeatRule' :: Int -> Rule -> Rule-> repeatRule' sum rule net = ->   let (net', num) = rule net->   in if num == 0 ->     then (net, sum)+> repeatRule' :: Monad m => Int -> RuleM m -> RuleM m+> repeatRule' sum rule net = do+>   (net', num) <- rule net+>   if num == 0 +>     then return (net, sum) >     else repeatRule' (sum + num) rule net'  Apply a local rule at the outermost position. actually beta and meta can be performed simultaneously) -> outermost :: LocalRule -> Rule-> outermost rule net =->   case findInitiator $ toList net of+> outermost :: Monad m => LocalRuleM m -> RuleM m+> outermost rule net@(netMap, _) =+>   case findInitiator $ toList netMap of >     Just (i, _) -> outermost' [] [i]->     Nothing -> (net, 0)+>     Nothing -> return (net, 0) >   where ->     outermost' _ [] = (net, 0)+>     outermost' _ [] = return (net, 0) >     outermost' visited (i:is) =->       let a@(at, ((j, _):ps), _) = net ! i      ->           b = net ! j+>       let a@(at, ((j, _):ps), _) = netMap ! i      +>           b = netMap ! j >       in if elem i visited->         then (net, 0)->         else maybe (outermost' (i:visited) (j:is)) (\net -> (net, 1)) $ rule net (i, a) (j, b)+>         then return (net, 0)+>         else rule (i, a) (j, b) net >>= +>              maybe (outermost' (i:visited) (j:is)) (\net -> return (net, 1)) +Similar to outermost, but it looks under lambda.++> outerReducible :: Monad m => LocalRuleM m -> RuleM m+> outerReducible rule net@(netMap, _) = reduce $ zip all (tail all)+>   where +>     all = traverse (\(i, u) a@(t, _, _) -> +>                      case (t, u) of +>                         (Duplicator (Cycle _), 1) -> []+>                         _ -> [(i, u, a)]) netMap+>     reduce [] = return (net, 0)+>     reduce (((i, _, a), (j, u, b@(_, bp, _))) : xs) = +>       if u == 0 && i == fst (bp!!u) +>         then rule (i, a) (j, b) net >>= maybe (reduce xs) (return . (,1))+>         else reduce xs+ Compose two local rules together, try the first one, if it succeeds, just return; otherwise, try the second one.  > infixr 5 ->--> (->-) :: LocalRule -> LocalRule -> LocalRule-> (->-) r1 r2 net a b = maybe (r2 net a b) Just $ r1 net a b+> (->-) :: Monad m => LocalRuleM m -> LocalRuleM m -> LocalRuleM m+> (->-) r1 r2 a b net = do+>   r1 a b net >>= maybe (r2 a b net) (return . Just)  > infixr 5 +>+-> (+>+) :: Rule -> Rule -> Rule-> (+>+) r1 r2 net = ->   let r@(net', n) = r2 net->   in if n == 0 then r1 net else r+> (+>+) :: Monad m => RuleM m -> RuleM m -> RuleM m+> (+>+) r1 r2 net = do+>   r@(net', n) <- r1 net+>   if n == 0 then r2 net else return r +> infixr 5 +&++> (+&+) :: Monad m => RuleM m -> RuleM m -> RuleM m+> (+&+) r1 r2 net = do+>   r@(net', _) <- r1 net+>   r2 net' +++> at f j net = f (j, fst net!j) net+ The cross rules: annihilate and commute.  -> cross net (i, a@(at, ap, av)) (j, b@(bt, bp, bv)) = +> cross :: LocalRule+> cross x@(i, a@(at, ap, av)) y@(j, b@(bt, bp, bv)) =  >   if head bp == (i, 0)                          -- princple ports meet?->     then if at == bt && av == bv                -- same type and value?->       then Just $ annihilate net (i, a) (j, b)+>     then if match at bt && av == bv             -- same type and value?+>       then Just . annihilate (i, a) (j, b)  >       else case (at, bt) of                     -- different type->         (Applicator, Abstractor) -> Nothing     -- leave the beta rule out->         (Abstractor, Applicator) -> Nothing->         (Constructor _, TwoPin _ _) -> Nothing  -- leave the meta rule out->         (TwoPin _ _, Constructor _) -> Nothing->         (Single _, TwoPin _ _) -> Nothing       -- FIXME: never cross single, but cross single'->         (TwoPin _ _, Single _) -> Nothing->         (Initiator, Delimiter) -> Just $ commute net (i, a) (j, b)->         (Delimiter, Initiator) -> Just $ commute net (i, a) (j, b)->         (Initiator, _) -> Nothing->         (_, Initiator) -> Nothing->         _ -> Just $ commute net (i, a) (j, b)+>         (Applicator, Abstractor) -> const Nothing     -- leave the beta rule out+>         (Abstractor, Applicator) -> const Nothing+>         (Constructor _, TwoPin _ _) -> const Nothing  -- leave the meta rule out+>         (TwoPin _ _, Constructor _) -> const Nothing+>         (Single _, TwoPin _ _) -> const Nothing       -- FIXME: never cross single, but cross single'+>         (TwoPin _ _, Single _) -> const Nothing+>--         (Initiator, Delimiter) -> Just . commute (i, a) (j, b) +>--         (Delimiter, Initiator) -> Just . commute (j, b) (i, a)+>         (Initiator, _) -> const Nothing+>         (_, Initiator) -> const Nothing+>         (Duplicator (Cycle False), _) -> const Nothing+>         (_, Duplicator (Cycle False)) -> const Nothing+>         (Constructor _, Duplicator k) -> cross y x+>         (Duplicator k, Constructor _) -> fmap (commute x y) . isSelector (tail ap)+> {-+>         (Duplicator (Cycle _), Duplicator Dup) -> Just . +>           (if bv == Just 0 then either (crossCycle x y) (commute x y) . +>                                        reachable (ap!!1) (j, 1)+>                            else commute x y)+> -}+>         _ -> Just . commute x y >     else case (at, bt, snd (head ap)) of->         (SuperDup, Applicator, 2) -> Just $ superDup net (i, a) (j, b)->         (SuperDup, TwoPin _ _, 1) -> Just $ superDup net (i, a) (j, b)->         (SuperDup, Delimiter,  1) -> Just $ superDup net (i, a) (j, b)->         _ -> Nothing+>         (Duplicator (Sup True), Applicator, 2) -> Just . superDup (i, a) (j, b) +>         (Duplicator (Sup True), TwoPin _ _, 1) -> Just . superDup (i, a) (j, b) +>         (Duplicator (Sup True), Delimiter,  1) -> Just . superDup (i, a) (j, b) +>         _ -> const Nothing+>   where+>     match (Duplicator _) (Duplicator _) = True+>     match t t' = t == t'  +>     reachable p q m@(netMap, _) = check p+>       where check p | p == q    = Left m+>                     | otherwise = case netMap ! (fst p) of+>                                     (_, [p0, p1], _) -> check (if snd p == 0 then p1 else p0)+>                                     _ -> Right m+>     isSelector xs m@(netMap, _) = check xs+>       where+>         check [] = Nothing+>         check ((k, u):xs) = +>           let (t, p, _) = netMap ! k +>               more = check (xs ++ tail p)+>               next = check xs+>           in if u /= 0 then next+>              else case t of+>                Delimiter  -> more+>                Duplicator _ -> more+>                TwoPin _ s -> if s == "fst" || s == "snd" then Just m+>                                else next+>                _ -> next  cross from rear -> superDup net (i, a) (j, b) = ->   let Just net' = upsideDown net (j, b)->       net'' = commute net' (i, a) (j, net'!j)+> superDup (i, a) (j, b) net = +>   let Just net' = upsideDown (j, b) net+>       net'' = at (commute (i, a)) j net' >       ks = map fst $ filter (\ (i, (ct, _, _)) -> case ct of >              Applicator'  -> True >              TwoPin' _ _ -> True >              Delimiter' -> True->              _  -> False) (toList net'')->   in foldr (\k net -> fromJust $ upsideDown net (k, net!k)) net'' ks+>              _  -> False) (toList $ fst net'')+>   in foldr (\k net -> fromJust $ upsideDown (k, fst net!k) net) net'' ks +A special case where a Cycle duplicator meets a normal duplicator of level 0. +> crossCycle (i, a@(at, ap, av)) (j, b@(bt, bp, bv)) = +>   let (x, p) = ap !! 2 +>       (y, q) = bp !! 2+>   in first (adjust (replaceP p (j, 0)) x . +>             adjust (replaceP q (i, 0)) y . +>             adjust (\_ -> (at, [(y, q), ap!!1, (j, 2)], av)) i .+>             adjust (\_ -> (bt, [(x, p), bp!!1, (i, 2)], bv)) j)+>       + The modified cross rule, only moves delimiter or annihilates. -> cross' net (i, a@(at, ap, av)) (j, b@(bt, bp, bv)) = +> cross' (i, a@(at, ap, av)) (j, b@(bt, bp, bv)) =  >   if head bp == (i, 0)                          -- princple ports meet? >     then if at == bt && av == bv                -- same type and value?->       then Just $ annihilate net (i, a) (j, b)+>       then Just . annihilate (i, a) (j, b)  >       else case (at, bt) of                     -- different type->         (Delimiter, _) -> Just $ commute net (i, a) (j, b)->         (_, Delimiter) -> Just $ commute net (i, a) (j, b)->         _ -> Nothing->     else Nothing+>         (Delimiter, _) -> Just . commute (i, a) (j, b) +>         (_, Delimiter) -> Just . commute (i, a) (j, b) +>         _ -> const Nothing+>     else const Nothing -> annihilate net (i, a@(at, ap, av)) (j, b@(bt, bp, bv)) = +> {-++Sharing recovery by moving identical nodes from the rear of a duplicator+to the front.++> share :: LocalRule+> share (i, a@(at, ap, av)) (j, b@(bt, bp, bv)) (netMap, maxID) = +>   let (_, p) = head ap+>       q = 3 - p+>       (k, kp) = bp !! q+>       (ct, cp, cv) = netMap ! k+>       (d, dp) = ap !! 1+>       (e, ep) = cp !! 1+>       (f, fp) = bp !! 0+>       n = maxID + 1+>   in if bt == Duplicator && length ap == 2 && p > 0 && kp == 0 && ct /= Delimiter && ct == at && cv == av+>     then Just ((insert n (at, [bp!!0, (j, 0)], av) .+>         adjust (replaceP fp (n, 0)) f .+>         adjust (replaceP ep (j, q)) e .+>         adjust (replaceP dp (j, p)) d .+>         adjust (replaceP 0 (n,1) . replaceP p (ap!!1) . replaceP q (cp!!1)) j .+>         delete i . delete k) netMap, n)+>     else if bt == Duplicator && at != Delimiter && length ap == 2 && p > 0 && kp == 0 +>       then Just ((insert n     (at, [bp!!q, (j, q)], av) .+>                   insert (n+1) (at, [bp!!0, (j, 0)], av) .+>                   adjust (replaceP fp (n+1, 0)) f .+>                   adjust (replaceP kp (n, 0)) k .+>                   adjust (replaceP dp (j, p)) d .+>                   adjust (replaceP 0 (n+1,1) . replaceP p (ap!!1) . replaceP q (n,1)) j .+>                   delete i) netMap, n + 1)+>       else Nothing+> -}++> annihilate (i, a@(at, ap, av)) (j, b@(bt, bp, bv)) =  >   let pairup = zip (tail ap) (tail bp) >       pair' = fixLoop pairup pairup->   in foldr (\ ((c, u), (d, v)) ->->        adjust (\ (ct, cp, cv) -> (ct, replace u (d, v) cp, cv)) c .->        adjust (\ (dt, dp, dv) -> (dt, replace v (c, u) dp, dv)) d)->        ((delete i . delete j) net) pair'+>   in first $ flip (foldr zipup) pair' . delete i . delete j >   where >     fixLoop _ [] = [] >     fixLoop pairs (x@(p@(c, u), q@(d, v)) : xs) =  >       let p' = if c == i then snd (pairs!!(u-1)) else p >           q' = if d == j then fst (pairs!!(v-1)) else q >       in (p',q') : fixLoop pairs xs+>     zipup ((c, u), (d, v)) =+>       adjust (replaceP u (d, v)) c .+>       adjust (replaceP v (c, u)) d  The commute rule should also work for the Eraser so that the other thing annihilates. -> commute net (i, a@(at, ap, av)) (j, b@(bt, bp, bv)) = ->   let maxID = maximum (keys net)->       bs = take (length ap - 1) [(maxID + 1) .. ] ->       as = take (length bp - 1) [(maxID + 1 + length bs) .. ]+> commute (i, a@(at, ap, av)) (j, b@(bt, bp, bv)) net@(netMap, maxID) = +>   let bs = take (length ap - 1) $ [(maxID + 1) .. ] +>       as = if at == Initiator then [j] else take (length bp - 1) [(maxID + 1 + length bs) .. ] >       bs_ = map (\ (k, (c, u)) -> (bt',  >         (if c == i then (bs!!(u-1), 0)  >            else if c == j then (as!!(u-1), 0) else (c, u))@@ -246,158 +363,299 @@ >       as_ = map (\ (k, (c, u)) -> (at',  >         (if c == j then (as!!(u-1), 0)  >            else if c == i then (bs!!(u-1), 0) else (c, u)) ->          : zip bs (repeat k), av'))->               (tail (zip [0..] bp))+>          : zip bs (repeat k), av')) (tail (zip [0..] bp)) >       av' = maybe Nothing (\v -> Just $ >               if bt == Abstractor ||  >                  (bt == Delimiter && v >= fromJust bv) then v + 1 else v) av >       bv' = maybe Nothing (\v -> Just $ >               if at == Abstractor ||  >                  (at == Delimiter && v >= fromJust av) then v + 1 else v) bv->       at' = if at == Duplicator && av' == Nothing && bt == Abstractor then SuperDup else at->       bt' = if bt == Duplicator && bv' == Nothing && at == Abstractor then SuperDup else bt->   in (flip (foldr (\ (k, (c, u)) -> adjust (\ (ct, cp, cv) ->->         (ct, replace u (bs !! k, 0) cp, cv)) c)) (zip [0..] (tail ap)) .->       flip (foldr (\ (k, (c, u)) -> adjust (\ (ct, cp, cv) ->->         (ct, replace u (as !! k, 0) cp, cv)) c)) (zip [0..] (tail bp)) .+>       at' = case (at, bt) of+>               (Duplicator (Sup False), Abstractor) -> Duplicator (Sup True)+>               (Duplicator (Cycle _), _) -> Duplicator (Cycle False) -- disable Cycle duplicator after commuting+>               _ -> at+>       bt' = case (bt, at) of +>               (Duplicator (Sup False), Abstractor) -> Duplicator (Sup True)+>               (Duplicator (Cycle _), _) -> Duplicator (Cycle False) -- disable Cycle duplicator after commuting+>               _ -> bt+>   in ((flip (foldr (\ (k, (c, u)) -> adjust (replaceP u (bs !! k, 0)) c)) (zip [0..] (tail ap)) .+>        flip (foldr (\ (k, (c, u)) -> adjust (replaceP u (as !! k, 0)) c)) (zip [0..] (tail bp)) . >       flip (foldr (uncurry insert)) (zip bs bs_) . >       flip (foldr (uncurry insert)) (zip as as_) .->       delete i . delete j) net+>       delete i . delete j) netMap, maximum $ maxID : (bs ++ as)) +> resetCycle :: LocalRule+> resetCycle (i, a@(Duplicator (Cycle False), p, v)) _ = +>   Just . first (adjust (\_ -> (Duplicator (Cycle True), p, v)) i)+> resetCycle _ (i, a@(Duplicator (Cycle False), p, v)) = +>   Just . first (adjust (\_ -> (Duplicator (Cycle True), p, v)) i)+> resetCycle _ _ = const Nothing++> {-++Fixed point operator BigY needs special treatment because we don't want to+unroll it more than once. The way to do this is to check its value, if it+is Nothing, then we don't unroll, otherwise unroll and change the value+to Nothing.++> unrollBigY (i, (yt, [(_, u), (c, v)], yv)) (j, (bt, bp, bv)) =+>   maybe (const Nothing) (const unroll) yv+>   where+>     unroll :: INet -> Maybe INet+>     unroll net@(netMap, maxID) = +>       let (a, d) = (maxID + 1, maxID + 2)+>           a_ = (Applicator, [(d, 2), (i, 1), (c, v)], Nothing)+>           d_ = (Duplicator, [(j, u), (i, 0), (a, 0)], Just 0)+>           y  = (yt, [(d, 1), (a, 1)], Nothing)+>           b' = (bt, replace u (d, 0) bp, bv)+>       in Just ((adjust (replaceP v (a, 2)) c . +>                 adjust (\_ -> b') j . adjust (\_ -> y) i .+>                 insert a a_ . insert d d_) netMap, maxID + 2)+> -}+ The erase rule erase anything it sees. -> erase net (i, a@(at, ap@((_, n):_), av)) (j, b@(bt, bp, bv)) =->   case (at, bt, head bp == (i, 0)) of+> erase (i, a@(at, ap@((_, n):_), av)) (j, b@(bt, bp, bv)) =+>   case (at, bt, bv, head bp == (i, 0)) of  Erase sharing is actually not strictly needed as they are not active pairs, though it helps to speed up garbage collection and the reduction of degerated  (self-looping) components. ->     (Eraser, Duplicator, False) -> eraseSharing ->     (Eraser, SuperDup, False)   -> eraseSharing->     (Eraser, _, True) -> cross net (i, a) (j, b)   -- otherwise cross->     (_, Eraser, _) -> erase net (j, b) (i, a)->     _ -> Nothing+>     (Eraser, Duplicator k, Just 0, False) -> eraseSharing +>--   (Eraser, BigY _, False) -> eraseSharing +>--   (Eraser, SuperDup, False)   -> eraseSharing+>     (Eraser, _, _, True) -> eraseNode+>     -- (Eraser, _, False) -> auxAreErasers >=> eraseNode +>     _ -> const Nothing >   where+>     auxAreErasers :: INet -> Maybe INet+>     auxAreErasers net@(netMap, _) = if all auxIsEraser (tail bp) then Just net else Nothing+>       where auxIsEraser (n, _) = case netMap ! n of+>                                    (Eraser, _, _) -> True+>                                    _ -> False+>     eraseSharing :: INet -> Maybe INet >     eraseSharing = >       let (c, u) = bp !! (3 - n) >           (d, v) = bp !! 0->        in Just $ (delete i . delete j . ->            adjust (\ (ct, cp, cv) -> (ct, replace u (d, v) cp, cv)) c .->            adjust (\ (dt, dp, dv) -> (dt, replace v (c, u) dp, dv)) d) net+>        in Just . first (delete i . delete j . +>            adjust (replaceP u (d, v)) c .+>            adjust (replaceP v (c, u)) d) +>     eraseNode :: INet -> Maybe INet+>     eraseNode (netMap, maxID) =+>       let bp' = filter ((i/=) . fst) bp+>           ibp = zip [(maxID + 1) ..] bp'+>           ers = map (\(u, (n, v)) -> insert u (Eraser, [(n, v)], Nothing)) ibp+>           adjustments = map (\(u, (n, v)) -> +>                               adjust (replaceP v (u, 0)) n) ibp+>       in Just ((foldr (.) id ers . foldr (.) id adjustments . delete i . delete j) netMap, +>                maxID + length bp') -The prune rule removes all trees without the initiator. This is necessary for-garbage collecting self-looping components.- +The prune rule removes all nodes that are not "reachable" from the initiator. +This is necessary for garbage collecting self-looping components.++The traversal itself is guided by a stack as outlined in the lambdascope paper+section 6.2.1.++> data Dir = L | R deriving (Eq, Show)+> type Stack = (Int, [Level])+> data Level = Level Block [Level] deriving Eq+> type Block = (Int, [Dir])+> +> showBlock (i, d) = show i ++ concatMap show d+> showLevel (Level b l) = showBlock b ++ showLevels l+> showStack (i, l) = show i ++ "," ++ showLevels l+> showLevels [] = ""+> showLevels (x@(Level _ l):xs) = let s = showLevel x in (if length s == 1 then id else paren) s  ++ showLevels xs+>   where paren s = "(" ++ s ++ ")"++> -- FIXME: prune is actually incorrect if implemented using traverse! > prune :: Rule-> prune net =->   let Just (i, a) = findInitiator (toList net)->       visited = visit [] [i]->       net' = filterWithKey (\k _ -> elem k visited) net->   in (net', 0)+> prune net@(netMap, maxID) =+>   let +>       visited = traverse (\x@(i, u) (t, _, _) ->+>                       case (t, u) of+>                         (Duplicator (Cycle _), 1) -> []+>                         _ -> [i]) netMap+>       netMap' = length visited `seq` filterWithKey (\k _ -> elem k visited) netMap+>       net' = fixUp (netMap', maxID)+>   in (net', size netMap - size (fst net'))+>   where+>     fixUp' x = let x' = traceShow ("before fixup:", x) $ fixUp x+>                in traceShow ("after fixup", x') x'+>     fixUp (netMap, maxID) = foldWithKey adjust (IntMap.empty, maxID) netMap+>       where+>         adjust i (t, p, v) (m, maxID) = +>           let ((m', maxID'), p') = mapAccumR addEraser (m, maxID) $ zip [0..] p+>           in (insert i (t, p', v) m', maxID')+>           where+>             addEraser acc@(m, maxID) (q, x@(j, u)) = +>               if member j netMap then (acc, x)+>                 else let n = maxID + 1+>                      in ((insert n (Eraser, [(i, q)], Nothing) m, n), (n, 0))+++A generic traversal guided by the stack:++1. It reaches all node that it is reachable from Initiator.++2. It does not avoid circles (introduced by Cycle duplicator), +   so potentially it may produce result that is infinite. ++> traverse :: Monoid m => (NetPort -> INetV -> m) -> INetMap -> m+> traverse f netMap = -- traceShow "======" $ +>   visit (i, 1) (0, [Level (0,[]) []]) >   where ->     visit visited [] = visited->     visit visited (i:is) = ->       let a@(_, p, _) = net ! i->           bs = filter (flip notElem visited) (map fst p)->       in visit (i:visited) (is ++ bs)- +>     Just (i, _) = findInitiator (toList netMap)+>     visit x@(n, u) s = +>       let a@(at, p, av) = netMap ! n+>           defaultVisit = if u == 0 +>                            then mconcat $ map (`visit` s) $ tail p+>                            else visit (p!!0) s+>           -- defaultVisit = error "not handled"+>           visited = case --traceShow (showStack s, (at, u, av)) $ +>                          (at, u, av, s) of+>             (Initiator, _, _, _)  -> visit (p!!0) s+>             (Applicator, 2, _, _) -> visit (p!!0) s `mappend` visit (p!!1) s+>             (Abstractor, 0, _, (i, l)) -> visit (p!!1) (i+1, (Level (i+1,[]) []):l)+>             (Abstractor, _, _, _) -> mempty+>             (Delimiter, 0, Just 0, (i, l)) -> visit (p!!1) (i, (Level (0,[]) []):l)+>             (Delimiter, 1, Just 0, (i, k:l)) -> visit (p!!0) (i, l)+>             (Delimiter, 0, Just i, s) -> +>               let (i', l) = s+>                   Level b (k:l') = l !! (i - 1)+>                   s' = (i', take (i-1) l ++ [Level b l', k] ++ drop i l)+>               in visit (p!!1) s'+>             (Delimiter, 1, Just i, s) -> +>               let (i', l) = s+>                   Level b l' = l !! (i - 1)+>                   k = l !! i +>                   s' = (i', take (i-1) l ++ [Level b (k:l')] ++ drop (i+1) l)+>               in visit (p!!0) s'+>             (Duplicator _, 0, Just i, s) ->+>               let (i', l) = s+>                   Level b l' = l !! i+>               in case b of+>                 (j, R:d) -> visit (p!!1) (i', replace i (Level (j,d) l') l)+>                 (j, L:d) -> visit (p!!2) (i', replace i (Level (j,d) l') l)+>                 _ -> error "invalid" +>             (Duplicator t, q, Just i, s) ->+>               let (i', l) = s+>                   Level (j, d) l' = l !! i+>                   d' = (if q == 1 then R else L) : d+>               in visit (p!!0) (i', replace i (Level (j,d') l') l)+>             _ -> defaultVisit+>       in f x a `mappend` visited+ The disintegrate rule is never really used here, because we won't be able to recover the Abstractor afterwards. (FIXME: to handle self-loop) -> disintegrate net (i, a@(at, ap@[(b,l), (c,u), (d,v)], av)) = ->   let maxID = maximum (keys net)->       (d1, d2) = (maxID + 1, maxID + 2)+> disintegrate (i, a@(at, ap@[(b,l), (c,u), (d,v)], av)) net@(netMap, maxID) = +>   let (d1, d2) = (maxID + 1, maxID + 2) >       d1_ = (Delimiter, [(i,2), (c, u)], Just 0) >       d2_ = (Delimiter, [(i,1), (d, v)], Just 0)->    in (adjust (const  (Applicator, [(b, l), (d2, 0), (d1, 0)], Nothing)) i .->        adjust (\ (ct, cp, cv) -> (ct, replace u (d1, 1) cp, cv)) c .->        adjust (\ (dt, dp, dv) -> (dt, replace v (d2, 1) dp, dv)) d .->        insert d1 d1_ . insert d2 d2_) net+>    in ((adjust (const  (Applicator, [(b, l), (d2, 0), (d1, 0)], Nothing)) i .+>        adjust (replaceP u (d1, 1)) c .+>        adjust (replaceP v (d2, 1)) d .+>        insert d1 d1_ . insert d2 d2_) netMap, maxID + 2)  The beta rule aplies when Applicator meets Abstractor. -> beta net (i, a@(at, ap, _)) (j, b@(bt, bp, _)) =+> beta (i, a@(at, ap, _)) (j, b@(bt, bp, _)) = >   case (head bp == (i, 0), at, bt) of->     (True, Applicator, Abstractor) -> Just $ beta' net (i, a) (j, b)->     (True, Abstractor, Applicator) -> Just $ beta' net (j, b) (i, a)->     _ -> Nothing+>     (True, Applicator, Abstractor) -> Just . beta' (i, a) (j, b) +>     (True, Abstractor, Applicator) -> Just . beta' (j, b) (i, a) +>     _ -> const Nothing -> beta' net (i, a@(at, [_, (c,u), (d,v)], av))->           (j, b@(bt, [_, (e,x), (f,y)], bv)) = ->   let maxID = maximum (keys net)->       (d1, d2) = (maxID + 1, maxID + 2)+> beta' (i, a@(at, [_, (c,u), (d,v)], av))+>       (j, b@(bt, [_, (e,x), (f,y)], bv)) net@(netMap, maxID) = +>   let (d1, d2) = (maxID + 1, maxID + 2) >       d1_ = (Delimiter, [(d, v), if e == j then (d2, 1) else (e, x)], Just 0) >       d2_ = (Delimiter, [(c, u), if f == j then (d1, 1) else (f, y)], Just 0)->   in (adjust (\ (ct, cp, cv) -> (ct, replace u (d2, 0) cp, cv)) c .->       adjust (\ (dt, dp, dv) -> (dt, replace v (d1, 0) dp, dv)) d .->       adjust (\ (et, ep, ev) -> (et, replace x (d1, 1) ep, ev)) e .->       adjust (\ (ft, fp, fv) -> (ft, replace y (d2, 1) fp, fv)) f .->       insert d1 d1_ . insert d2 d2_ . delete i . delete j) net+>   in ((adjust (replaceP u (d2, 0)) c .+>        adjust (replaceP v (d1, 0)) d .+>        adjust (replaceP x (d1, 1)) e .+>        adjust (replaceP y (d2, 1)) f .+>        insert d1 d1_ . insert d2 d2_ . delete i . delete j) netMap, maxID + 2)  > replace :: Int -> a -> [a] -> [a] > replace _ _ [] = [] > replace 0 v (x:xs) = v : xs > replace i v (x:xs) = x : replace (i - 1) v xs +> replaceP i u (t, p, v) = (t, replace i u p, v)+ The following rules are for reading back -> unwind net a@(_, (Delimiter, _, _)) _ = Nothing-> unwind net a@(_, (Delimiter', _, _)) _ = Nothing-> unwind net a@(_, (TwoPin _ _, _, _)) _ = Nothing-> unwind net a@(_, (TwoPin' _ _, _, _)) _ = Nothing-> unwind net a@(_, (Applicator', _, _)) _ = Nothing-> unwind net (i, a@(Single l, p, Nothing)) _ = Just $->   adjust (\_ -> (Single' l, p, Nothing)) i net-> unwind net a b = upsideDown net a+> tryBoth :: ((Int, INetV) -> INet -> Maybe INet) -> LocalRule +> tryBoth rule a@(i,_) b@(j,_) net = +>   maybe (rule b net) (\net' -> Just $ fromMaybe net' $ rule (j,(fst net'!j)) net') $ rule a net -> upsideDown net (i, a@(at, _, _)) = +> unwind = tryBoth unwind'+> unwind' a@(_, (Delimiter, _, _)) net = Nothing+> unwind' a@(_, (Delimiter', _, _)) net = Nothing+> unwind' a@(_, (TwoPin _ _, _, _)) net = Nothing+> unwind' a@(_, (TwoPin' _ _, _, _)) net = Nothing+> unwind' a@(_, (Applicator', _, _)) net = Nothing+> unwind' (i, a@(Single l, p, Nothing)) net = Just $ first  +>   (adjust (\_ -> (Single' l, p, Nothing)) i) net+> unwind' a net = upsideDown a net++> upsideDown (i, a@(at, _, _)) net = >   case at of >     Applicator ->  >       let (_, [(b,u), (c,v), (d, w)], _) = a ->       in Just $+>       in Just $ first >         (adjust (const (Applicator', [(d, w), (b, u), (c, v)], Nothing)) i .->          adjust (\ (bt, bp, bv) -> (bt, replace u (i, 1) bp, bv)) b .->          adjust (\ (ct, cp, cv) -> (ct, replace v (i, 2) cp, cv)) c .->          adjust (\ (dt, dp, dv) -> (dt, replace w (i, 0) dp, dv)) d) net+>          adjust (replaceP u (i, 1)) b .+>          adjust (replaceP v (i, 2)) c .+>          adjust (replaceP w (i, 0)) d) net >     Applicator' ->  >       let (_, [(b,u), (c,v), (d, w)], _) = a ->       in Just $+>       in Just $ first >         (adjust (const (Applicator, [(c, v), (d, w), (b, u)], Nothing)) i .->          adjust (\ (bt, bp, bv) -> (bt, replace u (i, 2) bp, bv)) b .->          adjust (\ (ct, cp, cv) -> (ct, replace v (i, 0) cp, cv)) c .->          adjust (\ (dt, dp, dv) -> (dt, replace w (i, 1) dp, dv)) d) net+>          adjust (replaceP u (i, 2)) b .+>          adjust (replaceP v (i, 0)) c .+>          adjust (replaceP w (i, 1)) d) net >     TwoPin  arity l -> Just $ upsideDown' (TwoPin' arity l) a >     TwoPin' arity l -> Just $ upsideDown' (TwoPin arity l) a >     Delimiter  -> Just $ upsideDown' Delimiter' a >     Delimiter' -> Just $ upsideDown' Delimiter a >     _ -> Nothing >   where->     upsideDown' at' a@(_, [(b,u),(c,v)], av) = +>     upsideDown' at' a@(_, [(b,u),(c,v)], av) = first >       (adjust (const (at', [(c, v), (b, u)], av)) i .->        adjust (\ (bt, bp, bv) -> (bt, replace u (i, 1) bp, bv)) b .->        adjust (\ (ct, cp, cv) -> (ct, replace v (i, 0) cp, cv)) c) net+>        adjust (replaceP u (i, 1)) b .+>        adjust (replaceP v (i, 0)) c) net -> scope net (i, a@(Delimiter, [(b, u), (c, v)], Just av)) _ = upsideDown net (i, a)-> scope _ _ _ = Nothing+> scope = tryBoth scope'+> scope' (i, a@(TwoPin _ _, _, _)) = upsideDown (i, a)+> scope' (i, a@(Delimiter, _, _))  = upsideDown (i, a)+> scope' _ = const Nothing -> loopcut net (i, a@(Abstractor, [(b, u), (c, v), (d, w)], Nothing)) _ = ->   let maxID = maximum (keys net)->       (e, f) = (maxID + 1, maxID + 2)+> loopcut = tryBoth loopcut'+> loopcut' (i, a@(Abstractor, [(b, u), (c, v), (d, w)], Nothing)) (net, maxID) = +>   let (e, f) = (maxID + 1, maxID + 2) >       e_ = (Eraser, [(i, 2)], Nothing) >       f_ = (Eraser, [(d, w)], Nothing) >       (dt, _, _) = net ! d >    in if dt /= Eraser  >      then Just $->        (adjust (\ (at, ap, av) -> (at, replace 2 (e, 0) ap, Nothing)) i .+>       ((adjust (\ (at, ap, av) -> (at, replace 2 (e, 0) ap, Nothing)) i . >         adjust (\ (dt, dp, dv) -> (dt, replace w (f, 0) dp, dv)) d .->         insert e e_ . insert f f_) net+>         insert e e_ . insert f f_) net, maxID + 2) >      else Nothing-> loopcut _ _ _ = Nothing+> loopcut' _ _ = Nothing -> readback = applyRule cross . fst . applyRule loopcut . fst . ->   applyRule cross . fst . applyRule scope . fst . ->   applyRule cross . fst . applyRule unwind+> readback :: Monad m => RuleM m+> readback = +>   apply' unwind  >=> return . fst >=> +>   apply' cross   >=> return . fst >=>+>   apply' scope   >=> return . fst >=>+>   apply' cross   >=> return . fst >=>+>   apply' loopcut >=> return . fst >=>+>   apply' cross   +>  where apply' = applyRule . ruleM   The Node representation helps to compose INet directly from let expressions. @@ -423,8 +681,10 @@ >   return $ Node Abstractor i [abs, body, bind] Nothing > delimiter to from v = incS >>= \i ->  >   return $ Node Delimiter i [to, from] (Just v)+> cycle out right left v = incS >>= \i -> +>   return $ Node (Duplicator (Cycle True)) i [out, right, left] (Just v) > duplicator out right left v = incS >>= \i -> ->   return $ Node Duplicator i [out, right, left] (Just v)+>   return $ Node (Duplicator Dup) i [out, right, left] (Just v) > eraser out = incS >>= \i ->  >   return $ Node Eraser i [out] Nothing > initiator out = incS >>= \i -> @@ -448,9 +708,9 @@  Converting from Node representation to INet (a tree in this case). -> nodeToNet node = removeDummy $ visit [] empty [node]+> nodeToNet node = let net = removeDummy $ visit [] empty [node] in (net, maximum $ 0:(keys net)) >   where->     visit :: [Node] -> INet -> [Node] -> INet+>     -- visit :: [Node] -> INet -> [Node] -> INet >     visit visited net [] = net >     visit visited net (x:xs) =  >       if elem x visited@@ -465,15 +725,15 @@ >                            j = snd (filter ((x==) . fst) qs !! k) >                        in (i', j)) ps) >           in visit (x:visited) net' (xs ++ nodePorts x)->     setNet :: INet -> Node -> [(Int, Int)] -> INet+>     -- setNet :: INet -> Node -> [(Int, Int)] -> INet >     setNet net (Node t id _ v) p = insert id (t, p, v) net >     nth i n = length . takeWhile (\ (_, m) -> m < n) . filter (\ (j, _) -> j == i) >     removeDummy net =  >       foldr (\ (i, (t, p, v)) net -> case t of >         Dummy ->  >           let [(b, j), (c, k)] = p->           in (adjust (\ (bt, bp, bv) -> (bt, replace j (c, k) bp, bv)) b .->               adjust (\ (ct, cp, cv) -> (ct, replace k (b, j) cp, cv)) c .+>           in (adjust (replaceP j (c, k)) b .+>               adjust (replaceP k (b, j)) c . >               delete i) net >         _ -> net) net (toList net) 
src/Lambda.lhs view
@@ -11,12 +11,12 @@ >   pretty >   ) where -> import Diagram hiding (S)+> -- import Diagram hiding (S) > import INet  > import Control.Monad.Fix > import Data.IntMap hiding (map)-> import Prelude hiding (take, drop, head, tail)+> import Prelude hiding (take, drop, head, tail, cycle)  > data Term = Z | S Term | Abs Term | App Term Term  >           | Y Term                                -- fix point@@ -41,7 +41,7 @@ >       es <- erasers xs >   return $ e : es -> termToNet i Z ps = debug ("termToNet: Z " ++ show i) $ do+> termToNet i Z ps = do >   let inp = head ps >       out = head (drop i ps) >       mid = take (i - 1) (tail ps)@@ -49,7 +49,7 @@ >   es <- erasers mid >   return $ (a : es) ++ [a] -> termToNet i x@(S t) ps = debug ("termToNet: " ++ show x ++ " " ++ show i) $ do+> termToNet i x@(S t) ps = do >   let inp = head ps >       out = head (drop i ps) >       mid = take (i - 1) (tail ps)@@ -58,14 +58,14 @@ >       e <- eraser out >   return $ (d : tail n) ++ [e] -> termToNet i x@(Abs t) ps = debug ("termToNet: " ++ show x ++ " " ++ show i) $ do+> termToNet i x@(Abs t) ps = do >   let inp = head ps >   rec a <- abstractor inp (head n) (head (drop (i + 1) n)) >       n <- termToNet (i + 1) t ((a : tail ps) ++ [a]) >   let mid = take i (tail n) >   return $ a : mid -> termToNet i x@(App t1 t2) ps = debug ("termToNet: " ++ show x ++ " " ++ show i) $ do+> termToNet i x@(App t1 t2) ps = do >   let inp = head ps >   rec a <- applicator (head m) (head n) inp >       m <- termToNet i t1 (a : qs)@@ -79,15 +79,23 @@ >       ds <- dup (i - 1) as bs cs >       return $ d : ds -> termToNet i x@(Y t) ps = debug ("termToNet: " ++ show x ++ " " ++ show i) $ do+> {-+> termToNet i x@(Y t) ps = do >   let inp = head ps->   rec d <- duplicator a b inp 0+>   rec a <- bigY (head n) inp 0+>       n <- termToNet i t (a : tail ps)+>   return $ a : tail n+> -}++> termToNet i x@(Y t) ps = do+>   let inp = head ps+>   rec d <- cycle a b inp 0 >       b <- dummy a d >       a <- applicator (head m) b d >       m <- termToNet i t (a : tail ps) >   return $ d : tail m -> termToNet i x@(Tup t1 t2) ps = debug ("termToNet: " ++ show x ++ " " ++ show i) $ do+> termToNet i x@(Tup t1 t2) ps = do >   let inp = head ps >   rec a <- tuple inp (head m) (head n)  >       m <- termToNet i t1 (a : qs)@@ -101,7 +109,7 @@ >       ds <- dup (i - 1) as bs cs >       return $ d : ds -> termToNet i x (inp:out) = debug ("termToNet: " ++ show x ++ " " ++ show i) $ +> termToNet i x (inp:out) =  >   case x of  >     Fst t -> do >       rec a <- twopin 1 "fst" (head n) inp@@ -130,8 +138,8 @@ >   return e  > netToTerm :: INet -> Term-> netToTerm net = ->   let Just (i, (_, [(b, u)], _)) = findInitiator (toList (debug1 "netToTerm net=" net))+> netToTerm (net, _) = +>   let Just (i, (_, [(b, u)], _)) = findInitiator (toList net) >   in toTerm (net ! b) >   where >     toTerm (Abstractor, [_, (b, _), _], _) = Abs (toTerm (net ! b))@@ -155,39 +163,36 @@  meta rules for tuple handling, etc. -> meta net (i, (TwoPin arity l, [_, (c, u)], _))->          (j, (bt, bp, _)) = +> meta (i, (TwoPin arity l, [_, (c, u)], _)) (j, (bt, bp, _)) =  >   case (l, bt) of->     ("fst", Constructor "T") -> Just $ project 1->     ("snd", Constructor "T") -> Just $ project 2->     (_, Single m) -> Just $ applyFunc m->     _ -> Nothing+>     ("fst", Constructor "T") -> Just . project 1+>     ("snd", Constructor "T") -> Just . project 2+>     (_, Single m) -> Just . applyFunc m+>     _ -> const Nothing >   where->     project n =+>     project n (net, maxID) = >       let (d, v) = bp !! n >           (e, w) = bp !! (3 - n)->           maxID = maximum (keys net) >           f = maxID + 1 >           f_ = (Eraser, [(e, w)], Nothing)->       in (adjust (\ (ct, cp, cv) -> (ct, replace u (d, v) cp, cv)) c .+>       in ((adjust (\ (ct, cp, cv) -> (ct, replace u (d, v) cp, cv)) c . >         adjust (\ (dt, dp, dv) -> (dt, replace v (c, u) dp, dv)) d . >         adjust (\ (et, ep, ev) -> (et, replace w (f, 0) ep, ev)) e .->         insert f f_ . delete i . delete j) net->     applyFunc m =->       let maxID = maximum (keys net)->           l' = "(" ++ l ++ " " ++ m ++ ")"+>         insert f f_ . delete i . delete j) net, maxID + 1)+>     applyFunc m (net, maxID) =+>       let l' = "(" ++ l ++ " " ++ m ++ ")" >           f = maxID + 1 >           f_ = (Abstractor, [(c, u), (i, 1), (i, 0)], Nothing) >       in if arity == 1 ->         then (adjust (\ (at, ap, av) -> (Single l', [(c, u)], av)) i .+>         then ((adjust (\ (at, ap, av) -> (Single l', [(c, u)], av)) i . >           adjust (\ (ct, cp, cv) -> (ct, replace u (i, 0) cp, cv)) c .->           delete j) net->         else (adjust (\ (at, ap, av) -> (TwoPin (arity - 1) l', [(f, 2), (f, 1)], av)) i .+>           delete j) net, maxID + 1)+>         else ((adjust (\ (at, ap, av) -> (TwoPin (arity - 1) l', [(f, 2), (f, 1)], av)) i . >           adjust (\ (ct, cp, cv) -> (ct, replace u (f, 0) cp, cv)) c .->           insert f f_ . delete j) net+>           insert f f_ . delete j) net, maxID + 1) -> meta net a b@(_, (TwoPin _ _, _, _)) = meta net b a-> meta _ _ _ = Nothing+> meta a b@(_, (TwoPin _ _, _, _)) = meta b a +> meta _ _ = const Nothing  a pretty printer for generalized lambda terms. 
src/Main.lhs view
@@ -1,27 +1,99 @@ The main program for Lambdascope -> {-# LANGUAGE DoRec,CPP #-}+> {-# LANGUAGE NoMonomorphismRestriction,DoRec,CPP #-}  > module Main where  > import INet > import Diagram hiding (S) > import Lambda+> import Grid  > import Graphics.Rendering.OpenGL (($=), GLfloat) > import qualified Graphics.Rendering.OpenGL as GL > import qualified Graphics.UI.GLFW as GLFW+> import qualified Data.Map as Map > import Control.Monad.Fix-> import Control.Monad (when, unless)+> import Control.Monad.Task+> import Graphics.UI.GLFW.Task+> import Control.Monad.IO.Class+> import Control.Monad.Trans+> import Control.Monad.Trans.State+> import Control.Monad.Identity+> import Control.Monad  > import Data.IORef-> import Data.IntMap hiding (lookup, mapMaybe)+> import qualified Data.Set as Set+> import qualified Data.IntMap as IntMap+> import Data.IntMap hiding (null, lookup, mapMaybe, map) > import Data.Maybe (mapMaybe, fromMaybe)-> import Prelude hiding (map)-> import System.IO.Unsafe+> import Control.Arrow (first, second)+> import Data.List (intercalate, sort)  An interactive net is a mapping from node IDs to their connected (node ID, port No) pairs. +> type Clicks = Map.Map String Int ++> data MainState +>   = MS { showHelp :: Bool                         -- whether to display help message +>        , inet     :: INet                         -- current INet being displayed +>        , frame    :: Frame                        -- single frame of display (for animation)+>        , hlset    :: Maybe ([(Int, Int)], HLSet)  -- wires to highlight+>        , backup   :: [(INet, GridMap Direction, Clicks)]  -- backups for undo/rollback+>        , clicks   :: Clicks                       -- collection of statistics+>        , preset   :: Int                          -- next preset to load+>        , title    :: String                       -- title of current inet+>        }+> initState net = MS False net emptyFrame Nothing [] Map.empty 0 ""++> lift3 = lift . lift . lift+> toggleShowHelp = lift3 $ modify $ \s -> s { showHelp = not (showHelp s) }+> getShowHelp = lift3 $ get >>= return . showHelp++> modifyTitle f = lift3 $ modify $ \s -> s { title = f (title s) }+> getTitle = lift3 $ get >>= return . title+> setTitle = modifyTitle . const++> modifyINet f  = lift3 $ modify $ \s -> s { inet = f (inet s) }+> setINet = modifyINet . const+> getINet = lift3 $ get >>= return . inet++> modifyFrame f = lift3 $ modify $ \s -> s { frame = f (frame s) }+> setFrame = modifyFrame . const+> getFrame = lift3 $ get >>= return . frame++> snapshot = do+>   grid <- lift $ getGrid+>   lift3 $ modify $ \s -> s { backup = take 10 $ (inet s, grid, clicks s) : backup s }+> rollback = do+>    grid <- lift3 $ do+>      b <- fmap backup get+>      case b of+>        [] -> return Nothing+>        (i, g, c) : bs -> modify (\s -> s { inet = i, clicks = c, backup = bs }) >> return (Just g)+>    maybe (return False) (\g -> lift (setGrid g) >> return True) grid++> click name f = lift3 $ modify $ \s -> s { clicks = click' (clicks s) }+>   where click' m = maybe (inc 0) inc $ Map.lookup name m +>           where inc x = Map.insert name (f x) m+> resetClicks = lift3 $ modify $ \s -> s { clicks = Map.empty }+> getClicks = do+>   m <- lift3 $ get >>= return . clicks+>   return $ intercalate ", " $ map (\(x, y) -> x ++ " " ++ show y) $ Map.toList m++> renderINet = getINet >>= setDiagram . netToDiagram++> loadPreset f = do+>    i <- lift3 $ get >>= return . preset+>    lift3 $ modify $ \s -> s { preset = f i, backup = [], clicks = Map.empty }+>    let (title, inet) = presetINet (f i)+>    setTitle title+>    setINet inet+>    lift clearGrid +>    setDirty Dirty+>    renderINet +>    autoAdjust+ Main Program  #ifdef _EnableGUI_@@ -31,217 +103,221 @@ > main = do #endif >   GLFW.initialize->   initWindow w h->   showHelp <- newIORef False->   factor <- newIORef (0, 0, 1.0)->   netRef <- newIORef net->   let loop handle = do ->         (UserAction handle', render) <- handle->         GL.clear [GL.ColorBuffer] ->         (cx, cy, s) <- readIORef factor->         GL.preservingMatrix (do->           GL.translate (vector3 (cx / unit) (cy / unit) 0) ->           GL.scale s s 1->           render)->         help <- readIORef showHelp ->         GL.preservingMatrix (do->           GL.color $ color3 0.2 0.3 0.8->           GL.translate (vector3 (- fromIntegral w / unit / 2) (fromIntegral h / unit / 2 - 1.25) 0)->           GL.scale 0.8 0.8 (1::GLfloat)->           renderString "H toggles help, ESC quits"->           GL.translate $ vector3 0 (-1.25) 0->           when help $ renderText helpText)->         GLFW.swapBuffers->         GLFW.sleep 0.01->         exit <- GLFW.getKey GLFW.ESC ->         unless (exit == GLFW.Press) $ loop handle'->   loop (handleUserAction factor (keyHandle showHelp netRef) (reduce netRef) d)+>   dState <- initWindow w h+>   evalStateT (evalStateT (evalGridT_ (runTask start) margin) dState) (initState emptyINet) >   GLFW.closeWindow >   GLFW.terminate >   where >     -- prepare an initial diagram to load->     net = nodeToNet (mkNode (termToNode t2')) --(App cube (VInt 3))))->     d = netToDiagram net+>     -- net = nodeToNet $ mkNode . termToNode $ App cube (VInt 3) -- (termToNode t4)) -- (App cube (VInt 3)))) >     w = 800 >     h = 600+>     start = do+>       loadPreset id+>       renderINet+>       waitForEvents <- liftIO registerTaskCallbacks+>       setupEvents $ (snapshot >>) . reduce+>       fork $ forever $ watch (onKey >=> isPress >=> isKey 'H') >> toggleShowHelp >> setDirty Lightly+>       fork $ forever $ watch (onKey >=> isPress >=> isKey 'L') >> snapshot >> lift clearGrid >> setDirty Dirty+>       fork $ forever $ watch (onKey >=> isPress >=> isKey '[') >> loadPreset (\x -> x - 1) +>       fork $ forever $ watch (onKey >=> isPress >=> isKey ']') >> loadPreset (+1)+>       fork $ forever $ watch (onKey >=> isPress >=> isKey GLFW.BACKSPACE) >>+>                        rollback >> renderINet >> setDirty Dirty+>       mapM_ (\(c, f) -> fork $ forever $ watch (onKey >=> isPress >=> isKey c) >> snapshot >> getINet >>= f >>= setINet >>+>                         renderINet >> setDirty Dirty)+>          [('R', reduceAll),+>           ('Y', resetAll),+>           ('X', crossAll),+>           ('B', betaAll),+>           ('E', eraseAll),+>           ('U', unwindAll),+>           ('S', scopeAll),+>           ('C', loopcutAll),+>           ('T', toTerm),+>           ('M', metaAll),+>           -- ('P', pruneAll),+>           ('O', outer), +>           ('Q', outerRed), +>           ('A', outerRedAll), +>           ('D', demoteDup) ]+>       forever $ do+>         waitForEvents+>         whenDirty renderAll+>         setDirty None+>       where+>         renderAll = do+>           title <- getTitle+>           font <- getFont+>           stats <- getClicks+>           renderS <- fmap (renderScene font) getFactor +>           renderH <- fmap (renderHelp font ("INet for (" ++ title ++ ") " ++ stats)) getShowHelp +>           oldFrame <- getFrame+>           newFrame <- getDiagram >>= renderDiagram +>           setFrame newFrame+>           d <- getDirty +>           if d < Dirty+>             then liftIO (clearAll >> renderS newFrame >> renderH)+>             else do+>               let f = betweenFrame oldFrame newFrame+>               t0 <- liftIO $ GL.get GLFW.time +>               let dur = 0.5+>                   animate t | t >= t0 + dur = return ()+>                             | otherwise = do+>                     t <- liftIO $ GL.get GLFW.time +>                     let p = realToFrac (t - t0) / dur+>                         g = f (if p > 1 then 1 else p)+>                     liftIO (clearAll >> renderS g >> renderH)+>                     animate t +>               fork $ animate t0 +>         clearAll = GL.clear [GL.ColorBuffer] +>         renderScene font (cx, cy, s) f = GL.preservingMatrix (do+>           GL.translate (vector3 (realToFrac (cx / unit)) (realToFrac (cy / unit)) 0) +>           GL.scale (realToFrac s) (realToFrac s) (1 :: GL.GLfloat)+>           renderFrame font Set.empty f)+>         renderHelp font tagline help = do+>           GL.Size w h <- GL.get GLFW.windowSize +>           GL.preservingMatrix (do+>               GL.color $ color3 0.2 0.3 0.8+>               GL.translate (vector3 (- fromIntegral w / unit' / 2) (fromIntegral h / unit' / 2 - 1.25) 0)+>               GL.scale 0.8 0.8 (1::GLfloat)+>               renderString font ("H toggles help, ESC quits. " ++ tagline)+>               GL.translate $ vector3 0 (-1.25) 0+>               when help $ renderText font helpText)+>           GLFW.swapBuffers  > helpText = unlines ->   [ "CTRL+ mouse  pan"->   , "ALT + mouse  zoom"->   , "Left click   rotate node"->   , "Right click  apply any rule to node"+>   [ -- "CTRL+ mouse  pan"+>     -- , "ALT + mouse  zoom"+>     "Left click   apply any rule to node" >   , "Drag mouse   move node"->   , "1 .. 9       load presets"+>   , "[  or  ]     load presets" >   , "Space        auto zoom"+>   , "BackSpace    rollback one step (max 10)" >   , "L  auto layout all nodes" >   , "R  reduce to head normal form"->   , "X  repeat outermost cross rule"+>   , "X  repeat cross rule everywhere" >   , "B  repeat beta rule everywhere" >   , "E  repeat erase rule everywhere" >   , "U  apply unwind rule everywhere" >   , "S  apply scope rule everywhere" >   , "C  apply loopcut rule everywhere"->   , "T  unwind, cross, scope, cross, loopcut, cross"+>   , "T  print term from net (after loopcut)" >   , "M  repeat meta rule everywhere"->   , "P  prune all none root tree"->   , "O  apply outermost reduction rules once"->   , "V  print beta, meta, size couter on console"+>   -- , "P  prune all none root tree" -- temporarily disable prune since it is not implemented correctly+>   , "O  outermost reduction once"+>   , "Q  outermost reduction once (also goes under lambda)"+>   , "A  repeat outermost reduction (also goes under lambda)" >   , "D  zap duplicator's value (become call-by-need)"] > ->             -> keyHandle showHelp netRef = (fst $ unzip ks, handle)+> presetINet i = (title, node) >   where->     -- change the following line to load different programs for 1..9->     -- It currently loads the (opt N) lambda expression.->     ks = [(c, load $ opt $ fromEnum c - 48) | c <- ['1'..'9']] ++ ->          [('R', reduceAll),->           ('X', crossAll),->           ('B', betaAll),->           ('E', eraseAll),->           ('U', unwindAll),->           ('S', scopeAll),->           ('C', loopcutAll),->           ('T', toTerm),->           ('M', metaAll),->           ('P', pruneAll),->           ('O', outer),->           ('V', viewCounter),->           ('L', reLayout),->           ('D', demoteDup),->           ('H', \n d r -> (n, d, r))]->     handle k d r = do->       net <- readIORef netRef->       when (k == 'H') $ (modifyIORef showHelp not)->       let (net', d', r') = maybe (net, d, r) (\f -> f net d r) (lookup k ks)->       writeIORef netRef net'->       return (d', r')+>     (title, node) = inets !! (i `mod` n)+>     n = length inets+>     inets +>       = [ ("test0", (testL0, 4))+>         , ("trace0", cleanup $ nodeToNet $ mkNode $ termToNode (trace juggle))+>         -- , ("ccaE", cleanup $ nodeToNet $ mkNode $ termToNode ccaE)+>         , ("map2", nodeToNet $ mkNode $ termToNode map2)+>         , ("exp",  cleanup $ nodeToNet $ mkNode $ termToNode e)+>         , ("test1", (testL1, 4))+>         , ("test2", (testL2, 4))+>         , ("test3", (testL3, 4))+>         , ("test4", (testL4, 4))+>         ] ++ map (second (nodeToNet . mkNode))+>         [ ("2", termToNode t2)+>         , ("2 2", termToNode t4)+>         , ("opt 1", termToNode (opt 1))+>         , ("opt 2", termToNode (opt 2))+>         , ("opt 3", termToNode (opt 3))+>         , ("one", termToNode one)+>         -- , ("(\\x.x) x", termToNode (App (Abs Z) (VStr "x")))+>         -- , ("cube 3", termToNode $ App cube (VInt 3))+>         ] -> demoteDup net d r@((posMap, _), _) =->   let net' = map (\a -> case a of->          (Duplicator, cp, cv) -> (Duplicator, cp, Nothing)->          _ -> a) net->       d' = netToDiagram net'->       ids = keys net'->       posMap' = filterWithKey (\i _ -> elem i ids) posMap->       r' = renderDiagram posMap' d'->   in (net', d', r')+> cleanup = fst . runIdentity . repeatRule (applyRule (ruleM erase))  -> load x n _ _ = ->   let net = nodeToNet (mkNode (termToNode x))->       d = netToDiagram net->   in resetCounters n `seq` (net, d, renderDiagram empty d) -> reLayout net d r@((posMap, _), _) = (net, d, renderDiagram empty d)+> demoteDup = return . first (IntMap.map demote)+>   where +>      demote a = case a of+>                   (Duplicator _, cp, cv) -> (Duplicator (Sup False), cp, cv)+>                   _ -> a +> reLayout net d = (net, d)+ activates a local rule to a node, and apply it once.+This will also revive dormant Cycle.  -> reduce :: IORef INet -> Int -> IO ([Int], Diagram)-> reduce netRef i = do->   net <- readIORef netRef->   let a@(at, ap, av) = net ! i+> -- reduce :: Int -> IO ([Int], Diagram)+> reduce i = do+>   -- traceShow ("NID", i)+>   net@(netMap, _) <- getINet+>   let a@(at, ap, av) = revive (netMap ! i) >       (j, n) = head ap->       b@(bt, bp, bv) = net ! j->       net' = debug1 ("reduced from\n   " ++ show net ++ "\nto ") $ if ap == [] ->         then error "here!" -- delete i net->         else fromMaybe net $ localAll net (i, a) (j, b)->       ids = keys net'->   writeIORef netRef net'->   return (ids, netToDiagram net')+>       b@(bt, bp, bv) = revive (netMap ! j)+>   when (ap == []) $ error "here!" -- delete i net+>   net' <- localAll (i, a) (j, b) net >>= return . fromMaybe net +>   -- traceShow ("reduced", net, net') +>   setINet net'+>   renderINet+>   where+>     revive (Duplicator (Cycle _), ap, v) = (Duplicator (Cycle True), ap, v)+>     revive x = x -> localAll = meta_ ->- beta_ ->- cross_ ->- erase +> localAll = meta_ ->- beta_ ->- cross_ ->- erase_ -- ->- share_+ Note that in optimal reduction, the erase is a global rule rather than an  outermost one because it'll otherwise results in redudant beta or meta  reduction. -> reduceAll  = wrapRule (repeatRule (outermost localAll +>+ applyRule erase_))-> crossAll   = wrapRule (repeatRule (outermost cross))-> betaAll    = wrapRule (repeatRule (applyRule beta_))-> eraseAll   = wrapRule (repeatRule (applyRule erase))-> unwindAll  = wrapRule (applyRule unwind)-> scopeAll   = wrapRule (applyRule scope)-> loopcutAll = wrapRule (applyRule loopcut)-> metaAll    = wrapRule (repeatRule (applyRule meta_))+> reduceAll  = wrapRule (applyRule (ruleM resetCycle) +&+ +>                        repeatRule (applyRule meta_) +&+ +>                        repeatRule (applyRule beta_) +&+ +>                        repeatRule (applyRule cross_) +&+ +>                        repeatRule (applyRule erase_))+> resetAll   = wrapRule (applyRule (ruleM resetCycle))+> crossAll   = wrapRule (applyRule cross_)+> betaAll    = wrapRule (applyRule beta_)+> eraseAll   = wrapRule (applyRule erase_)+> metaAll    = wrapRule (applyRule meta_)+> unwindAll  = wrapRule (applyRule unwind_)+> scopeAll   = wrapRule (applyRule scope_)+> loopcutAll = wrapRule (applyRule loopcut_) > outer      = wrapRule (outermost localAll)-> pruneAll   = wrapRule prune--> toTerm net = ->   let r@(net', d') = readback net->       t = netToTerm net'->   in debug ("toTerm=" ++ show t) $ wrapRule (const r) net--> wrapRule f net d r@((posMap, _), _)  = ->   let (net', _) = f net->       d' = netToDiagram net'->       ids = keys net'->       posMap' = filterWithKey (\i _ -> elem i ids) posMap->       r' = renderDiagram posMap' d'->   in (net', d', r')--Counters are hacks. Though our rules are already return the counting, -they are not used.--> crossCounter = unsafePerformIO (newIORef 0)-> betaCounter = unsafePerformIO (newIORef 0)-> metaCounter = unsafePerformIO (newIORef 0)-> sizeTracker = unsafePerformIO (newIORef (1000000,0))--> trackSize net = unsafePerformIO $ do->   m <- readIORef sizeTracker->   let s = size net->       m' = (min (fst m) s, max (snd m) s)->   s `seq` fst m' `seq` snd m' `seq` writeIORef sizeTracker m'->   --putStrLn $ show m'  ->   return net--> resetCounters n = unsafePerformIO $ do->   writeIORef crossCounter 0->   writeIORef betaCounter 0->   writeIORef metaCounter 0->   writeIORef sizeTracker (1000000, 0)--> viewCounter n d r = ->   let view n c = do->         m <- readIORef c->         putStrLn $ n ++ " = " ++ show m->       viewAll n = do->         view "cross" crossCounter->         view "beta" betaCounter->         view "meta" metaCounter->         view "size" sizeTracker->   in unsafePerformIO (viewAll n) `seq` (n, d, r)+> outerRed   = wrapRule (outerReducible localAll)+> outerRedAll = wrapRule (repeatRule (outerReducible localAll))+> pruneAll   = wrapRule (return . prune) -> incCounter c x y z = do->   m <- readIORef c->   let m' = m + 1->   m' `seq` writeIORef c m'+> readbackM = +>   applyRule unwind_  >=> return . fst >=> +>   applyRule scope_   >=> return . fst >=>+>   wrapRule (repeatRule (applyRule cross_)) >=>+>   applyRule loopcut_ >=> return . fst >=>+>   wrapRule (repeatRule (applyRule cross_))  -> mkCounter c f x y z = ->   let r = f x y z->   in if r == Nothing ->     then r->     else unsafePerformIO (incCounter c x y z) `seq` r+> toTerm net = do+>   net' <- readbackM net+>   let t = netToTerm net'+>   liftIO $ putStrLn ("toTerm = " ++ show t) +>   return net' -> mkCounter' c f x y@(_, (t,_,_)) z@(_, (t', _, _)) = ->   let r = f x y z->       tup = case (t, t') of->         (Constructor _, _) -> True->         (_, Constructor _) -> True->         _ -> False->   in if r == Nothing || tup ->     then r ->     else unsafePerformIO (incCounter c x y z) `seq` r+> wrapRule f net = fmap fst (f net)  -Customizd beta, meta and erase rules that track statistics.+Rules with click counters. -> cross_ = mkCounter crossCounter cross-> beta_ = mkCounter betaCounter beta-> meta_ = mkCounter metaCounter meta         -- don't track tuple projection-> erase_ net a b =->   let net' = trackSize net  ->   in (trackSize net `seq`) $->     maybe Nothing (Just . (\net -> trackSize net `seq` net)) $ ->     erase net' a b+> cross_   = wrapClick "cross"   cross+> beta_    = wrapClick "beta"    beta+> meta_    = wrapClick "meta"    meta+> erase_   = wrapClick "erase"   erase+> unwind_  = wrapClick "unwind"  unwind+> scope_   = wrapClick "scope"   scope+> loopcut_ = wrapClick "loopcut" loopcut+> -- share_   = wrapClick "share"   share+> wrapClick counter r a b c = do+>   n <- ruleM r a b c+>   click counter (maybe id (\_ -> (+1)) n)+>   return n  Testing =======@@ -265,7 +341,7 @@ >   return a  > four = do->   rec s <- eraser a+>   rec s <- initiator a >       a <- applicator b b s >       b <- duplicator t a a 0 >       t <- two b@@ -275,11 +351,12 @@  > x = VStr "x" > f = Abs (VFunc 1 "f" Z)+> g = Abs (VFunc 1 "g" Z)  > t2 = church 2 > t2' = App (App (church 2) f) x --App (App (Abs (Abs (App (S Z) (App (S Z) Z))))  f) x -> t4 = App (Abs (App Z Z)) t2+> t4 = App t2 t2 > t4' = App (App t4 f) x  > church n = Abs (Abs (app n (S Z) Z))@@ -306,7 +383,6 @@  Test for handling disconnected graph, rather than tree -> test :: INet > test = fromList [ >   (0, (Eraser, [(1, 0)], Nothing)), >   (1, (Eraser, [(0, 0)], Nothing)),@@ -328,45 +404,41 @@  two duplicators wiring to each other on one side -> testL0 :: INet > testL0 = fromList [ >   (0, (Eraser, [(2, 1)], Nothing)),->   (1, (Eraser, [(2, 2)], Nothing)),->   (2, (Duplicator, [(3, 0), (0, 0), (1, 0)], Just 0)),->   (3, (Duplicator, [(2, 0), (3, 2), (3, 1)], Just 0))]+>   (1, (Eraser, [(3, 2)], Nothing)),+>   (2, (Delimiter, [(3, 1), (0, 0)], Just 0)),+>   (3, (Duplicator Dup, [(4, 0), (2, 0), (1, 0)], Just 0)),+>   (4, (Duplicator Dup, [(3, 0), (4, 2), (4, 1)], Just 0))]  two duplicators wiring to each other on both sides -> testL1 :: INet > testL1 = fromList [->   (2, (Duplicator, [(3, 0), (2, 2), (2, 1)], Just 0)),->   (3, (Duplicator, [(2, 0), (3, 2), (3, 1)], Just 0))]+>   (2, (Duplicator Dup, [(3, 0), (2, 2), (2, 1)], Just 0)),+>   (3, (Duplicator Dup, [(2, 0), (3, 2), (3, 1)], Just 0))]  for commute:  similar to testL0 -> testL2 :: INet > testL2 = fromList [ >   (0, (Eraser, [(2, 1)], Nothing)), >   (1, (Eraser, [(2, 2)], Nothing)),->   (2, (Duplicator, [(3, 0), (0, 0), (1, 0)], Just 1)),->   (3, (Duplicator, [(2, 0), (3, 2), (3, 1)], Just 0))]+>   (2, (Duplicator Dup, [(3, 0), (0, 0), (1, 0)], Just 1)),+>   (3, (Duplicator Dup, [(2, 0), (3, 2), (3, 1)], Just 0))]  similar to testL1 -> testL3 :: INet > testL3 = fromList [->   (2, (Duplicator, [(3, 0), (2, 2), (2, 1)], Just 1)),->   (3, (Duplicator, [(2, 0), (3, 2), (3, 1)], Just 0))]+>   (2, (Duplicator Dup, [(3, 0), (2, 2), (2, 1)], Just 1)),+>   (3, (Duplicator Dup, [(2, 0), (3, 2), (3, 1)], Just 0))]  when a single duplicator loops its two ports -> testL4 :: INet > testL4 = fromList [ >   (0, (Eraser, [(1, 1)], Nothing)), >   (1, (Delimiter, [(2, 0), (0,0)], Just 0)),->   (2, (Duplicator, [(1, 0), (2, 2), (2, 1)], Just 0))]+>   (2, (Duplicator Dup, [(1, 0), (2, 2), (2, 1)], Just 0))]  These are tests for optimality. With church numbers, (n 2 i x) takes exponential time in call-by-need, but only linear to n@@ -414,8 +486,8 @@ 5   70   66     255 6   135  114    323 7   264  204    398-8   392  377    453-9   644  719    539+8   392  377    480+9   644  719    569  It's easy to tell that they are of O(n * 2^n), O(n^7) and O(n^2) respectively. It's also worth mentioning that if we only count the number of@@ -444,7 +516,7 @@ >                        (Fst (S Z))))            -- fst x >                      (App (Snd (S Z)) Z)))))))  -- snd x dt -> e = Y (App integral (VInt 1))+> e = Y (App integral (VInt 1)) -- (1, \dt -> (next dt 1 1, snd e)) > unfold = Abs (App (Snd Z) (VStr "dt")) > expN n = Fst (app n unfold e) @@ -553,4 +625,71 @@ >                (times Z (App (S (S Z)) (minus (S Z) (VInt 1)))))))) > cube = App power (VInt 3) > powerCube = cons power (cons cube (App cube (VInt 5)))- ++CCA Integral of Exp Example+===========================++GHC cannot give the most optimized result for the following expression:++    trace (juggle .  ((dup .  snd) `cross`  id) .  (swap `cross` id) . juggle . +           ((((+1) `cross`  id) .  trace (juggle .  (dup `cross`  id) .  swap .  +           (acc `cross`  id) .  juggle)) `cross`  id) .  juggle .  +           (swap `cross`  id) .  juggle)++    trace f x = let (y, z) = f (x, z) in y+    juggle ((x, y), z) = ((x, z), y)+    cross f g (x, y) = (f x, g y)+    acc (x, i) = i + dt * x+    assoc ((x,y),z) = (x,(y,z))+    unassoc (x,(y,z)) = ((x,y),z)++    trace f x = let g z = snd (f (x, z))+                in fst (f (x, (fix g)))++> trace = App $ Abs $ Abs $ Fst $ App (S Z) (Tup Z (Y (Abs $ Snd $ App (S (S Z)) (Tup (S Z) Z))))+> plus x y = App (VFunc 2 "+" x) y+> juggle = Abs $ Tup (Tup (Fst (Fst Z)) (Snd Z)) (Snd (Fst Z))+> across f g = Abs $ Tup (App f (Fst Z)) (App g (Snd Z))+> acc = Abs $ plus (Snd Z) (times (VStr "dt") (Fst Z))+> assoc = Abs $ Tup (Fst (Fst Z)) (Tup (Snd (Fst Z)) (Snd Z))+> unassoc = Abs $ Tup (Tup (Fst Z) (Fst (Snd Z))) (Snd (Snd Z))+> dup = Abs $ Tup Z Z+> f `dot` g = App (App (Abs $ Abs $ Abs $ App (S (S Z)) (App (S Z) Z)) f) g++> cca0 = assoc `dot` unassoc+> ccaE = trace (juggle `dot` +>        ((dup `dot` snd) `across` id) `dot`+>        (swap `across` id) `dot` juggle `dot`+>        ((((Abs $ plus Z (VInt 1)) `across` id) `dot`  +>        trace (juggle `dot` (dup `across` id) `dot` swap `dot`+>              (acc `across` id) `dot` juggle)) `across` id) `dot` +>        juggle `dot` (swap `across` id) `dot` juggle)+> {-+> ccaE = trace+>        (juggle `dot` (dup `across` id) `dot` swap `dot`+>              (acc `across` id) `dot` juggle)+> -}+>   where+>     snd = Abs $ Snd Z+>     id = Abs Z+>     swap = Abs (Tup (Snd Z) (Fst Z))++toTerm = Abs (Tup (App (VFunc 2 "+" (Snd Z)) (VInt 1)) (App (VFunc 2 "+" (Snd Z)) (App (VFunc 1 "(* dt)" (VFunc 2 "+" (Snd Z))) (VInt 1))))++f (a, b) = (1 + b, dt * b + 1)+f (a, b) = (1 + b, b + dt * (1 + b))++Map Fusion+==========++   map f (x,y) = (f x, map f y)++> map' = Y $ Abs $ Abs $ Abs $ Tup (App (S Z) (Fst Z)) (App (App (S (S Z)) (S Z)) (Snd Z))+> map2 = App (Abs (App Z f `dot` App Z g)) map'+> map2' = App map' (f `dot` g)++(map f . map g) (x, y) = ((f . g) x, (map f ((map g) y)))++(map f . map g) = Y F +  where F h (x, y) = ((f.g) x, h y)+