with
  DC_Keyb,
  DC_Scrn;

use
  DC_Keyb,
  DC_Scrn;

package body ANN6S is
---------------------------------------------------------------------------
---------------------------------------------------------------------------

procedure Net_Settle (
	    States  : in out States_Type;
	    Weights : in out Weights_Type;
	    Average :    out States_Type;
	    Train   : in     boolean := false ) is
---------------------------------------------------------------------------
  Totals : Array_Integer ( States'range ) := ( others => 0 );
  Visited : Array_Boolean ( 0..( ( 2 ** States'last ) - 1 ) )
    := ( others => false );
  Cycling : boolean := false;
  Count : integer := 0;
  State_Int : integer;
begin
  loop
    Array_Boolean_Show ( States  );
    Array_Float_2_Show ( Weights );
    Net_Update ( States, Weights, Train );
    State_Int := Array_Boolean_To_Integer ( States );
    -- first time hit = nothing, 2nd time = start averaging, 3rd = stop
    Cycling := Cycling or Visited ( State_Int );
    Visited ( State_Int ) := not ( Visited ( State_Int ) );
    if Cycling then
      exit when Visited ( State_Int );
      Count := Count + 1;
      Boolean_Average ( Totals, States, Count, Average );
    end if;
  end loop;
end Net_Settle;

procedure Net_Update
	    ( States  : in out States_Type;
	      Weights : in out Weights_Type;
	      Train   : in     boolean := false ) is
---------------------------------------------------------------------------
  Weighted_Sum : float;
  Temp : States_Type ( States'range );
begin
  for State in States'range loop
    Weighted_Sum := 0.0;
    for Weight in Weights'range loop
      if States ( Weight ) then
	Weighted_Sum := Weighted_Sum + Weights ( State, Weight );
      end if;
    end loop;
    Temp ( State ) := Weighted_Sum >= Threshhold;
    if Train then
      Weights_Train ( State, States, Temp ( State ), Weights );
    end if;
  end loop;
  States := Temp;
end Net_Update;

function  Patterns_Fetch return Patterns_Type is
---------------------------------------------------------------------------
begin
  return ( ( false, false, false ),
	   ( false, true , true  ),
	   ( true , false, true  ),
	   ( true , true , false ) );
end Patterns_Fetch;

procedure Start is
---------------------------------------------------------------------------
  Inputs_Count : positive := 2;
  Neuron_Count : positive := Inputs_Count;
  Weights : Weights_Type ( 1..Neuron_Count, 1..Neuron_Count )
    := ( others => ( others => - float ( Neuron_Count - 1 ) ) );
  States : States_Type ( 1..Neuron_Count ) := ( others => false );
  Average : States_Type ( 1..Neuron_Count );
  Train : boolean := false;
  Patterns_Count : constant := 4;
  Outputs_Count  : positive := 1;
  Patterns : Patterns_Type ( 1..Patterns_Count,
    1..( Inputs_Count + Outputs_Count ) ) := Patterns_Fetch;
begin
  for Pattern in Patterns'range loop
    loop
      for Input in 1..Inputs_Count loop
	States ( Input ) := Patterns ( Pattern, Input );
      end loop;
      Net_Settle ( States, Weights, Average, Train );
      Put_Line ( "Average states once it started repeating:" );
      Array_Boolean_Show ( Average );
      exit when Average ( 1..Outputs_Count ) = Patterns ( Pattern ) ( 1..Outputs_Count );
      Train := not Train;
      Pause;
    end loop;
  end loop;
end Start;

procedure Weight_Add ( Weight : in out float; Neuron_Count: in integer ) is
---------------------------------------------------------------------------
begin
  Weight := Weight + 1.0 / float ( Neuron_Count - 1 );
  if Weight > Threshhold then
    Weight := Threshhold;
  end if;
end Weight_Add;

procedure Weight_Sub ( Weight : in out float; Neuron_Count: in integer ) is
---------------------------------------------------------------------------
begin
  Weight := Weight - 1.0 / float ( Neuron_Count - 1 );
  if Weight < 0.0 then
    Weight := - float ( Neuron_Count - 1 ) * Threshhold;
  end if;
end Weight_Sub;

procedure Weights_Train
	    ( State_Num  : in     integer;
	      States_Old : in     States_Type;
	      State_New  : in     boolean;
	      Weights    : in out Weights_Type ) is
---------------------------------------------------------------------------
begin
  for Weight in Weights'range loop
  --if not diagonal inhibition row
    if Weight /= State_Num then
    --if neuron fires & fresh, weight triggered & excitatory, add
      if State_New then                                     -- fires
	if not States_Old ( State_Num ) then                -- fresh
	  if States_Old ( Weight ) then                     -- triggered
	    if Weights ( State_Num, Weight ) > 0.0 then     -- excitatory
	      Weight_Add ( Weights ( State_Num, Weight ), Weights'last );
	    end if;
	  end if;
	end if;
      end if;
    --if neuron fires & tired, weight triggered & excitatory, subtract
      if State_New then                                     -- fires
	if States_Old ( State_Num ) then                    -- tired
	  if States_Old ( Weight ) then                     -- triggered
	    if Weights ( State_Num, Weight ) > 0.0 then     -- excitatory
	      Weight_Sub ( Weights ( State_Num, Weight ), Weights'last );
	    end if;
	  end if;
	end if;
      end if;
    --if neuron sits & fresh, weight triggered & not excitatory, add
      if not State_New then                              -- sits
	if not States_Old ( State_Num ) then             -- fresh
	  if States_Old ( Weight ) then                  -- triggered
	    if Weights ( State_Num, Weight ) <= 0.0 then -- not excitatory
	      Weight_Add ( Weights ( State_Num, Weight ), Weights'last );
	    end if;
	  end if;
	end if;
      end if;
    end if;
  end loop;
end Weights_Train;

---------------------------------------------------------------------------
---------------------------------------------------------------------------
end ANN6S;
