packages feed

INblobs-0.1.1: src/Functional/Compiler.hs

{-|
    Module      :  Functional.Compiler
    Copyright   :  (c) Daniel Mendes and Miguel Vilaça 2007
    Maintainer  :  danielgomesmendes@gmail.com and jmvilaca@di.uminho.pt
    Stability   :  experimental
    Portability :  portable

    Small Functional Language: Compiler -}
module Functional.Compiler ( compile) where

import Network
import InfoKind
import Palette
import Shape
import Document
import Ports
import Math
import List
import INRule
import INRules
import Common
import CommonUI

import Functional.Language

import Data.Maybe
import Data.List
import qualified Data.Map as Map


type Context = Map.Map Variable ConnectionPoint
data AgentType = Syntactical | Computational


-- | Given a strategy, a closed term and a list of names for iter agents,
-- update the document with the dynamic part of the IN system.
compile :: (Eq n, InfoKind n g, InfoKind e g) => g -> n -> e -> CallBy -> FuncLang -> [(FuncLang, String)] -> Document g n e -> IO (Document g n e)
compile g n e callBy term names doc
  | callBy `elem` [Name,Value] =
	-- add iter symbols
	let symbs = foldr (\h r->  createIterSymbol Syntactical h : createIterSymbol Computational h :r) [] names
 	    repeated = map fst symbs `intersect` (shapesNames $ getPalette doc)
	in if null repeated
	    then
	     do
		let    doc1 = joinPalette (Palette symbs) doc
	    	       palette = getPalette doc1
		tokenRules <- mapM (createIterTokenRule g n e palette) names
		lrules <- mapM (createIterConstructorRules g n e palette names) names
		return $ setNetwork (draw term names palette $ Network.empty g n e)
			$ updateRules (++ tokenRules ++ concat lrules) doc1
	    else fail $ "Give different names to iter symbols. This ones are already in use:\n"
			++ commasAnd repeated

  | otherwise = fail "unknown strategy"


-- | Create the main net.
draw ::(InfoKind n g, InfoKind e g) => FuncLang -> [(FuncLang, String)] -> Palette n -> Network g n e -> Network g n e
draw t names p network = let (x,net) = addNode "interface" p network
		             (n,net1) = addNode "evaluation" p net
		             net2 = setNodePosition n (DoublePoint 15 1.03)
				    .  setNodePosition x (DoublePoint 15 0.01)
		                    .  connectNodes p (x,"interface","interface") (n,"evaluation","res") $ net1
			 in iso p names (n,"evaluation","arg") (0,1.6) t Map.empty net2

-- | The \cal{T} function of the paper.
iso :: (InfoKind n g, InfoKind e g) => Palette n
	-> [(FuncLang, String)] -- ^ list of names for Iter_TYPE symbols
	-> ConnectionPoint -- point to connect the root of the IN term
	-> (Double,Double) -- translation from connection node
	-> FuncLang -- term to translate to INs
	-> Context -- free variables and their respective connection points
	-> Network g n e -- already constructed net
	-> Network g n e -- resulting net
iso palette names point trans term ctx net = -- trace ("ISO: TERM: " ++ show term ++ " CTX: " ++ show (Map.keys ctx)) $
     case term of
	Var v -> case Map.lookup v ctx of
			Just pointVar -> connectNodes palette point pointVar net
			Nothing -> error $ "Internal Error: variable \"" ++ v ++ "\" is not in context " ++ show (Map.keys ctx)

	Abst v t -> let (abstNr, net1) = addNode "lambda" palette net
                        net2           = connectNodes palette point (abstNr,"lambda","res")
					  . moveNode abstNr trans point
					  $ net1
			abstPoint = translate (getNodePosition net2 abstNr) (DoublePoint (-1) 1)
                        (net3, ctx1) = if v `elem` (freeVars t)
					then (net2, Map.insert v (abstNr,"lambda","var") ctx)
					else let (eraseNr,net4) = addNode "Erase" palette net2
					     in (connectNodes palette (eraseNr,"Erase","down") (abstNr,"lambda","var")
							$  setNodePosition eraseNr abstPoint net4, ctx)
                    in iso palette names (abstNr,"lambda","body") (1,1.2) t ctx1 net3

	Appl t u -> createNet2 "beforeApplication" t "func" u "arg"
	TT   -> createNet0 "True"
	FF   -> createNet0 "False"
	Zero -> createNet0 "Zero"
	Succ t -> createNet1 "Succ" t "arg"
	Nil  -> createNet0 "Nil"
	Cons t u -> createNet2 "Cons" t "head" u "tail"
	iter@(IterBool v f b)     -> createNetIter iter [(v, 'v'),(f, 'f')]                 b "arg"
	iter@(IterNat x s z n)    -> createNetIter iter [(Abst x s, 's'),(z, 'z')]          n "arg"
	iter@(IterList x y c n l) -> createNetIter iter [(Abst x $ Abst y c, 'c'),(n, 'n')] l "arg"
	_ -> error "Internal Error: none exaustive patterns in function iso."
  where
	-- | Create a net with the agent of arity 0 and connect it to the given point.
	-- createNet0 :: String -> Network g n e
	createNet0 agentName = let (nr, net1) = addNode agentName palette net
			     in connectNodes palette point (nr,agentName,"res")
				 $ moveNode nr trans point net1

	-- | Create a net with the agent of arity 1 and connect
	-- it to the given point and to the translation of the subterm.
	-- createNet1 :: String -> FuncLang -> PortName -> Network g n e
	createNet1 agentName term portName =
		   let (nr, net1) = addNode agentName palette net
		   in iso palette names (nr,agentName,portName) (0,1) term ctx
			. connectNodes palette point (nr,agentName,"res")
			. moveNode nr trans point
			$ net1

	-- | Create a net with the agent of arity 2 and connect it to the given
	-- point and to the subterms, taking care of shared variables.
	-- createNet2 :: String -> FuncLang -> PortName -> FuncLang -> PortName -> Network g n e
	createNet2 agentName tLeft portLeft tRight portRight =
		    let (nr, net1) = addNode agentName palette net
                        net2       = connectNodes palette point (nr,agentName,"res")
					. moveNode nr trans point
					$  net1
			(net3, [ctxL,ctxR]) = shareCommonVariables palette [tLeft, tRight] ctx net2
                    in iso palette names (nr,agentName,portLeft) (-5,1) tLeft ctxL
			. iso palette names (nr,agentName,portRight) (5,1) tRight ctxR
			$ net3

	-- | Create a net with the iter agent and connect it to the given
	-- point and to the subterms, taking care of shared variables.
	-- createNetIter :: FuncLang -- iter_TYPE term
	-- 	-> [(FuncLang, Char)] -- list of sub terms and respective port prefixs
	-- 	-> FuncLang -- middle (arg) sub term
	-- 	-> PortName -- middle (arg) port name
	-- 	-> Network g n e
	createNetIter term l tMiddle portMiddle =
	  case lookupIterName term names of
	    Just agentName ->
		let (nr, net1) = addNode agentName palette net
                    net2       = connectNodes palette point (nr,agentName,"res")
				  . moveNode nr trans point
				  $ net1
		    (terms,portPrefixs) = unzip l
		    (net3, ctxM:ctxs) = shareCommonVariables palette (tMiddle:terms) ctx net2
		in iso palette names (nr,agentName,portMiddle) (0,1.5) tMiddle ctxM
		  . foldlCont (connect2Iter nr agentName) (zip portPrefixs ctxs)
		  $ net3
	    Nothing -> error $ "Internal Error: symbol " ++ show term ++ "not found in list of symbol's names."

	-- connect2Iter :: (InfoKind e g) => NodeNr -> String -> Network g n e -> (Char,Context) -> Network g n e
	connect2Iter nr agentName net (portPrefix, ctx) =
		connectPorts palette portPrefix nr agentName ctx net

-- | Connect the set of ports with prefix 'pF' from given agent to the context.
connectPorts :: (InfoKind e g) => Palette n ->
		Char -- port prefix
		-> NodeNr -- iter agent
		-> String -- iter agent
		-> Context
		-> Network g n e -> Network g n e
connectPorts palette pF iterNr iterAgent ctx net = Map.foldWithKey (connectIterZs iterNr) net ctx
  where -- connectIterZs :: (InfoKind e g) => NodeNr
	--		-> Variable -> ConnectionPoint -> Network g n e
	--		-> Network g n e
 	connectIterZs iterNr var point net =
		connectNodes palette (iterNr,iterAgent,pF:'_':var) point net


type RichCtx = Map.Map Variable [ConnectionPoint]

-- | Add as many copy agents as necessary for variables that are shared between sub-terms.
shareCommonVariables :: (InfoKind n g,InfoKind e g) =>
	Palette n
	-> [FuncLang] -- list of n subterms
	-> Context
	-> Network g n e
	-> (Network g n e, [Context]) -- new net with necessary copy agents
				      -- and n contexts: respectively for each subterm
shareCommonVariables palette l ctx net =
    let
	lVars = map freeVars l
	(net1, richCtx) =
		 Map.mapAccumWithKey copyPointInNet net . Map.mapWithKey count $ ctx
    in (net1, snd $ mapAccumL subTermCtx richCtx lVars)
  where
	lVars = map freeVars l

	count :: Variable -> ConnectionPoint -> (Int, ConnectionPoint)
	count var point = (length $ findIndices (var `elem`) lVars, point)

--	copyPointInNet :: (InfoKind n g,InfoKind e g) =>
--		Network g n e -> Variable -> (Int, ConnectionPoint)
--		-> (Network g n e, [ConnectionPoint])
	copyPointInNet net _ (0, point) = error "Internal Error in copyPointInNet"
	copyPointInNet net _ (1, point) = (net, [point])
	copyPointInNet net v (n, point) =
	    let (nNr, net1) = addNode "copy" palette net
		m = n `div` 2
		net2 = moveNode nNr (-1.5,1) point
			$ connectNodes palette point (nNr,"copy","src") net1
		(netL, pointsL) = copyPointInNet net2 v (m  , (nNr,"copy","fst_target"))
		(netR, pointsR) = copyPointInNet netL v (n-m, (nNr,"copy","snd_target"))
	    in (netR, pointsL ++ pointsR)



	subTermCtx :: RichCtx -> [Variable] -> (RichCtx, Context)
	subTermCtx richctx = (id >< Map.fromList) . mapAccumL variableCtx richctx

	variableCtx :: RichCtx -> Variable -> (RichCtx, (Variable,ConnectionPoint))
	variableCtx richctx var = (Map.adjust tail var richctx, (var, head $ richctx Map.! var))

-- | Connect unused variables to erase agents.
closeEpsilon ::(InfoKind n g, InfoKind e g) =>  Palette n -> Context -> Network g n e -> Network g n e
closeEpsilon pal ctx net = Map.fold aux net ctx
  where
	-- aux :: ConnectionPoint -> Network g n e -> Network g n e
	aux point nt = let (node_nr, n_nt) = addNode "Erase" pal nt
                       in connectNodes pal point (node_nr,"Erase","down")
			  . moveNode node_nr (0,-1) point
			  $ n_nt

createIterSymbol :: AgentType -> (FuncLang, String) -> (String, (Shape, Ports, Maybe a))
createIterSymbol at (term, name) = (name2, (shape, ports, Nothing))
  where
	(x,y) = (1.0, 1.0)
	topPosition = DoublePoint 0.0 (-y)
	name2 =  case at of
	 		Syntactical -> name
			Computational -> '^':name
	shape =
	 case at of
	  Syntactical -> -- triangle with text
	       Composite { shapeSegments =
		[ Polygon { shapeStyle =    ShapeStyle { styleStrokeWidth = 1
						       , styleStrokeColour = licorice
						       , styleFill = lightRed }
			  , shapePerimeter = [topPosition, DoublePoint (-x) 0.3, DoublePoint x 0.3] }
		, Text { shapeStyle = defaultShapeStyle
		       , shapeText = name }
		] }
	  Computational -> -- circle
	       TextInEllipse { shapeStyle = ShapeStyle { styleStrokeWidth = 1
						       , styleStrokeColour = licorice
						       , styleFill = lightRed }
			     , shapeText = name }
	(resP,argP) = case at of
			Syntactical    -> (("res", topPosition),("arg", DoublePoint 0.0 0.3))
			Computational -> (("arg", DoublePoint 0.0 0.3),("res", topPosition))

	ports =
	  case term of
	   IterBool v f (Var "") -> resP: portsFrom (-x) 'v' v ++ argP: portsFrom x 'f' f
	   IterNat a s z (Var "") -> resP: portsFrom (-x) 's' (Abst a s) ++ argP: portsFrom x 'z' z
	   IterList a b c n (Var "") -> resP: portsFrom (-x) 'c' (Abst a $ Abst b c) ++ argP: portsFrom x 'n' n
	   _ -> error "unexpected case"
	portsFrom :: Double -> Char -> FuncLang -> [Port]
	portsFrom pos char term = snd . mapAccumL (createPort char desc) pos $ freeVars term
	  where desc = (signum pos) * (-0.3)

createPort :: Char -> Double -> Double -> String -> (Double, Port)
createPort c desc x str = (x + desc, (c:'_':str, DoublePoint x 0.3))

createIterTokenRule :: (InfoKind n g, InfoKind e g) => g -> n -> e -> Palette n -> (FuncLang, String) -> IO (INRule g n e)
createIterTokenRule g n e palette (_, agent) =
     do
	(rule,nNr1,nNr2) <- createRuleWizard g n e palette "evaluation" agent
	portsEval <- getSymbolPorts "evaluation" palette
	return . updateRHS (f (map fst portsEval) ('^':agent) nNr1 nNr2) $ copyLHS2RHS rule
  where
	-- f :: (InfoKind e g) => [PortName] -> ShapeName -> NodeNr -> NodeNr -> Network g n e -> Network g n e
	f ["arg","res"] shape2 nNr1 nNr2 net =
		let pos1 = getNodePosition net nNr1
		    pos2 = getNodePosition net nNr2
		    mid = fromJust $ otherExtremeOfEdgeConnectedOnPort net nNr1 "arg"
		    top = fromJust $ otherExtremeOfEdgeConnectedOnPort net nNr1 "res"
		    bot = fromJust $ otherExtremeOfEdgeConnectedOnPort net nNr2 "arg"
		    arc1 = fromJust $ edgeConnectedOnPort net nNr1 "arg"
		    arc2 = fromJust $ edgeConnectedOnPort net nNr1 "res"
		    arc3 = fromJust $ edgeConnectedOnPort net nNr2 "arg"

		in addEdges palette
			[ ((nNr2,"arg"),(nNr1,"res")) -- (iter,arg) |-> (token,res)
			, (top,(nNr2,"res")) -- top interface |-> (iter,res)
			, ((nNr1,"arg"), bot) -- (token,arg) |-> bottom interface
			]
		   . removeEdge arc1 . removeEdge arc2 . removeEdge arc3
		   . setNodeShape nNr2 shape2 -- change symbol iter to ^iter
		   . setNodePosition nNr1 pos2 . setNodePosition nNr2 pos1 -- swap positions
		   $ net
	f _ _ _ _ _ = error $ "Internal Error: creating rule for token and " ++ agent

createIterConstructorRules :: (InfoKind n g, InfoKind e g) => g -> n -> e -> Palette n -> [(FuncLang, String)] -> (FuncLang, String) -> IO (INRules g n e)
createIterConstructorRules g n e palette names (term, agent) =
	mapM (createIterConstructorRule g n e palette names (term, agent) ) constructorAgents
	where constructorAgents = case term of
		IterBool _ _ (Var "") -> ["True","False"]
		IterNat _ _ _ (Var "") -> ["Zero","Succ"]
		IterList _ _ _ _ (Var "") -> ["Nil","Cons"]
		-- new Iterators
		_ -> error "Internal Error: iterator term expected and something different found."


data InterfaceID =
	RES ConnectionPoint
	| SuccArg ConnectionPoint
	| ConsHead ConnectionPoint
	| ConsTail ConnectionPoint
	| IterBoolV Context
	| IterBoolF Context
	| IterNatS Context
	| IterNatZ Context
	| IterListC Context
	| IterListN Context
	deriving(Eq,Ord,Show)

equalIID :: InterfaceID -> InterfaceID -> Bool
equalIID (RES _)       (RES _)       = True
equalIID (SuccArg _)   (SuccArg _)   = True
equalIID (ConsHead _)  (ConsHead _)  = True
equalIID (ConsTail _)  (ConsTail _)  = True
equalIID (IterBoolV _) (IterBoolV _) = True
equalIID (IterBoolF _) (IterBoolF _) = True
equalIID (IterNatS _)  (IterNatS _)  = True
equalIID (IterNatZ _)  (IterNatZ _)  = True
equalIID (IterListC _) (IterListC _) = True
equalIID (IterListN _) (IterListN _) = True
equalIID _ _ = False

createIterConstructorRule :: (InfoKind n g, InfoKind e g) => g -> n -> e -> Palette n -> [(FuncLang, String)] -> (FuncLang, String) -> String -> IO (INRule g n e)
createIterConstructorRule g n e palette names (term, agent) agent2 =
     do
	(rule,nNr1,nNr2) <- createRuleWizard g n e palette ('^':agent) agent2
	let lhs = getLHS rule
	    (_:lPorts1) = map fst . maybe (error "Internal Error: unexpected Nothing") id $ getPorts palette lhs nNr1
	    (_:lPorts2) = map fst . maybe (error "Internal Error: unexpected Nothing") id $ getPorts palette lhs nNr2
	    groupedInterfaces = groupInterfaces lhs (nNr1, lPorts1) (nNr2, lPorts2)

	return . updateRHS (makeRHS term agent agent2 groupedInterfaces) $ copyLHSInterface2RHS rule
  where
	groupInterfaces :: Network g n e -> (NodeNr, [PortName]) -> (NodeNr, [PortName])
			-> [InterfaceID]
	groupInterfaces lhs (nNr1, auxPorts1) (nNr2, auxPorts2) =
		sort (f4 nNr1 auxPorts1) ++ f4 nNr2 auxPorts2
	  where
		f4 :: NodeNr -> [PortName] -> [InterfaceID]
		f4  nNr = map (f3 lhs nNr) . groupBy aux

	f3 :: Network g n e -> NodeNr -> [PortName] -> InterfaceID
	f3 net nNr = aux3
	  where
		aux3 [p@"res" ] = RES $ f2 p
		aux3 [p@"head"] = ConsHead $ f2 p
		aux3 [p@"tail"] = ConsTail $ f2 p
		aux3 [p@"arg" ] = SuccArg $ f2 p
		aux3 l@(('v':'_':_):_) = IterBoolV . f6 $ l
		aux3 l@(('f':'_':_):_) = IterBoolF . f6 $ l
		aux3 l@(('s':'_':_):_) = IterNatS  . f6 $ l
		aux3 l@(('z':'_':_):_) = IterNatZ  . f6 $ l
		aux3 l@(('c':'_':_):_) = IterListC . f6 $ l
		aux3 l@(('n':'_':_):_) = IterListN . f6 $ l
		aux3 _		 = error "Internal Error in aux3"

		f2 :: PortName -> ConnectionPoint
		f2 = f4 . maybe (error "Internal Error: unexpected Nothing") id . otherExtremeOfEdgeConnectedOnPort net nNr

		f4 (nNr,"interface") = (nNr,"interface","interface")
		f4 _ = error "Internal Error: unexpected agent"

		f5 (_:'_':portName) = portName
		f5 _ = error "Internal Error: unexpected port name"

		f6 = Map.fromList . map (f5 /\ f2)

	-- makeRHS :: FuncLang -> String -> String -> [InterfaceID] -> Network g n e -> Network g n e
	makeRHS term iterAgent consAgent (RES interfacePoint:lInters) net =
		let (conE,net1) = connectToken palette interfacePoint net
		in reallyMakeRHS palette names term iterAgent consAgent lInters conE net1
	makeRHS _ _ _ _ _ = error "Internal Error making RHS."

-- | Add a token agent and connect its auxiliar port to the given point.
connectToken :: (InfoKind n g, InfoKind e g) => Palette n -> ConnectionPoint -> Network g n e -> (ConnectionPoint, Network g n e)
connectToken palette topPoint net =
	let (nNrE, net1) = addNode "evaluation" palette net -- add token agent
	in ( (nNrE,"evaluation","arg")
	   , connectNodes palette (nNrE, "evaluation", "res") topPoint -- (token, res) |-> topPoint
		. moveNode nNrE (0,1) topPoint
		$ net1 )

aux :: PortName -> PortName -> Bool
aux (c1:'_':_) (c2:'_':_) = c1 == c2
aux _ _ = False

-- | Creates the network that is the RHS of rule for given symbols.
-- See interaction rules for iterators in paper
-- "Encoding Iterators in Interaction Nets" for more details.
reallyMakeRHS :: (InfoKind n g, InfoKind e g)
	      => Palette n
	      -> [(FuncLang, String)]
	      -> FuncLang -- Iter_TYPE args (Var "")
	      -> String -- iter agent name
	      -> String -- constructor agent name
	      -> [InterfaceID] -> ConnectionPoint
	      -> Network g n e -> Network g n e
reallyMakeRHS palette names term iterAgent const l point =
 case (term, const) of
   (IterBool v _ (Var ""), "True") ->
     case emptyfy l [IterBoolV emp, IterBoolF emp] of
	[IterBoolV ctxV, IterBoolF ctxF] -> simpleRHS palette names point ctxV ctxF v
	_ -> error "Internal Error: unexpected [InterfaceID]"
   (IterBool _ f (Var ""), "False") ->
     case emptyfy l [IterBoolV emp, IterBoolF emp] of
	[IterBoolV ctxV, IterBoolF ctxF] -> simpleRHS palette names point ctxF ctxV f
	_ -> error "Internal Error: unexpected [InterfaceID]"
   (IterNat _ _ z (Var ""), "Zero") ->
     case emptyfy l [IterNatS emp, IterNatZ emp] of
	[IterNatS ctxS, IterNatZ ctxZ] -> simpleRHS palette names point ctxZ ctxS z
	_ -> error "Internal Error: unexpected [InterfaceID]"
   (IterNat x s _ (Var ""), "Succ") ->
     case emptyfy l [IterNatS emp, IterNatZ emp, SuccArg undef] of
	[IterNatS ctxS, IterNatZ ctxZ, SuccArg pSucArg] ->
	   oneRecursiveCallRHS palette names point ctxS emp ctxZ pSucArg x s iterAgent 'z' 's'
	_ -> error "Internal Error: unexpected [InterfaceID]"
   (IterList _ _ _ n (Var ""), "Nil") ->
     case emptyfy l [IterListC emp, IterListN emp] of
	[IterListC ctxC, IterListN ctxN] -> simpleRHS palette names point ctxN ctxC n
	_ -> error "Internal Error: unexpected [InterfaceID]"
   (IterList x y c _ (Var ""), "Cons") ->
     case emptyfy l [IterListC emp, IterListN emp, ConsHead undef, ConsTail undef] of
	[IterListC ctxC, IterListN ctxN, ConsHead pConsHead, ConsTail pConsTail] ->
	   oneRecursiveCallRHS palette names point ctxC (Map.singleton x pConsHead) ctxN pConsTail y c iterAgent 'n' 'c'
	_ -> error "Internal Error: unexpected [InterfaceID]"
   _ -> error "Undefined case making RHS."
  where emp = Map.empty
	undef = (0,"INVALID NODE","")

emptyfy :: [InterfaceID] -> [InterfaceID] -> [InterfaceID]
emptyfy given needed = map (\e -> maybe e id $ find (equalIID e) given ) needed


-- | Defines a simple RHS that evaluates the translation of term t
-- connecting its free variables to the corresponding interface agents
-- and connects the remaing interface agents to erase agents.
-- For example RHS for rule  IterBool |X| TT  that is
--  \TOKEN(T(V))
simpleRHS :: (InfoKind n g, InfoKind e g)
	=> Palette n
	-> [(FuncLang, String)]
	-> ConnectionPoint
	-> Context -- context to connect
	-> Context -- context to erase
	-> FuncLang -- term to translate
	-> Network g n e -> Network g n e
simpleRHS palette names point goodCtx eraCtx term = -- trace ("simpleRHS: POINT: " ++ show point ++ " GOODCTX" ++ show (Map.keys goodCtx) ++ " ERACTX: " ++  show (Map.keys eraCtx) ++" TERM: " ++ show term ) $
	iso palette names point (2,0) term goodCtx
	. closeEpsilon palette eraCtx


-- | Defines a RHS with one recursive call.
-- Like    IterNat |X| suc    or    IterList |X| cons
oneRecursiveCallRHS :: (InfoKind n g, InfoKind e g)
	=> Palette n
	-> [(FuncLang, String)]
	-> ConnectionPoint -- (token, arg) port to connect generated net
	-> Context -- context to connect to copy
	-> Context -- context to pass to translate function; refers to arguments of constructor
	-> Context -- context to connect to iterator
	-> ConnectionPoint -- interface for place to apply recursion
	-> Variable -- variable that represents result of recursive call
	-> FuncLang -- term to translate
	-> String -- Iterator agent name
	-> Char -- port prefix for final elements
	-> Char -- port prefix for cont elements
	-> Network g n e -> Network g n e
oneRecursiveCallRHS palette names topPoint copyCtx argCtx stopCtx argPoint var term iterAgent pF pC net =
  let (newNet, newCtx) =
	if var `elem` lFreeVars
	then
    	  let
   		(iterNr,net1) = addNode iterAgent palette net -- add iter agent
		(net2, ctxNew)  = Map.mapAccumWithKey (connectCopies iterNr) net1 copyCtx -- (Iter, FV(S)) |-> (copy, snd_target)
											  -- (copy, src) |-> copyCtx
    	  in ( connectNodes palette (iterNr,iterAgent,"arg") argPoint -- (Iter, arg) |-> argInterface
		. connectPorts palette pF iterNr iterAgent stopCtx -- (Iter, FV(Z)) |-> stopCtx
		. moveNode iterNr (0,-1) argPoint
		$ net2, Map.insert var (iterNr,iterAgent,"res") ctxNew )

	else (closeEpsilon palette (Map.insert "" argPoint stopCtx) net, copyCtx)
      (frees, dontExist) = Map.partitionWithKey aux argCtx

  in iso palette names topPoint (2,0.7) term (frees `Map.union` newCtx)
	. closeEpsilon palette dontExist
	$ newNet
   where
	lFreeVars = freeVars term

--	connectCopies :: NodeNr -> Network g n e -> Variable -> ConnectionPoint -> (Network g n e, ConnectionPoint)
	connectCopies iterNr net var point =
	  let (copyNr,net1) = addNode "copy" palette net
	  in (connectNodes palette (iterNr,iterAgent,pC:'_':var) (copyNr,"copy","snd_target")
		. connectNodes palette (copyNr,"copy","src") point
		. moveNode copyNr (0,-0.8) point
		$ net1, (copyNr,"copy","fst_target") )

	aux var _ = var `elem` lFreeVars

type ConnectionPoint =  ( NodeNr
			, ShapeName
			, PortName
			)

-- | Connect a port in a node to another.
connectNodes :: (InfoKind e g) => Palette n -> ConnectionPoint -> ConnectionPoint -> Network g n e -> Network g n e
connectNodes palette (nrFrom, shapeFrom, portFrom) (nrTo, shapeTo, portTo) net =
    case (getNodeShape net nrFrom, getNodeShape net nrTo) of
	(str1, str2) | str1 == shapeFrom && str2 == shapeTo ->
	    case (getPorts palette net nrFrom, getPorts palette net nrTo) of
		(Just portsFrom, Just portsTo) ->
		   case (Data.List.lookup portFrom portsFrom, Data.List.lookup portTo portsTo) of
			(Just _, Just _) ->
			   addEdge palette nrFrom portFrom nrTo portTo net
			(Nothing,_) -> error $ "Port \"" ++ portFrom ++ "\" do not exist in symbol \"" ++ shapeFrom ++ "\"."
			(_,Nothing) -> error $ "Port \"" ++ portTo ++ "\" do not exist in symbol \"" ++ shapeTo ++ "\"."
		_ -> error "No ports."
	_ -> error "Shapes do not coincide."

-- | Move first node to position @(dx,dy)@ relatively to reference point position.
moveNode :: NodeNr -> (Double,Double) -> ConnectionPoint -> Network g n e -> Network g n e
moveNode node (dx,dy) refPoint net =
	setNodePosition node ( translate (DoublePoint dx dy)
				   $ getNodePosition net $ fst3 refPoint)
	   net