function TCM_linear_program

% TCM_LINEAR_PROGRAM Interactive illustration of the simplex method for solving 
%   a linear program.
%
%   TCM_LINEAR_PROGRAM illustrates the feasible region of a linear program
%   defined by constraints that are either inputted numerically or
%   graphically. Afterwards the modification steps of the simplex algorithm 
%   of a feasible solution is shown.
%
%   EXAMPLES
%
%   Basic Example: For a convex, bounded and nonempty feasible region, there 
%   always exists an optimal solution.
%
%   Edge Solution: If the normal vector of a constraint is parallel to the
%   search direction, the minimal value can be found on a total edge.
% 
%   Unbounded Region: An optimal solution can even exists if the feasible 
%   region is unbounded.
%
%   To investigate other cases, insert a function or other constraints 
%   in the corresponding fields.
%
%   TCM__LINEAR_PROGRAM is part of the <a href="http://www2.imng.uni-stuttgart.de/LstNumGeoMod/TCM/">TCM</a> project.
%   Related course materials can be found at <a href="http://www.mathematics-online.org/inhalt/aussage/aussage690/">Mathematics Online</a>.

% Authors: Florian Martin, Lars von Wolff
% Last updated: 15.11.2017

% Initialize data
TCM_Data.move = 0;
TCM_Data.mode = 1;
TCM_Data.A = [];
TCM_Data.b = [];
TCM_Data.f = [];
TCM_Data.points = [];
TCM_Data.click_point = [];
TCM_Data.sol = [];
TCM_Data.intersections = {[]};

% Do not delete these Variables
TCM_Data.plot_window = [-2 1 0 3];
TCM_Data.LmbMenuString = '';
TCM_Data.examples = {'Basic Example','Edge Solution','Unbounded Region'};

% Layout:
% ---------------------------------------
% |               Title                 |
% | ------------ --------- ------------ |
% | | Settings | |Buttons| | Feedback | |
% | ------------ --------- ------------ |
% | ----------------------------------- |
% | |           Axis (Plot)           | |
% | ----------------------------------- |
% | Info-Button         Hint            |
% ---------------------------------------
% Initialize Layout:
TCM_UI.TitleHeightMin = 10;        % minimal height of the title
TCM_UI.TitleHeightMax = 32;        % maximal height of the title
TCM_UI.HintHeightMin = 8;          % minimal height of the hint
TCM_UI.HintHeightMax = 15;         % maximal height of the hint
TCM_UI.SettingsHeightMin = 40;     % minimal height of the feedback, buttons and 
                                   % setting panel
TCM_UI.SettingsHeightMax = 70;     % maximal height of the feedback, buttons and 
                                   % setting panel
TCM_UI.SettingsRatio = 4.5;          % aspect ratio of the settings  
                                   % (increase if more settings needed)
TCM_UI.ButtonsRatio = 1.4;         % aspect ratio of the Buttons panel
TCM_UI.FeedbackRatio = 3.4;        % aspect ratio of the feedback
TCM_UI.AxisHeightTarget = 0.73;    % desired fraction of figure 
                                   % used for the plot
TCM_UI.InfoPortion = 1/8;          % Portion of the Info Button in the last  
                                   % line 
TCM_UI.Title = 'Linear Program';
TCM_UI.mono = get(0,'FixedWidthFontName');
% Strings for generating the information window
TCM_UI.Author = 'F. Martin and L. von Wolff';
TCM_UI.Date = '15.11.2017';
TCM_UI.Web = 'http://www.mathematics-online.org/inhalt/aussage/aussage690/';
      
CreateUI();

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% UI-Elements in Settings %%%%%%%%%%%%%%%%%%%%
% Text for Function
TCM_UI.funtext = uicontrol('Units','normalized','Parent',TCM_UI.settingspanel, ...
        'BackgroundColor',get(gcf,'Color'), ...
        'FontUnits','normalized','FontSize',0.67,...
        'HorizontalAlignment','left', ...
	'Position',[.01,.70,.29,.30],...
	'String','Function:',...
	'Style','text');

% Input for Function
TCM_UI.fun1input=uicontrol('Units','normalized', 'Parent',TCM_UI.settingspanel, ...
        'BackgroundColor',get(gcf,'Color'), ...
        'FontWeight','bold',...
        'FontUnits','normalized','FontSize',0.65,'FontName',TCM_UI.mono,...
        'HorizontalAlignment','left', ...
	'Position',[.38,.70,.14,.30],...
	'String','0',...
	'Style','edit',...
	'Callback',@function_update);

TCM_UI.xtext = uicontrol('Units','normalized', 'Parent',TCM_UI.settingspanel, ...
        'BackgroundColor',get(gcf,'Color'), ...
        'FontUnits','normalized','FontSize',0.67,...
        'HorizontalAlignment','left', ...
	'Position',[.52,.70,.1,.30],...
	'String','*x+',...
	'Style','text');

TCM_UI.fun2input=uicontrol('Units','normalized', 'Parent',TCM_UI.settingspanel, ...
        'BackgroundColor',get(gcf,'Color'), ...
        'FontWeight','bold',...
        'FontUnits','normalized','FontSize',0.65,'FontName',TCM_UI.mono,...
        'HorizontalAlignment','left', ...
	'Position',[.59,.70,.14,.30],...
	'String','0',...
	'Style','edit',...
	'Callback',@function_update);

TCM_UI.secondtext = uicontrol('Units','normalized', 'Parent',TCM_UI.settingspanel, ...
        'BackgroundColor',get(gcf,'Color'), ...
        'FontUnits','normalized','FontSize',0.67,...
        'HorizontalAlignment','left', ...
	'Position',[.73,.70,.3,.30],...
	'String','*y -> min',...
	'Style','text');

% Text for integral interval 
TCM_UI.constrtext = uicontrol('Units','normalized', 'Parent',TCM_UI.settingspanel, ...
        'BackgroundColor',get(gcf,'Color'), ...
        'FontUnits','normalized','FontSize',0.67,...
        'HorizontalAlignment','left', ...
	'Position',[.01,.35,.4,.30],...
	'String','Constraint Input:',...
	'Style','text');

% Input the integral interval
TCM_UI.numbutton=uicontrol('Units','normalized', 'Parent',TCM_UI.settingspanel, ...
        'BackgroundColor',get(gcf,'Color'), ...
        'FontWeight','bold',...
        'FontUnits','normalized','FontSize',0.67,...
        'HorizontalAlignment','left', ...
	'Position',[.38,.35,.4,.30],...
	'String','Numerical','Value',1,...
	'Style','radiobutton','Callback',{@changemode,1});

TCM_UI.grbutton=uicontrol('Units','normalized', 'Parent',TCM_UI.settingspanel, ...
        'BackgroundColor',get(gcf,'Color'), ...
        'FontWeight','bold',...
        'FontUnits','normalized','FontSize',0.67,...
        'HorizontalAlignment','left', ...
	'Position',[.7,.35,.4,.30],...
	'String','Graphical','Value',0,...
	'Style','radiobutton','Callback',{@changemode,2});

% Input for Function
TCM_UI.ainput=uicontrol('Units','normalized', 'Parent',TCM_UI.settingspanel, ...
        'BackgroundColor',get(gcf,'Color'), ...
        'FontWeight','bold',...
        'FontUnits','normalized','FontSize',0.65,'FontName',TCM_UI.mono,...
        'HorizontalAlignment','left', ...
	'Position',[.38,.0,.14,.30],...
	'String','',...
	'Style','edit');

% Text for partition
TCM_UI.firsttext = uicontrol('Units','normalized', 'Parent',TCM_UI.settingspanel, ...
        'BackgroundColor',get(gcf,'Color'), ...
        'FontUnits','normalized','FontSize',0.67,...
        'HorizontalAlignment','left', ...
	'Position',[.52,.00,.1,.30],...
	'String','*x+',...
	'Style','text');

% Input for Function
TCM_UI.binput=uicontrol('Units','normalized', 'Parent',TCM_UI.settingspanel, ...
        'BackgroundColor',get(gcf,'Color'), ...
        'FontWeight','bold',...
        'FontUnits','normalized','FontSize',0.65,'FontName',TCM_UI.mono,...
        'HorizontalAlignment','left', ...
	'Position',[.59,.0,.14,.30],...
	'String','',...
	'Style','edit');

% Text for partition
TCM_UI.secondtext = uicontrol('Units','normalized', 'Parent',TCM_UI.settingspanel, ...
        'BackgroundColor',get(gcf,'Color'), ...
        'FontUnits','normalized','FontSize',0.67,...
        'HorizontalAlignment','left', ...
	'Position',[.73,.00,.13,.30],...
	'String','*y <=',...
	'Style','text');

% Input for Function
TCM_UI.cinput=uicontrol('Units','normalized', 'Parent',TCM_UI.settingspanel, ...
        'BackgroundColor',get(gcf,'Color'), ...
        'FontWeight','bold',...
        'FontUnits','normalized','FontSize',0.65,'FontName',TCM_UI.mono,...
        'HorizontalAlignment','left', ...
	'Position',[.85,.0,.14,.30],...
	'String','',...
	'Style','edit',...
	'Callback',@add_constraint);


%%%%%%%%%%%%%%%%%%%%%%%%% UI-Elements in Buttons Panel %%%%%%%%%%%%%%%%%%%%
% Reset-Button
TCM_UI.reset=uicontrol('Units','normalized','Parent',TCM_UI.buttonspanel, ...
	'BackgroundColor',get(gcf,'Color'), ...
	'String','Reset', ...
    'FontUnits','normalized','FontSize',0.7,...
	'Position',[.01,.70,.98,.30], ...
	'Callback',@reset,...
	'Style','pushbutton');

% Example button
TCM_UI.examples=uicontrol('Units','normalized','Parent',TCM_UI.buttonspanel, ...
	'BackgroundColor',get(gcf,'Color'), ...
	'String','Examples', ...
    'FontUnits','normalized','FontSize',0.7,...
	'Position',[.01,.35,.98,.30], 'Callback',@OpenExampleMenu,...
	'Style','pushbutton');

% Next-Button
TCM_UI.solve=uicontrol('Units','normalized','Parent',TCM_UI.buttonspanel, ...
	'BackgroundColor',get(gcf,'Color'), ...
	'String','Solve', ...
    'FontUnits','normalized','FontSize',0.7,...
	'Position',[.01,.0,.98,.30], ...
	'Callback',@solve,...
	'Style','pushbutton','Enable','Off');

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% UI-Elements in Feedback %%%%%%%%%%%%%%%%%%%%
% Text for sum
TCM_UI.feastext=uicontrol('Units','normalized','Parent',TCM_UI.feedbackpanel,...
        'BackgroundColor',get(gcf,'Color'), ...
        'HorizontalAlignment','left', ...
        'FontUnits','normalized','FontSize',0.7,...
	'Position',[.01,.7,.9,.3],...
	'String','No feasible solutions.',...
	'Style','text','visible','off');

% Text for sum
TCM_UI.unbtext=uicontrol('Units','normalized','Parent',TCM_UI.feedbackpanel,...
        'BackgroundColor',get(gcf,'Color'), ...
        'HorizontalAlignment','left', ...
        'FontUnits','normalized','FontSize',0.7,...
	'Position',[.01,.7,.9,.3],...
	'String','Unbounded solution.',...
	'Style','text','visible','off');

% Text for error
TCM_UI.mintext=uicontrol('Units','normalized','Parent',TCM_UI.feedbackpanel,...
        'BackgroundColor',get(gcf,'Color'), ...
        'HorizontalAlignment','left', ...
        'FontUnits','normalized','FontSize',0.7,...
	'Position',[.01,.7,.9,.3],...
	'String','Minimal value',...
	'Style','text');

TCM_UI.attext=uicontrol('Units','normalized','Parent',TCM_UI.feedbackpanel,...
        'BackgroundColor',get(gcf,'Color'), ...
        'HorizontalAlignment','left', ...
        'FontUnits','normalized','FontSize',0.7,...
	'Position',[.01,.35,.9,.3],...
	'String','at point',...
	'Style','text');

TCM_UI.soltext=uicontrol('Units','normalized','Parent',TCM_UI.feedbackpanel,...
        'BackgroundColor',get(gcf,'Color'), ...
        'HorizontalAlignment','left', ...
        'FontUnits','normalized','FontSize',0.7,...
	'Position',[.01,.01,.9,.3],...
	'String','',...
	'Style','text');

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% UI-Elements in Axis %%%%%%%%%%%%%%%%%%%%%%%%
TCM_UI.ax=axes('Position',[.05,.1,.9,.85],'Parent',TCM_UI.axispanel);
axis(TCM_UI.ax,'equal');
TCM_UI.modification = uicontextmenu('Callback',@reset_zoom_limits); 
TCM_UI.mpan = uimenu('Parent',TCM_UI.modification,'Label','Enable pan',...
        'Callback',@axis_context);
TCM_UI.mzoomi = uimenu('Parent',TCM_UI.modification,'Label','Enable zoom in',...
    'Callback',@axis_context); 
TCM_UI.mzoomo = uimenu('Parent',TCM_UI.modification,'Label','Enable zoom out',...
        'Callback',@axis_context); 
TCM_UI.ax.UIContextMenu = TCM_UI.modification;

% functions for smoothless zooming and panning
panfun = pan(TCM_UI.fig);
panfun.UIContextMenu = TCM_UI.modification;
set(panfun,'ActionPostCallback',@pan_modify);
zoomfun = zoom(TCM_UI.fig);
zoomfun.UIContextMenu = TCM_UI.modification; 
set(zoomfun,'ActionPostCallback',@zoom_modify);
set(zoomfun,'ButtonDownFilter',@set_zoom_limits);
set(zoomfun,'RightClickAction','PostContextMenu');
set(findall(TCM_UI.fig,'tag','Exploration.Pan'),'enable','off');
set(findall(TCM_UI.fig,'tag','Exploration.ZoomOut'),'enable','off');
set(findall(TCM_UI.fig,'tag','Exploration.ZoomIn'),'enable','off');

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Start the program %%%%%%%%%%%%%%%%%%%%%%%%%%
changeexample(1);

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%% Callback functions %%%%%%%%%%%%%%%%%%%%%%%%%%%
function changemode(varargin)
if(TCM_Data.mode ~= varargin{end})
    TCM_Data.mode = varargin{end};
end
UpdateModeButtons;
end

function UpdateModeButtons
if(TCM_Data.mode == 1)
    TCM_UI.numbutton.Value = 1;
    TCM_UI.grbutton.Value = 0;
    TCM_UI.ainput.Enable = 'on';
    TCM_UI.binput.Enable = 'on';
    TCM_UI.cinput.Enable = 'on';  
    TCM_Data.click_point = [];
else
    TCM_UI.grbutton.Value = 1;
    TCM_UI.numbutton.Value = 0;
    TCM_UI.ainput.Enable = 'off';
    TCM_UI.binput.Enable = 'off';
    TCM_UI.cinput.Enable = 'off';
end
UpdateHinttext();
end

% new function input
function function_update(varargin)
f = [str2double(get(TCM_UI.fun1input,'String')) str2double(get(TCM_UI.fun2input,'String'))];
if~any(isnan(f))
    TCM_Data.f = f;
    TCM_UI.solve.Enable = 'on';
    TCM_Data.sol = [];
    compute_window();
    myplot();
end
end

function add_constraint(varargin)
A = [str2double(get(TCM_UI.ainput,'String')) str2double(get(TCM_UI.binput,'String'))];
b = str2double(get(TCM_UI.cinput,'String'));
if ~any(isnan([A b]))
    if max(abs(A(1:2))) < 10^-6 && A(3) > 10^-6
        return
    elseif max(abs(A(1:2))) < 10^-6 && A(3) <= 0
        return
    else        
        TCM_Data.A = [TCM_Data.A; A];
        TCM_Data.b = [TCM_Data.b; b];
        points_update();
    end
end
end

% calculate y-range depending von the size of the function
function compute_window
if isempty(TCM_Data.points) && isempty(TCM_Data.b)
    TCM_Data.plot_window = [0 10 0 10];
else
    if isempty(TCM_Data.points) && ~isempty(TCM_Data.b)
        points = [];
        for i=1:numel(TCM_Data.b)
            axis_points = TCM_Data.b(i)./TCM_Data.A(i,:);
            number = ~isinf(axis_points) & ~isnan(axis_points);
            axis_points = [axis_points(1) 0;0 axis_points(2)];
            points = [points axis_points(:,number)];
        end
    else        
        points = TCM_Data.points;
        ind = sum(double(TCM_Data.A*points<=TCM_Data.b+10^-6)) < size(TCM_Data.A,1);
        points(:,ind) = [];
    end
    TCM_Data.plot_window = [min(points(1,:)) max(points(1,:))...
        min(points(2,:)) max(points(2,:))];
    h = diff(TCM_Data.plot_window);
    h(h < 10^-2) = 1;
    TCM_Data.plot_window = TCM_Data.plot_window + [-h(1) h(1) -h(3) h(3)]/3;
end
end

% interval input
function points_update(varargin)
new_points = [];
new_con = [TCM_Data.A(end,:) -TCM_Data.b(end)];
[~,ind] = max(abs(new_con(1:2)));
new_con = new_con/new_con(ind);
intersections = cell(size(TCM_Data.A,1));
intersections(1:size(TCM_Data.intersections,1),1:size(TCM_Data.intersections,2)) = TCM_Data.intersections;
for i=1:size(TCM_Data.A,1)-1
    con = [TCM_Data.A(i,:) -TCM_Data.b(i)];
    [~,ind] = max(abs(con(1:2)));
    con = con/con(ind);
    
    if max(abs(new_con-con)) < 10^-6
        TCM_Data.A = TCM_Data.A(1:end-1,:);
        TCM_Data.b = TCM_Data.b(1:end-1);
        return
    else
        y =(con(1)*new_con(3)-new_con(1)*con(3))/(con(2)*new_con(1)-new_con(2)*con(1));
        if ~isnan(y) && ~isinf(y)
            if abs(con(1))<10^-6
                x = -(new_con(3)+new_con(2)*y)/new_con(1);
            else
                x = -(con(3)+con(2)*y)/con(1);
            end
            new_points = [new_points [x;y]];
            intersections{i,end} = [x;y];
            intersections{end,i} = [x;y];
        end
    end    
end
TCM_Data.intersections = intersections;
TCM_Data.points = [TCM_Data.points new_points];
compute_window();
TCM_Data.sol = [];
myplot();
end


% refresh all data from UI
function reset(varargin)
window_disable.Label = 'Disable pan';
axis_context(window_disable);
TCM_Data.sol = [];
TCM_Data.click_point = [];
TCM_Data.A = [];
TCM_Data.b = [];
TCM_Data.points = [];
TCM_UI.ainput.String = '';
TCM_UI.binput.String = '';
TCM_UI.cinput.String = '';
TCM_Data.intersections = cell(1);

compute_window();
myplot;
end

% This function is called when the mouse button is clicked in the GUI.
function myDownfct(varargin)
p = getClickPosition(TCM_UI.ax);
if isnan(p) 
    return
elseif TCM_Data.mode == 2
    if isempty(TCM_Data.click_point)
        TCM_Data.click_point = p';
    else
        click_point = [TCM_Data.click_point p'];
        m = diff(click_point(2,:))/diff(click_point(1,:));
        if isinf(m)
            a = sign(diff(click_point(2,:))); b = 0;
            c = click_point(1,1)*sign(diff(click_point(2,:)));
        else
            a = m*sign(diff(click_point(1,:))); b = -sign(diff(click_point(1,:)));
            c = -sign(diff(click_point(1,:)))*(-m*click_point(1,1)+click_point(2,1));
        end
        TCM_UI.ainput.String = num2str(round(a,2));
        TCM_UI.binput.String = num2str(round(b,2));
        TCM_UI.cinput.String = num2str(round(c,2));
        TCM_Data.A = [TCM_Data.A; [a b]];
        TCM_Data.b = [TCM_Data.b;c];
        TCM_Data.move = 1;
    end    
    TCM_Data.sol = [];
    myplot;
end

end

% moving the mouse in the axis
function myMovefct(varargin)
if TCM_Data.move > 0 && TCM_Data.mode == 2 && ~isempty(TCM_Data.click_point)
    p = getClickPosition(TCM_UI.ax);
    if(isnan(p))
       return;
    end
    click_point = [TCM_Data.click_point p'];
    m = diff(click_point(2,:))/diff(click_point(1,:));
    if isinf(m)
        a = sign(diff(click_point(2,:))); b = 0;
        c = click_point(1,1)*sign(diff(click_point(2,:)));
    else
        a = m*sign(diff(click_point(1,:))); b = -sign(diff(click_point(1,:)));
        c = -sign(diff(click_point(1,:)))*(-m*click_point(1,1)+click_point(2,1));
    end
    TCM_UI.ainput.String = num2str(round(a,2));
    TCM_UI.binput.String = num2str(round(b,2));
    TCM_UI.cinput.String = num2str(round(c,2));
    TCM_Data.A = [TCM_Data.A(1:end-1,:); [a b]];
    TCM_Data.b = [TCM_Data.b(1:end-1);c];
    TCM_Data.sol = [];
    myplot;
end
end


% releasing a mouse button
function myUpfct(varargin)
if TCM_Data.move > 0
    TCM_Data.move = 0;
    TCM_Data.click_point = [];
    points_update();
    myplot;
end
end

function p = getClickPosition(myaxes)
x = get(myaxes,'xlim');
y = get(myaxes,'ylim');
newpoint=get(myaxes,'CurrentPoint');
if (newpoint(1) < x(1) || newpoint(1) > x(2) || ...
    newpoint(3) < y(1) || newpoint(3) > y(2))
    p = NaN;
else
    p = newpoint([1 3]);
end
end


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% kernel functions %%%%%%%%%%%%%%%%%%%%%%%%%%%
% compute the integration points
function solve(varargin)   
f = [TCM_Data.f -TCM_Data.f zeros(1,size(TCM_Data.A,1))];
A = [TCM_Data.A -TCM_Data.A eye(size(TCM_Data.A,1))];
b = TCM_Data.b;
ind = sign(b) == -1;
A(ind,:) = -1*A(ind,:);
b(ind) = -1*b(ind);

[X,I] = simplex([A eye(size(A,1))],b,[zeros(size(f)) ones(1,size(A,1))],size(A,2)+[1:size(A,1)]);

if isempty(X) || max(X(end-size(A,1)+1:end))>0
    TCM_Data.sol = NaN;
else    
    [solution,~] = simplex(A,b,f,I);
    if ~isinf(solution)
        TCM_Data.sol = [solution(1)-solution(3);solution(2)-solution(4)];
    else
        TCM_Data.sol = inf;
    end
end
     
myplot();
end  

function [x,I] = simplex(A,b,c,I)
[m,n] = size(A); 
E = eye(m,m);
T = inv(A(:,I)); 
T = [T, T*b];
c = c(:);

for i = 1:nchoosek(n,m)
   K = setdiff(1:n,I);
   d = c(K) - A(:,K)'*T(:,1:m)'*c(I); 
   [~,k] = min(d);
   if d(k) >= -10^-8
      x = zeros(n,1); x(I) = T(:,m+1);
      return;
   end;
   k = K(k);
   z = T(:,1:m)*A(:,k);
   J = find(z>0);
   if isempty(J)
      x = Inf;
      return;
   end;
   [~,j] = min(T(J,m+1)./z(J));
   j = J(j);
   T = (E + (E(:,j)-z)*E(j,:)/z(j)) * T;
   I(j) = k; 
end;

x = Inf;
end

% plot function
function myplot
subplot(TCM_UI.ax)
cla reset; hold on

hx = TCM_Data.plot_window(2) - TCM_Data.plot_window(1);
x = linspace(TCM_Data.plot_window(1)-hx,TCM_Data.plot_window(2)+hx,2000);
hy = TCM_Data.plot_window(4) - TCM_Data.plot_window(3);
y = linspace(TCM_Data.plot_window(3)-hy,TCM_Data.plot_window(4)+hy,2000);
[X,Y] = meshgrid(x,y);
X = X(:); Y = Y(:);
if ~isempty(TCM_Data.f) && isempty(TCM_Data.A)
    fill(x([1 end end 1 1]),y([1 1 end end 1]),'yellow');
elseif ~isempty(TCM_Data.A)
    feasible = sum(TCM_Data.A*[X';Y']<repmat(TCM_Data.b,1,numel(X)),1) == numel(TCM_Data.b);
    p = plot(X(feasible),Y(feasible),'.yellow');
end

for i=1:size(TCM_Data.A,1)
    if isempty(TCM_Data.sol) && isempty(TCM_Data.click_point) && i==size(TCM_Data.A,1) 
        color = 'blue';
    else
        color = 'black';
    end
    
    if abs(TCM_Data.A(i,2)) < 10^-6
        plot(TCM_Data.b(i)/TCM_Data.A(i,1) * ones(size(y)),y,'color',color,'LineWidth',2);
    else
        plot(x,(TCM_Data.b(i)-TCM_Data.A(i,1)*x)/TCM_Data.A(i,2),'color',color,'LineWidth',2);
    end    
end

if ~isempty(TCM_Data.click_point)
    plot(TCM_Data.click_point(1,1),TCM_Data.click_point(2,1),'xred');
end

if ~isempty(TCM_Data.sol)
    if isinf(TCM_Data.sol)
        TCM_UI.feastext.Visible = 'off';
        TCM_UI.unbtext.Visible = 'on';
        TCM_UI.mintext.Visible = 'off';
        TCM_UI.attext.Visible = 'off';
        TCM_UI.soltext.Visible = 'off';
    elseif isnan(TCM_Data.sol)
        TCM_UI.feastext.Visible = 'on';
        TCM_UI.unbtext.Visible = 'off';
        TCM_UI.mintext.Visible = 'off';
        TCM_UI.attext.Visible = 'off';
        TCM_UI.soltext.Visible = 'off';        
    else
        TCM_UI.feastext.Visible = 'off';
        TCM_UI.unbtext.Visible = 'off';
        TCM_UI.mintext.Visible = 'on';
        TCM_UI.attext.Visible = 'on';
        TCM_UI.soltext.Visible = 'on';        

        fmin = TCM_Data.f*TCM_Data.sol;
        
        points = TCM_Data.points;
        ind = sum(double(TCM_Data.A*points<=TCM_Data.b+10^-6)) < size(TCM_Data.A,1);
        points(:,ind) = [];
        if ~isempty(points)
            fvalues = TCM_Data.f*points;
            fmax = max(fvalues);
            fval = fliplr(linspace(fmin,fmax,10));
            for i=1:numel(fval)
                f = fimplicit(@(x,y) TCM_Data.f(1)*x+TCM_Data.f(2)*y-fval(i),[x([1 end]) y([1 end])],'red','LineWidth',2);
                xlim(TCM_Data.plot_window([1 2]));
                ylim(TCM_Data.plot_window([3 4]));
                pause(0.5);
                f.Visible = 'off';
            end
        end       
        
        TCM_UI.mintext.String = sprintf('Minimal value %.3f',fmin);
        exact = find(abs(TCM_Data.A*TCM_Data.sol-TCM_Data.b) < 10^-6);
        if numel(exact) == 1
            TCM_UI.attext.String = 'on line';
            TCM_UI.soltext.String = sprintf('%+.3f*x%+.3f*y=%.3f',TCM_Data.A(exact,1),TCM_Data.A(exact,2),TCM_Data.b(exact));
            if abs(TCM_Data.A(exact,2)) < 10^-6
                plot(TCM_Data.b(exact)/TCM_Data.A(exact,1)*[1 1],y([1 end]),'red','LineWidth',2);
            else
                plot(x([1 end]),(TCM_Data.b(exact)-TCM_Data.A(exact,1)*x([1 end]))/TCM_Data.A(exact,2),'red','LineWidth',2);
            end
        else
            TCM_Data.intersections(cellfun(@isempty,TCM_Data.intersections)) = {[NaN;NaN]};
            found = 0;
            for i=1:numel(exact)
                dist = cellfun(@(x) sqrt((x(1)-TCM_Data.sol(1)).^2+(x(2)-TCM_Data.sol(2)).^2),TCM_Data.intersections(:,exact(i)));
                feas = cellfun(@(x) min(TCM_Data.A*x<=TCM_Data.b+10^-6),TCM_Data.intersections(:,exact(i)));
                edge_point = find(feas == 1 & dist > 10^-6);
                
                if numel(edge_point) == 1 && abs(TCM_Data.f*TCM_Data.intersections{edge_point,exact(i)}-fmin)<10^-6
                    TCM_UI.attext.String = 'on the edge';
                    TCM_UI.soltext.String = sprintf('[%.3f,%.3f],[%.3f,%.3f]',TCM_Data.sol(1),TCM_Data.sol(2),...
                        TCM_Data.intersections{edge_point,exact(i)}(1),TCM_Data.intersections{edge_point,exact(i)}(2));
                    found = 1;
                    plot([TCM_Data.sol(1) TCM_Data.intersections{edge_point,exact(i)}(1)],[TCM_Data.sol(2) TCM_Data.intersections{edge_point,exact(i)}(2)],'red','LineWidth',2);
                    break;
                else
                    if abs(TCM_Data.A(exact(i),2))<10^-6
                        p1 = [TCM_Data.b(exact(i))/TCM_Data.A(exact(i),1);TCM_Data.sol(2)-1];
                        p2 = [TCM_Data.b(exact(i))/TCM_Data.A(exact(i),1);TCM_Data.sol(2)+1];
                    else
                        p1 = [TCM_Data.sol(1)-1;(TCM_Data.b(exact(i))-TCM_Data.A(exact(i),1)*(TCM_Data.sol(1)-1))/TCM_Data.A(exact(i),2)];
                        p2 = [TCM_Data.sol(1)+1;(TCM_Data.b(exact(i))-TCM_Data.A(exact(i),1)*(TCM_Data.sol(1)+1))/TCM_Data.A(exact(i),2)];
                    end
                    if (min(TCM_Data.A*p1<=TCM_Data.b+10^-6)==1 && abs(TCM_Data.f*p1-fmin)<10^-6) || ...
                        (min(TCM_Data.A*p2<=TCM_Data.b+10^-6)==1 && abs(TCM_Data.f*p2-fmin)<10^-6)
                        TCM_UI.attext.String = sprintf('on %+.3f*x%+.3f*y=%.3f',TCM_Data.A(exact(i),1),TCM_Data.A(exact(i),2),TCM_Data.b(exact(i)));
                        TCM_UI.soltext.String = sprintf('cut at [%.3f,%.3f]',TCM_Data.sol(1),TCM_Data.sol(2));

                        if (min(TCM_Data.A*p1<=TCM_Data.b+10^-6)==1 && abs(TCM_Data.f*p1-fmin)<10^-6)
                            if abs(TCM_Data.A(exact(i),2))<10^-6
                                p = [TCM_Data.sol(1) y(1)];
                            else
                                p = [x(1) (TCM_Data.b(exact(i))-TCM_Data.A(exact(i),1)*x(1))/TCM_Data.A(exact(i),2)];
                            end
                        else
                            if abs(TCM_Data.A(exact(i),2))<10^-6
                                p = [TCM_Data.sol(1) y(end)];
                            else
                                p = [x(end) (TCM_Data.b(exact(i))-TCM_Data.A(exact(i),1)*x(end))/TCM_Data.A(exact(i),2)];
                            end
                        end                        
                        plot([TCM_Data.sol(1) p(1)],[TCM_Data.sol(2) p(2)],'red','LineWidth',2);
                        found = 1;
                        break
                    end
                end
            end
            if ~found
                TCM_UI.attext.String = 'at point';
                TCM_UI.soltext.String = sprintf('[%.3f,%.3f]',TCM_Data.sol(1),TCM_Data.sol(2));
                plot(TCM_Data.sol(1),TCM_Data.sol(2),'ored','MarkerFaceColor','red');
            end
        end
    end
else
    TCM_UI.feastext.Visible = 'off';
    TCM_UI.unbtext.Visible = 'off';
    TCM_UI.mintext.Visible = 'on';
    TCM_UI.attext.Visible = 'on';
    TCM_UI.soltext.Visible = 'on'; 
    
    TCM_UI.mintext.String = 'Minimal value';
    TCM_UI.attext.String = 'at point';
    TCM_UI.soltext.String = '';
end

xlim(TCM_Data.plot_window([1 2]));
ylim(TCM_Data.plot_window([3 4]));

SetWindowLimits
UpdateHinttext
end

% Update the Hinttext
function UpdateHinttext
if (TCM_Data.mode == 2) && isempty(TCM_Data.click_point)
    LmbString = 'Left = Choose first point on the constraint. ';
elseif (TCM_Data.mode == 2) && ~isempty(TCM_Data.click_point) && TCM_Data.move == 0
    LmbString = 'Left = Choose second point on the constraint. ';
elseif (TCM_Data.mode == 2) && ~isempty(TCM_Data.click_point) && TCM_Data.move == 1
    LmbString = 'Left = Move constraint. ';
else
    LmbString = '';
end
if(numel(TCM_Data.LmbMenuString) == 0)
    set(TCM_UI.hinttext,'String',[LmbString 'Right Mouse = Access window menu.']);
else
    set(TCM_UI.hinttext,'String',[TCM_Data.LmbMenuString 'Right Mouse = Access window menu.']);
end
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%% example functions %%%%%%%%%%%%%%%%%%%%%%%%%%%

function CreateExampleMenu
TCM_UI.UICexamples = uicontextmenu;
for i=1:numel(TCM_Data.examples)
    uimenu('Parent',TCM_UI.UICexamples,'Label',TCM_Data.examples{i},'Callback',{@changeexample,i});
end
end

function OpenExampleMenu(varargin)
TCM_UI.examples.Units = 'pixels';
TCM_UI.buttonspanel.Units = 'pixels';
TCM_UI.UICexamples.Position = TCM_UI.examples.Position(1:2) + TCM_UI.buttonspanel.Position(1:2);
TCM_UI.examples.Units = 'normalized';
TCM_UI.buttonspanel.Units = 'points';
TCM_UI.UICexamples.Visible = 'on';
end
 
              
function changeexample(varargin)  
reset();
switch varargin{numel(varargin)}
    case 1
        TCM_UI.fun1input.String = '-1';
        TCM_UI.fun2input.String = '-1';
        function_update();
        pause(1/2);
        A = [-3 1.0000;-9 -1.0000;1 2;6 -1.0000];
        b = [-1 1 -0 2];
    case 2
        TCM_UI.fun1input.String = '-50';
        TCM_UI.fun2input.String = '-100';
        function_update();
        pause(1/2);
        A = [-1 0;0 -1;1 3;1 2];
        b = [0;0;90;80];
    case 3
        TCM_UI.fun1input.String = '-2';
        TCM_UI.fun2input.String = '-1';
        function_update();
        pause(1/2);
        A = [-2 1.0000;7 1.0000;1 1.0000];
        b = [-2 4 -1];
end
for i=1:numel(b)
    TCM_UI.ainput.String = num2str(A(i,1));
    TCM_UI.binput.String = num2str(A(i,2));
    TCM_UI.cinput.String = num2str(b(i));
    add_constraint();
    pause(1/2);
end
end


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% zoom functions %%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% function for updating the internal plot window
function zoom_modify(varargin)
if max(abs(TCM_Data.plot_window-axis(TCM_UI.ax)))< 10^-10
    ZoomOut;
end
pan_modify;
end

function pan_modify(varargin)
TCM_Data.plot_window = axis(TCM_UI.ax);
myplot;
end

% functions for proper zooming in (prevent double-click zoom out)
function [flag] = set_zoom_limits(varargin)
if(strcmp(TCM_UI.fig.SelectionType,'open'))
    flag = true;
else
    flag = false;
end
end

function reset_zoom_limits(varargin)
SetWindowLimits;
end

% handling the axis context menu
function axis_context(varargin)
zoom(TCM_UI.fig, 'off'); pan(TCM_UI.fig, 'off');
mode_strings = {'pan','zoom in','zoom out'};
status_strings = {'Enable ','Disable '};
switch varargin{1}.Label(1:7)
    case 'Enable '
        mode = strcmp(varargin{1}.Label(8:end),mode_strings);
        TCM_Data.LmbMenuString = ['Left Mouse = ' mode_strings{mode} ' the window. '];
        set(TCM_UI.mpan,'Label',horzcat(status_strings{mode(1)+1},mode_strings{1}));
        set(TCM_UI.mzoomi,'Label',horzcat(status_strings{mode(2)+1},mode_strings{2}));
        set(TCM_UI.mzoomo,'Label',horzcat(status_strings{mode(3)+1},mode_strings{3}));
        split_mode = strsplit(mode_strings{mode},' ');
        if numel(split_mode) == 1
            pan(TCM_UI.fig, 'on');
        else
            zoomh = zoom(TCM_UI.fig);
            zoomh.Enable = 'on';
            zoomh.Direction = split_mode{2};
        end
    case 'Disable'
        TCM_Data.LmbMenuString = '';
        set(TCM_UI.mpan,'Label','Enable pan');
        set(TCM_UI.mzoomi,'Label','Enable zoom in');
        set(TCM_UI.mzoomo,'Label','Enable zoom out');     
end
UpdateHinttext;
end

function SetWindowLimits
ZoomOut;
xlim(TCM_UI.ax,[TCM_Data.plot_window(1) TCM_Data.plot_window(2)]);
ylim(TCM_UI.ax,[TCM_Data.plot_window(3) TCM_Data.plot_window(4)]);
end

function ZoomOut
hx = TCM_Data.plot_window(2) - TCM_Data.plot_window(1);
hy = TCM_Data.plot_window(4) - TCM_Data.plot_window(3);
xlim(TCM_UI.ax,[TCM_Data.plot_window(1)-hx/2 TCM_Data.plot_window(2)+hx/2]);
ylim(TCM_UI.ax,[TCM_Data.plot_window(3)-hy/2 TCM_Data.plot_window(4)+hy/2]);
zoom(TCM_UI.fig, 'reset');
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% layout functions %%%%%%%%%%%%%%%%%%%%%%%%%%%

% functions for creating the different UI-Parts and managing the resize of
% the window
function CreateUI
TCM_UI.fig = figure('Visible','off', 'Units','points','Toolbar','none',...
    'SizeChangedFcn',@calculate_layout, 'WindowButtonDownFcn',@myDownfct,'WindowButtonMotionFcn',...
    @myMovefct,'WindowButtonUpFcn',@myUpfct,'numbertitle','off','name',TCM_UI.Title);
TCM_UI.titletext = uicontrol('Units','points','BackgroundColor',TCM_UI.fig.Color, ...
	'HorizontalAlignment','center',  'FontWeight','bold', ...
    'FontUnits','normalized',        'FontSize',0.8,...
	'String',TCM_UI.Title,        'Style','text');
TCM_UI.hinttext = uicontrol('Units','points','BackgroundColor',TCM_UI.fig.Color, ...
    'HorizontalAlignment','right',...
    'FontUnits','normalized',        'FontSize',0.63,...
	'String','','Style','text');
TCM_UI.infobutton=uicontrol('Units','points', ...
	'BackgroundColor',TCM_UI.fig.Color, ...
	'String','Info', 'HorizontalAlignment','left',...
    'FontUnits','normalized','FontSize',0.7,...
	'Callback',@information,'Style','pushbutton');
TCM_UI.feedbackpanel = uipanel('Units','points','BackgroundColor',TCM_UI.fig.Color,'BorderType','none');
TCM_UI.buttonspanel = uipanel('Units','points','BackgroundColor',TCM_UI.fig.Color,'BorderType','none');
TCM_UI.settingspanel = uipanel('Units','points','BackgroundColor',TCM_UI.fig.Color,'BorderType','none');
TCM_UI.axispanel = uipanel('Units','points','BackgroundColor',TCM_UI.fig.Color,'BorderType','none');
CreateExampleMenu
TCM_UI.fig.Visible= 'on';
end


function calculate_layout(varargin) 
figpos = TCM_UI.fig.Position;
WMax = TCM_UI.SettingsHeightMax*(TCM_UI.SettingsRatio + TCM_UI.FeedbackRatio + TCM_UI.ButtonsRatio);
WMin = TCM_UI.SettingsHeightMin*(TCM_UI.SettingsRatio + TCM_UI.FeedbackRatio + TCM_UI.ButtonsRatio);
HMax = TCM_UI.TitleHeightMax + TCM_UI.HintHeightMax;
HMin = TCM_UI.TitleHeightMin + TCM_UI.HintHeightMin;

wratio = max(0,min(1,(min(figpos(3),1.3*figpos(4))-WMin)/(WMax-WMin)));
XCoords = [1,0,0,0,0,0,0,figpos(3)];
SWidth =      wratio*TCM_UI.SettingsHeightMax*TCM_UI.SettingsRatio...
        + (1-wratio)*TCM_UI.SettingsHeightMin*TCM_UI.SettingsRatio;
BWidth =      wratio*TCM_UI.SettingsHeightMax*TCM_UI.ButtonsRatio...
        + (1-wratio)*TCM_UI.SettingsHeightMin*TCM_UI.ButtonsRatio; 
FWidth =      wratio*TCM_UI.SettingsHeightMax*TCM_UI.FeedbackRatio...
        + (1-wratio)*TCM_UI.SettingsHeightMin*TCM_UI.FeedbackRatio; 
RemWidth = figpos(3)-3-SWidth-FWidth-BWidth;
XCoords(2) = max(1,0.3*RemWidth);
XCoords(3) = XCoords(2) + 1 + SWidth;
XCoords(4) = XCoords(3) + max(0,0.2*RemWidth);
XCoords(5) = XCoords(4) + 1 + BWidth;
XCoords(6) = XCoords(5) + max(0,0.2*RemWidth);
XCoords(7) = XCoords(6) + 1 + FWidth;
SHeight = wratio*TCM_UI.SettingsHeightMax + (1-wratio)*TCM_UI.SettingsHeightMin;
if(HMin > min(figpos(4),0.9*figpos(3))*(1-TCM_UI.AxisHeightTarget)-5-SHeight)
    hratio = 0;
elseif(HMax <  min(figpos(4),0.9*figpos(3))*(1-TCM_UI.AxisHeightTarget)-5-SHeight)
    hratio = 1;
else
    hratio = (( min(figpos(4),0.9*figpos(3))*(1-TCM_UI.AxisHeightTarget)-5-SHeight)  -HMin)/(HMax-HMin);
end
YCoords = [1,0,0,0,figpos(4)-1];
YCoords(2) = YCoords(1) + 1 + hratio*TCM_UI.HintHeightMax     + (1-hratio)*TCM_UI.HintHeightMin;
YCoords(4) = YCoords(5) - 1 - hratio*TCM_UI.TitleHeightMax    - (1-hratio)*TCM_UI.TitleHeightMin;
YCoords(3) = YCoords(4) - 1 - SHeight;
TCM_UI.titletext.Position =     [XCoords(1)+1,  YCoords(4)+1,  XCoords(8)-XCoords(1)-1,  YCoords(5)-YCoords(4)-1];
TCM_UI.settingspanel.Position = [XCoords(2)+1,  YCoords(3)+1,  XCoords(3)-XCoords(2)-1,  YCoords(4)-YCoords(3)-1];
TCM_UI.buttonspanel.Position =  [XCoords(4)+1,  YCoords(3)+1,  XCoords(5)-XCoords(4)-1,  YCoords(4)-YCoords(3)-1];
TCM_UI.feedbackpanel.Position = [XCoords(6)+1,  YCoords(3)+1,  XCoords(7)-XCoords(6)-1,  YCoords(4)-YCoords(3)-1];
TCM_UI.axispanel.Position =     [XCoords(1)+1,  YCoords(2)+1,  XCoords(8)-XCoords(1)-1,  max(0,YCoords(3)-YCoords(2)-1)];
TmpXCoord = TCM_UI.InfoPortion*(XCoords(8)-XCoords(1)-1);
TCM_UI.infobutton.Position =    [XCoords(1)+1,  YCoords(1)+1,  TmpXCoord-XCoords(1)-1,   YCoords(2)-YCoords(1)-1];
TCM_UI.hinttext.Position =      [TmpXCoord+1,   YCoords(1)+1,  XCoords(8)-TmpXCoord-1,   YCoords(2)-YCoords(1)-1];
end

% function for opening the information webpage
function information(varargin)
pos = TCM_UI.fig.OuterPosition;
infodialog = dialog('Name','Information','Units','points',...
    'OuterPosition',[pos(1)+pos(3)/4 pos(2)+pos(4)/4 pos(3)/2 pos(4)/2]);
uicontrol('Units','normalized', 'Parent',infodialog, ...
        'FontUnits','normalized', 'FontSize',0.67,...
        'HorizontalAlignment','center', 'Position',[.01,.6,.98,.15],...
	'String','Mathematics Online Link', 'Style','text',...
    'ForegroundColor','blue','ButtonDownFcn',@linkfun,...
    'Enable','inactive');
uicontrol('Units','normalized', 'Parent',infodialog, ...
        'FontUnits','normalized', 'FontSize',0.4,...
        'HorizontalAlignment','center', 'Position',[.01,.4,.98,.15],...
	'String',{horzcat('Created by ',TCM_UI.Author,'.');horzcat('Version: ',TCM_UI.Date)} , 'Style','text');
end

function linkfun(varargin)
web(TCM_UI.Web)
end
end
