|  | @@ -2,9 +2,10 @@
 | 
	
		
			
				|  |  |  -include("../include/ebb_prim.hrl").
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  %%% Operation construction
 | 
	
		
			
				|  |  | --export([func/1, value/1, values/1, dynamic/1, route/2,
 | 
	
		
			
				|  |  | -	 pipe/1, parallel/1, sequence/1, funnel/1,
 | 
	
		
			
				|  |  | -	 split/1, merge/1, switch/2, loop/2]).
 | 
	
		
			
				|  |  | +-export([func/1, value/1, values/1,
 | 
	
		
			
				|  |  | +	 pipe/1, par/1,
 | 
	
		
			
				|  |  | +	 route/3, sync/1,
 | 
	
		
			
				|  |  | +	 split/1, merge/1, switch/2]).
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  %%% Operation querying
 | 
	
		
			
				|  |  |  -export([in_arity/1, out_arity/1, can_connect/2]).
 | 
	
	
		
			
				|  | @@ -21,31 +22,14 @@ func(F) when is_function(F) ->
 | 
	
		
			
				|  |  |  value(X) -> #value{value=X}.
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  values(Xs = [_|_]) ->
 | 
	
		
			
				|  |  | -    parallel(lists:map(fun value/1, Xs)).
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -dynamic(Gen) when is_function(Gen) ->
 | 
	
		
			
				|  |  | -    {arity, N} = erlang:fun_info(Gen, arity),
 | 
	
		
			
				|  |  | -    #dynamic{in=N, code=Gen}.
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -route(N, Map) ->
 | 
	
		
			
				|  |  | -    try
 | 
	
		
			
				|  |  | -	lists:foldl(fun(Target, Count) when Target =< N -> Count+1;
 | 
	
		
			
				|  |  | -		       (Target, _Count) when Target > N -> throw(out_of_bounds)
 | 
	
		
			
				|  |  | -		    end,
 | 
	
		
			
				|  |  | -		    0, Map)
 | 
	
		
			
				|  |  | -    of 
 | 
	
		
			
				|  |  | -	Count -> #route{in=N, out=Count, map=Map}
 | 
	
		
			
				|  |  | -    catch
 | 
	
		
			
				|  |  | -	throw:out_of_bounds -> erlang:error(badarg, [N, Map])
 | 
	
		
			
				|  |  | -    end.
 | 
	
		
			
				|  |  | +    par([ value(X) || X <- Xs ]).
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  pipe(Ops = [First|Rest]) ->
 | 
	
		
			
				|  |  |      try 
 | 
	
		
			
				|  |  | -	lists:foldl(fun(Op2, Op1) ->
 | 
	
		
			
				|  |  | -			       case can_connect(Op1, Op2) of
 | 
	
		
			
				|  |  | -				   true  -> Op2;
 | 
	
		
			
				|  |  | -				   false -> throw(bad_connection)
 | 
	
		
			
				|  |  | -			       end
 | 
	
		
			
				|  |  | +	lists:foldl(fun(Op2, Op1) -> case can_connect(Op1, Op2) of
 | 
	
		
			
				|  |  | +					 true  -> Op2;
 | 
	
		
			
				|  |  | +					 false -> throw(bad_connection)
 | 
	
		
			
				|  |  | +				     end
 | 
	
		
			
				|  |  |  		    end,
 | 
	
		
			
				|  |  |  		    First, Rest)
 | 
	
		
			
				|  |  |      of
 | 
	
	
		
			
				|  | @@ -54,92 +38,74 @@ pipe(Ops = [First|Rest]) ->
 | 
	
		
			
				|  |  |  	throw:bad_connection -> erlang:error(badarg, [Ops])
 | 
	
		
			
				|  |  |      end.
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -parallel(Ops = [_|_]) ->
 | 
	
		
			
				|  |  | +par(Ops = [_|_]) ->
 | 
	
		
			
				|  |  |      {In, Out} = flatten_arity(Ops),
 | 
	
		
			
				|  |  | -    #parallel{in=In, out=Out, ops=Ops}.
 | 
	
		
			
				|  |  | +    #par{in=In, out=Out, ops=Ops}.
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +route(N, M, Map) ->
 | 
	
		
			
				|  |  | +    Good = lists:all(fun({Source, Target}) when Source >= 1, Source =< N,
 | 
	
		
			
				|  |  | +						Target >= 1, Target =< M ->
 | 
	
		
			
				|  |  | +			     true;
 | 
	
		
			
				|  |  | +			({_Source, _Target}) ->
 | 
	
		
			
				|  |  | +			     false
 | 
	
		
			
				|  |  | +		     end,
 | 
	
		
			
				|  |  | +		     Map),
 | 
	
		
			
				|  |  | +    case Good of
 | 
	
		
			
				|  |  | +	true  -> #route{in=N, out=M, map=Map};
 | 
	
		
			
				|  |  | +	false -> erlang:error(badarg, [N, M, Map])
 | 
	
		
			
				|  |  | +    end.
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -sequence(Ops = [_|_]) ->
 | 
	
		
			
				|  |  | -    {In, Out} = flatten_arity(Ops),
 | 
	
		
			
				|  |  | -    #sequence{in=In, out=Out, ops=Ops}.
 | 
	
		
			
				|  |  | +sync(N) -> #sync{size=N}.
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  split(N) -> #split{size=N}.
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  merge(N) -> #merge{size=N}.
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -funnel(N) -> #funnel{size=N}.
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -switch(Switch, Map = [{_, Op1}|Rest]) ->
 | 
	
		
			
				|  |  | +switch(N, Map = [_|_]) ->
 | 
	
		
			
				|  |  | +    SMap = lists:ukeysort(2, Map),
 | 
	
		
			
				|  |  |      try
 | 
	
		
			
				|  |  | -	{I, O} = lists:foldl(
 | 
	
		
			
				|  |  | -		   fun({_, Op}, {In, Out}) -> known_op_arity(Op, In, Out) end,
 | 
	
		
			
				|  |  | -		   {in_arity(Op1), out_arity(Op1)}, Rest),
 | 
	
		
			
				|  |  | -	known_op_arity(Switch, I, 1),
 | 
	
		
			
				|  |  | -	{I, O}
 | 
	
		
			
				|  |  | +	lists:foldl(fun({_T_I, I}, Prev_I) when I == 1+Prev_I -> I;
 | 
	
		
			
				|  |  | +		       ({_T_I, _I}, _Prev_I) -> throw(missing_path)
 | 
	
		
			
				|  |  | +		    end,
 | 
	
		
			
				|  |  | +		    0, SMap)
 | 
	
		
			
				|  |  |      of
 | 
	
		
			
				|  |  | -	{In, Out} -> #switch{in=In, out=Out, switch=Switch, map=Map}
 | 
	
		
			
				|  |  | +	I -> #switch{in=1+N, out=I*N, map=SMap}
 | 
	
		
			
				|  |  |      catch
 | 
	
		
			
				|  |  | -	error:function_clause -> erlang:error(badarg, [Switch, Map])
 | 
	
		
			
				|  |  | +	throw:missing_path -> erlang:error(badarg, [N, Map])
 | 
	
		
			
				|  |  |      end.
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -loop(Init, Op) ->
 | 
	
		
			
				|  |  | -    S = out_arity(Init),
 | 
	
		
			
				|  |  | -    {In, Out} = case {in_arity(Op)-S, out_arity(Op)-S} of
 | 
	
		
			
				|  |  | -		    {I, O} when I >= 0, O > 0 -> {I+in_arity(Init), O};
 | 
	
		
			
				|  |  | -		    {_, _} -> erlang:error(badarg, [Init, Op])
 | 
	
		
			
				|  |  | -		end,
 | 
	
		
			
				|  |  | -    #loop{in=In, out=Out, init=Init, op=Op}.
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |  %%%-----------------------------------------------------------------------------
 | 
	
		
			
				|  |  |  %%% Operation querying
 | 
	
		
			
				|  |  |  %%%-----------------------------------------------------------------------------
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -in_arity(#func{in=In})     -> In;
 | 
	
		
			
				|  |  | -in_arity(#value{})         -> 0;
 | 
	
		
			
				|  |  | -in_arity(#dynamic{in=In})  -> In;
 | 
	
		
			
				|  |  | -in_arity(#route{in=In})    -> In;
 | 
	
		
			
				|  |  | -in_arity(#pipe{in=In})     -> In;
 | 
	
		
			
				|  |  | -in_arity(#parallel{in=In}) -> In;
 | 
	
		
			
				|  |  | -in_arity(#sequence{in=In}) -> In;
 | 
	
		
			
				|  |  | -in_arity(#split{})         -> 1;
 | 
	
		
			
				|  |  | -in_arity(#merge{size=N})   -> N;
 | 
	
		
			
				|  |  | -in_arity(#funnel{size=N})  -> N;
 | 
	
		
			
				|  |  | -in_arity(#switch{in=In})   -> In;
 | 
	
		
			
				|  |  | -in_arity(#loop{in=In})     -> In.
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -out_arity(#func{})            -> 1;
 | 
	
		
			
				|  |  | -out_arity(#value{})           -> 1;
 | 
	
		
			
				|  |  | -out_arity(#dynamic{})         -> unknown;
 | 
	
		
			
				|  |  | -out_arity(#route{out=Out})    -> Out;
 | 
	
		
			
				|  |  | -out_arity(#pipe{out=Out})     -> Out;
 | 
	
		
			
				|  |  | -out_arity(#parallel{out=Out}) -> Out;
 | 
	
		
			
				|  |  | -out_arity(#sequence{out=Out}) -> Out;
 | 
	
		
			
				|  |  | -out_arity(#split{size=N})     -> N;
 | 
	
		
			
				|  |  | -out_arity(#merge{})           -> 1;
 | 
	
		
			
				|  |  | -out_arity(#funnel{})          -> 1;
 | 
	
		
			
				|  |  | -out_arity(#switch{out=Out})   -> Out;
 | 
	
		
			
				|  |  | -out_arity(#loop{out=Out})     -> Out.
 | 
	
		
			
				|  |  | +in_arity(#func{in=In})   -> In;
 | 
	
		
			
				|  |  | +in_arity(#value{})       -> 0;
 | 
	
		
			
				|  |  | +in_arity(#pipe{in=In})   -> In;
 | 
	
		
			
				|  |  | +in_arity(#par{in=In})    -> In;
 | 
	
		
			
				|  |  | +in_arity(#route{in=In})  -> In;
 | 
	
		
			
				|  |  | +in_arity(#sync{size=N})  -> N;
 | 
	
		
			
				|  |  | +in_arity(#split{})       -> 1;
 | 
	
		
			
				|  |  | +in_arity(#merge{size=N}) -> N;
 | 
	
		
			
				|  |  | +in_arity(#switch{in=In}) -> In.
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +out_arity(#func{})          -> 1;
 | 
	
		
			
				|  |  | +out_arity(#value{})         -> 1;
 | 
	
		
			
				|  |  | +out_arity(#pipe{out=Out})   -> Out;
 | 
	
		
			
				|  |  | +out_arity(#par{out=Out})    -> Out;
 | 
	
		
			
				|  |  | +out_arity(#route{out=Out})  -> Out;
 | 
	
		
			
				|  |  | +out_arity(#sync{size=N})    -> N;
 | 
	
		
			
				|  |  | +out_arity(#split{size=N})   -> N;
 | 
	
		
			
				|  |  | +out_arity(#merge{})         -> 1;
 | 
	
		
			
				|  |  | +out_arity(#switch{out=Out}) -> Out.
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  flatten_arity([First|Rest]) ->
 | 
	
		
			
				|  |  |      lists:foldl(fun(Op, {In, Out}) ->
 | 
	
		
			
				|  |  | -			{add_arity(In, in_arity(Op)),
 | 
	
		
			
				|  |  | -			 add_arity(Out, out_arity(Op))}
 | 
	
		
			
				|  |  | +			{In + in_arity(Op), Out + out_arity(Op)}
 | 
	
		
			
				|  |  |  	  end,
 | 
	
		
			
				|  |  |  	  {in_arity(First), out_arity(First)}, Rest).
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -add_arity(unknown, _) -> unknown;
 | 
	
		
			
				|  |  | -add_arity(_, unknown) -> unknown;
 | 
	
		
			
				|  |  | -add_arity(N, M)       -> N + M.
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -known_arity(N, N)       -> N;
 | 
	
		
			
				|  |  | -known_arity(unknown, N) -> N;
 | 
	
		
			
				|  |  | -known_arity(N, unknown) -> N.
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -known_op_arity(Op, In, Out) ->
 | 
	
		
			
				|  |  | -    {known_arity(in_arity(Op), In), known_arity(out_arity(Op), Out)}.
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |  can_connect(Op1, Op2) ->
 | 
	
		
			
				|  |  |      case {out_arity(Op1), in_arity(Op2)} of
 | 
	
		
			
				|  |  |  	{N,       N} -> true;
 | 
	
		
			
				|  |  | -	{unknown, _} -> true;
 | 
	
		
			
				|  |  |  	_            -> false
 | 
	
		
			
				|  |  |      end.
 |