defmodule Scopes.CSys.Program do require Logger alias Scopes.CSys alias Scopes.CSys.Environ alias Scopes.Shape defmodule State do defstruct [:value, :count, :stage, :prog] end def prepare(prog, args) do state = %State{ value: args[:bias], count: 0, stage: :initial, prog: prog } {state, get_proc(state)} end def get_proc(state) do get_proc(elem(state.prog, 0), state.stage) end defp get_proc(stages, stage) do stages[stage] || stages[:default] end # basic program def basic_prog(args) do default = basic_active(args) stages = %{ default: default, initial: basic_initial(args), active: default, retired: default } transitions = %{ restart: :initial, retire: :retired, next: [initial: :active, active: :retired] } {stages, transitions} end def prepare_basic(args \\ [threshold: 0, bias: 0]) do prepare(basic_prog(args), args) end # basic processors def basic_initial(args) do basic(args) end def basic_active(args) do basic(args) end #def basic(msg, scope) do # + proc_ops def basic(args) do # + proc_ops fn msg, scope -> case Shape.head(msg) do [:csys, :data | _rest] -> process_data(msg, scope, args) #[:csys, :trigger | _rest] -> process_trigger(msg, scope) [:csys, :set | _rest] -> set(msg, scope) [:csys, :connect | _rest] -> CSys.connect(msg, scope) [:csys, :create, :succ | _rest] -> create_succ(msg, scope) [:csys, :create, :pred | _rest] -> create_pred(msg, scope) #[:csys, :next | _rest] -> next(scope, meta) #[:csys, action | rest] -> transition(action, scope) _ -> CSys.forward(msg, scope) || CSys.notify(msg, scope) end end end # message handlers // proc steps def process_value(_msg, _scope, _ops) do #proc ops: #reduce, (&(&1 + &2)) #trigger_cond, (&(&1 > 0)) #out_op, (&(&1)) # (&(rem(&1 - 1, 5) + 1) #output (&forward_or_notify) #next_op, (const(0)) # (&max(&1 - 5, 0)) end def process_data(msg, scope, args) do data = Shape.data(msg) state = CSys.state(scope) value_n = state.value + data.value threshold = args[:threshold] || 0 bias = args[:bias] || 0 Logger.info data: data.value, state: state.value, threshold: threshold if value_n >= threshold do msg = Shape.create(Shape.head(msg), data: %{data | value: value_n}) CSys.forward(msg, scope) || CSys.notify(msg, scope) state_n = if bias, do: %{state | value: bias}, else: state CSys.update(put_elem(scope, 0, state_n)) else state_n = %{state | value: value_n} CSys.update(put_elem(scope, 0, state_n)) end end def set(msg, scope) do data = Shape.data(msg) state = CSys.state(scope) value = data[:value] || state.value threshold = data[:threshold] || state.threshold bias = data[:bias] || state.bias state_n = %{state | value: value, threshold: threshold, bias: bias} CSys.update(put_elem(scope, 0, state_n)) end def create_succ(msg, scope) do new = CSys.create(msg, restart(scope)) data = Shape.data(msg) CSys.send_message(self(), ~w(csys connect)a, Map.put(data, :target, new)) end def create_pred(msg, scope) do new = CSys.create(msg, restart(scope)) data = Shape.data(msg) CSys.send_message(new, ~w(csys connect)a, Map.put(data, :target, self())) end # synapse operations def multiply(n) do fn msg -> value = Shape.data(msg)[:value] || 0 Shape.create(Shape.head(msg), data: %{Shape.data(msg) | value: n * value}) end end def negate(), do: multiply(-1) # helper functions def restart({state, _proc, syns, env}) do state_n = %{state | stage: :initial} {state_n, get_proc(state_n), syns, env} end # demo init sequences def init_seq_b1() do zero = [:csys, :c00, {0, 0}] one = [:csys, :s01, {1, 0}] two = [:csys, :c00, {1, 1}] three = [:csys, :s01, {1, 1}] [ fn state -> Environ.send_message(state, zero, ~w(csys create pred)a, %{op: [CSys.data_only(), negate()], addr: one}) end, fn state -> Environ.send_message(state, one, ~w(csys create succ)a, %{op: CSys.data_only(), addr: two}) end, fn state -> Environ.send_message(state, two, ~w(csys create pred)a, %{addr: three}) end, fn state -> Environ.connect(state, three, zero) end ] end def init_recursive_1() do zero = [:csys, :c00, {0, 0}] [ fn state -> Environ.connect(state, zero, zero, negate()) Environ.send_message(state, zero, ~w(csys create succ)a, %{addr: [:csys, :e01, {1, 1}]}) end, fn state -> Environ.send_message(state, zero, ~w(csys create pred)a, %{addr: [:csys, :s01, {1, 1}]}) end ] end end