function varargout = Attractor_Network(varargin)

gui_Singleton = 1;
gui_State = struct('gui_Name',       mfilename, ...
                   'gui_Singleton',  gui_Singleton, ...
                   'gui_OpeningFcn', @Attractor_Network_OpeningFcn, ...
                   'gui_OutputFcn',  @Attractor_Network_OutputFcn, ...
                   'gui_LayoutFcn',  [] , ...
                   'gui_Callback',   []);
if nargin && ischar(varargin{1})
    gui_State.gui_Callback = str2func(varargin{1});
end

if nargout
    [varargout{1:nargout}] = gui_mainfcn(gui_State, varargin{:});
else
    gui_mainfcn(gui_State, varargin{:});
end
% End initialization code - DO NOT EDIT


% --- Executes just before Attractor_Network is made visible.
function Attractor_Network_OpeningFcn(hObject, eventdata, handles, varargin)
% This function has no output args, see OutputFcn.
handles.output = hObject;

% Update handles structure
guidata(hObject, handles);

% setting up grid
set(handles.grid, 'XTick', [1:13]./13);
set(handles.grid, 'YTick', [1:12]./12);
set(handles.grid, 'Color', get(0,'defaultUicontrolBackgroundColor'));
xlim(handles.grid, [0 1]);
ylim(handles.grid, [0 1]);

% declaring global variables
global colourMatrix; 
colourMatrix=zeros(12,13)-1; % holds current colours of grid; -1 is gray & 1 is red
global nStoredPatterns; nStoredPatterns=0; % number of stored patterns
global ntime; ntime=0; % number of times updated
global StoredPatterns; StoredPatterns=zeros(156,1)-1; % matrix to hold stored patterns


% --- Outputs from this function are returned to the command line.
function varargout = Attractor_Network_OutputFcn(hObject, eventdata, handles) 
varargout{1} = handles.output;


function LearnButton_Callback(hObject, eventdata, handles)
% stores current grid pattern and updates number of stored patterns
global nStoredPatterns; nStoredPatterns=nStoredPatterns+1;
global StoredPatterns; global colourMatrix;
StoredPatterns(:,nStoredPatterns)=reshape(colourMatrix,156,1);
set(handles.StoredPatternsLabel,'String',['Stored Patterns: ', num2str(nStoredPatterns)]);


function ApplyLetterButton_Callback(hObject, eventdata, handles)
% applies specified letter to the grid. inputted letter must be a capital.
global ntime; ntime=0;
set(handles.TimeLabel,'String',['Times Updated: ',num2str(ntime)]);
global colourMatrix;
load pattern1; % pattern1 is matrix of 26 letters
pattern1=pattern1*2-1; % converting 0s and 1s to -1s and 1s, respectively
letter=get(handles.LetterInput,'String');
error=false;
switch letter; 
    % getting letter and finding corresponding indicies where the letter
    % can be found in pattern 1
    case 'A'; index=12;
    case 'B'; index=24;
    case 'C'; index=36;
    case 'D'; index=48;
    case 'E'; index=60;
    case 'F'; index=72;
    case 'G'; index=84;
    case 'H'; index=96;
    case 'I'; index=108;
    case 'J'; index=120;
    case 'K'; index=132;
    case 'L'; index=144;
    case 'M'; index=156;
    case 'N'; index=168;
    case 'O'; index=180;
    case 'P'; index=192;
    case 'Q'; index=204;
    case 'R'; index=216;
    case 'S'; index=228;
    case 'T'; index=240;
    case 'U'; index=252;
    case 'V'; index=264;
    case 'W'; index=276;
    case 'X'; index=288;
    case 'Y'; index=300;
    case 'Z'; index=312;
    otherwise
        error=true;
end

if error==false
    % clearing the grid to prevent rectangles to build up and slow down the
    % program
    ClearButton_Callback;
    letterPattern=pattern1(index-11:index,:); % extracting pattern from pattern 1
    
    for rows=1:12
        for cols=1:13
            if colourMatrix(rows,cols)~=letterPattern(rows,cols);
                % changing colourMatrix to match letterPattern
                changeColour((cols-1)/13,(12-rows)/12,rows,cols);
            end
        end
    end
    set(handles.errorText,'String',''); 
else
    % if letter input is invalid, gives error
    set(handles.errorText,'String','Input must be a capital letter');
end

function EnterPatternButton_Callback(hObject, eventdata, handles)
% allows user to manually enter a pattern
global ntime; ntime=0;
set(handles.TimeLabel,'String',['Times Updated: ',num2str(ntime)]);
while get(handles.EnterPatternButton,'Value')==1
    [x,y]=ginput(1);
    if x<0||x>1||y<0||y>1
        set(handles.EnterPatternButton,'Value',0);        
    else
        coordx=(1/13)*(floor(x/(1/13))); % finding grid x coordinate
        coordy=(1/12)*(floor(y/(1/12))); % finding grid y coordinate
        row=13-ceil(y*12); % finding corresponding row in colourMatrix
        col=ceil(x*13); % finding corresponding column in colourMatrix
        changeColour(coordx,coordy,row,col); % changing grid square colour
    end
end

 
function changeColour(xCoord,yCoord,row,col)
% changes the current colour of the specified square and its corresponding
% colourMatrix index.
global colourMatrix;
colourMatrix(row,col)=colourMatrix(row,col)*-1;
if colourMatrix(row,col)==1; % if was gray, paint red
    rectangle('Position', [xCoord, yCoord, 1/13, 1/12], 'FaceColor', [1 0 0]);
else % if it was red, paint gray
    rectangle('Position', [xCoord, yCoord, 1/13, 1/12], 'FaceColor', get(0,'defaultUicontrolBackgroundColor'));
end


function UpdateButton_Callback(hObject, eventdata, handles)
global colourMatrix; global StoredPatterns; global ntime;

pattern=reshape(colourMatrix,156,1); % initial pattern
weights=StoredPatterns*StoredPatterns'; % hebbian learning 
for t=2:10; 
    pattern(:,t)=sign(weights*pattern(:,t-1)); 
end % updating network
overlaps=pattern'*StoredPatterns/156; % finding overlaps between pattern and storedPattern to find best match
[~,bestPattern]=max(abs(overlaps(t,:))); % finding index of best matched stored pattern

% clearing grid so it doesn`t get clogged up.
ClearButton_Callback;

% changing colourMatrix to match the best matched pattern, and applying it
% to the grid.
colourMatrix=reshape(StoredPatterns(:,bestPattern),12,13)*((overlaps(t,bestPattern)>0)*2-1);
for rows=1:12
    for cols=1:13
        if colourMatrix(rows,cols)==1
             rectangle('Position', [(cols-1)/13, (12-rows)/12, 1/13, 1/12], 'FaceColor', [1 0 0]);
        end
    end
end
% updating number of times updated.
ntime=ntime+1;
set(handles.TimeLabel,'String',['Times Updated: ', num2str(ntime)]);

function ClearButton_Callback(hObject, eventdata, handles)
% clears colourMatrix and the grid.
global colourMatrix; colourMatrix=zeros(12,13)-1;
cla;


function PercentInput_Callback(hObject, eventdata, handles)
% if an invalid percentage is inputted, gives error
if str2num(get(handles.PercentInput,'String'))<0 || str2num(get(handles.PercentInput,'String'))>100
set(handles.errorText,'String','Invalid Percentage input');
else % updates Percent Slider to match current value
    set(handles.PercentSlider,'Value',str2num(get(handles.PercentInput,'String')));
    set(handles.errorText,'String','');
end
function PercentInput_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end

function LetterInput_Callback(hObject, eventdata, handles)
function LetterInput_CreateFcn(hObject, eventdata, handles)
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end

function PercentSlider_Callback(hObject, eventdata, handles)
% sets PercentInput to match current value
set(handles.PercentInput,'String',num2str(get(handles.PercentSlider,'Value')));
set(handles.errorText,'String','');
function PercentSlider_CreateFcn(hObject, eventdata, handles)
if isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor',[.9 .9 .9]);
end

function ApplyNoiseButton_Callback(hObject, eventdata, handles)
global ntime; ntime=0;
if str2num(get(handles.PercentInput,'String'))~=0
    set(handles.TimeLabel,'String',['Times Updated: ',num2str(ntime)]);
end
global colourMatrix;
random=randperm(156);
percent=round(str2num(get(handles.PercentInput,'String'))*1.56);
if percent<=156 && percent>=0
% clearing axes before noise is applied. If this is not done, the drawn
% rectangles build up over time and will slow down the program.
cla;
for rows=1:12;  
    for cols=1:13
        if colourMatrix(rows,cols)==1
              rectangle('Position', [(cols-1)/13, (12-rows)/12, 1/13, 1/12], 'FaceColor', [1 0 0]);
        end
    end
end
% temporary matrix noisedMatrix describes how the grid SHOULD look after
% noise is applied.
noisedMatrix=reshape(colourMatrix,156,1);
for i=1:percent; noisedMatrix(random(i))=noisedMatrix(random(i))*-1; end
noisedMatrix=reshape(noisedMatrix,12,13);
% making neccesary changes to grid and changing colourMatrix to match noisedMatrix
for rows=1:12; 
    for cols=1:13;
        if colourMatrix(rows,cols)~=noisedMatrix(rows,cols)
            changeColour((cols-1)/13,(12-rows)/12,rows,cols);
        end
    end
end
colourMatrix=noisedMatrix;
clear noisedMatrix;
set(handles.errorText,'String','');
else
    % error is invalid percentage has been inputted
    set(handles.errorText,'String','Invalid Percentage Input');
end


function ResetButton_Callback(hObject, eventdata, handles)
% resets all parameters, clears grid using ClearButton_Callback
global ntimes; global nStoredPatterns; global StoredPatterns;
ntimes=0; nStoredPatterns=0;
set(handles.TimeLabel,'String',['Times Updated: ', num2str(ntimes)]);
set(handles.StoredPatternsLabel,'String',['Stored Patterns: ', num2str(nStoredPatterns)]);
StoredPatterns=zeros(156,1)-1;
set(handles.PercentSlider,'Value',0);
set(handles.LetterInput,'String','');
set(handles.PercentInput,'String',num2str(0));

ClearButton_Callback();

function CloseButton_Callback(hObject, eventdata, handles)
% closes the GUI, close all, clear all;
close(handles.figure1);
close all; clear all;
