function TCM_rational_function_analysis

% TCM_RATIONAL_FUNCTION_ANALYSIS Interactive illustration of a function 
% analysis for polynomials and rational functions
%
%   TCM_RATIONAL_FUNCTION_ANALYSIS determines the characteristic points (zeros,
%   extrema, inflection points, and poles) for polynomials and rational 
%   functions. If the range of the variable is restricted to a finite 
%   or semi-infinite interval, boundary extrema are included too.
%
%   EXAMPLES
%
%   Cubic polynomial: characteristic points with integer coordinates on a 
%   semi-infinite interval.
%
%   Rational function, degree 2/1: simple rational function with few 
%   characterisitc points.
%
%   Rational function, degree 4/4: rational function of higher degree, 
%   restricted to a bounded interval, with a slightly more complicated graph.
%    
%   To investigate other cases, insert the coefficient vectors for numerator 
%   and denominator in the corresponding fields according to the MATLAB 
%   represention of polynomials. 
%
%   TCM_RATIONAL_FUNCTION_ANALYSIS 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/aussage45">Mathematics Online</a>.

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

% Initialize data
TCM_Data.nom = [1 0 0];  
TCM_Data.nomstring = '[1 0 0]';
TCM_Data.denom = 1;  
TCM_Data.denomstring = '1';
TCM_Data.int = [-Inf Inf];
TCM_Data.fun = @(x) polyval(TCM_Data.nom,x)./polyval(TCM_Data.denom,x);
TCM_Data.properties = struct('status',0,'zeros',[],...
    'maxima',[],'minima',[],'inflection',[],'poles',[],'asymptote',[],'allpoles',[]);
% Property status with following values
% 0 - nothing
% 1 - zeros
% 2 - maxima
% 3 - minima
% 4 - inflection
% 5 - pole
% 6 - asymptotes
% allpoles is used for plotting

% Do not delete these Variables
TCM_Data.plot_window = [-2 2 -1.5 1.5];
TCM_Data.LmbMenuString = '';
TCM_Data.examples = {'Cubic polynomial','Rational function, degree 2/1','Rational function, degree 4/4'};

% 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 = 'Rational function analysis';
TCM_UI.mono = get(0,'FixedWidthFontName');
% Strings for generating the information window
TCM_UI.Author = 'F. Martin and L. von Wolff';
TCM_UI.Date = '02.04.2017';
TCM_UI.Web = 'http://www.mathematics-online.org/inhalt/aussage/aussage45/';
      
CreateUI();

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

% Input for numerator
TCM_UI.nominput=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',[.35,.70,.6,.30],...
	'String','',...
	'Style','edit',...
	'Callback',@function_update);

% Text for Denominator
TCM_UI.denomtext = uicontrol('Units','normalized', 'Parent',TCM_UI.settingspanel, ...
        'BackgroundColor',get(gcf,'Color'), ...
        'FontUnits','normalized','FontSize',0.67,...
        'HorizontalAlignment','left', ...
	'Position',[.01,.35,.5,.30],...
	'String','Denominator:',...
	'Style','text');

% Input for Denominator
TCM_UI.denominput=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',[.35,.35,.6,.30],...
	'String','',...
	'Style','edit','Callback',@function_update);

% Text for Interval
TCM_UI.inttext = uicontrol('Units','normalized', 'Parent',TCM_UI.settingspanel, ...
        'BackgroundColor',get(gcf,'Color'), ...
        'FontUnits','normalized','FontSize',0.67,...
        'HorizontalAlignment','left', ...
	'Position',[.01,.0,.5,.30],...
	'String','Interval:',...
	'Style','text');

% Input for left boundary
TCM_UI.leftinput=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',[.35,.0,.25,.30],...
	'String','-Inf',...
	'Style','edit','Callback',@interval_update);

% Input for right boundary
TCM_UI.rightinput=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',[.7,.0,.25,.30],...
	'String','Inf',...
	'Style','edit','Callback',@interval_update);

%%%%%%%%%%%%%%%%%%%%%%%%% 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.next=uicontrol('Units','normalized','Parent',TCM_UI.buttonspanel, ...
	'BackgroundColor',get(gcf,'Color'), ...
	'String','Next', ...
    'FontUnits','normalized','FontSize',0.7,...
	'Position',[.01,.0,.98,.30], 'Callback',@next_property,...
	'Style','pushbutton');

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% UI-Elements in Feedback %%%%%%%%%%%%%%%%%%%%

% Text for the value of the global maximal value
TCM_UI.max_value_text=uicontrol('Units','normalized','Parent',TCM_UI.feedbackpanel, ...
        'BackgroundColor',get(gcf,'Color'), ...
        'HorizontalAlignment','left', ...
        'FontUnits','normalized','FontSize',0.7,...
	'Position',[.05,.7,.95,.3],...
	'String','',...
	'Style','text');

% Text for the value of the global minimal value
TCM_UI.min_value_text=uicontrol('Units','normalized','Parent',TCM_UI.feedbackpanel, ...
        'BackgroundColor',get(gcf,'Color'), ...
        'HorizontalAlignment','left', ...
        'FontUnits','normalized','FontSize',0.7,...
	'Position',[.05,.35,.95,.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 %%%%%%%%%%%%%%%%%%%%%%%%%%%
% new function input
function function_update(varargin)
TCM_Data.nom = eval(get(TCM_UI.nominput,'String'));
TCM_Data.denom = eval(get(TCM_UI.denominput,'String'));
TCM_Data.fun = @(x) polyval(TCM_Data.nom,x)./polyval(TCM_Data.denom,x);
TCM_UI.leftinput.String = '-Inf'; TCM_UI.rightinput.String = 'Inf';
if isequal(TCM_Data.nom,TCM_Data.denom)
    TCM_Data.nom = 1;
    TCM_Data.denom = 1;
end
interval_update;
end

% update if new interval boundaries has been entered
function interval_update(varargin)
newint = [str2double(TCM_UI.leftinput.String) str2double(TCM_UI.rightinput.String)];
if abs(newint(2)-newint(1))<10^-10 || isnan(abs(newint(2)-newint(1)))
    TCM_UI.leftinput.String = num2str(TCM_Data.int(1));
    TCM_UI.rightinput.String = num2str(TCM_Data.int(2));
    return;
else
    TCM_Data.int = newint;
end
if TCM_Data.int(1) > TCM_Data.int(2)
    TCM_Data.int = TCM_Data.int([2 1]);
    TCM_UI.leftinput.String = num2str(TCM_Data.int(1));
    TCM_UI.rightinput.String = num2str(TCM_Data.int(2));
end
function_analysis;
try
    TCM_Data.plot_window(1) = min([TCM_Data.properties.zeros,...
        TCM_Data.properties.maxima,TCM_Data.properties.minima,...
        TCM_Data.properties.inflection,TCM_Data.properties.poles]);
    TCM_Data.plot_window(2) = max([TCM_Data.properties.zeros,...
        TCM_Data.properties.maxima,TCM_Data.properties.minima,...
        TCM_Data.properties.inflection,TCM_Data.properties.poles]);
catch
    TCM_Data.plot_window(1:2) = [-1 1];
end
h = TCM_Data.plot_window(2) - TCM_Data.plot_window(1);
if abs(h) < 10^-6
    h = 1;
    TCM_Data.plot_window(1:2) = TCM_Data.plot_window(1:2)+[-1/2 1/2];
end
TCM_Data.plot_window(1:2) = TCM_Data.plot_window(1:2) + [-h/4,h/4];
y = TCM_Data.fun([TCM_Data.properties.zeros,...
    TCM_Data.properties.maxima,TCM_Data.properties.minima,...
    TCM_Data.properties.inflection,TCM_Data.plot_window(1:2)]);
y(y > 10^10) = NaN;
TCM_Data.plot_window(3:4) = [min(min(y),-1) max(1,max(y))];

x = linspace(TCM_Data.plot_window(1),TCM_Data.plot_window(2),fix(2000/3));
rel_amount = 0.9;
f = sort(TCM_Data.fun(x));
ind_up = find(f > TCM_Data.plot_window(4),1,'first');
ind_down = find(f < TCM_Data.plot_window(3),1,'last');
if isempty(ind_up) 
    ind_up = fix(2000/3);
end
if isempty(ind_down) 
    ind_down = 1;
end  
while (ind_up-ind_down-1)/fix(2000/3) < rel_amount    
    ind_up = min(ind_up+1,fix(2000/3));
    ind_down = max(ind_down-1,1);
end
TCM_Data.plot_window(3:4) = f([ind_down ind_up]);

h = TCM_Data.plot_window(4)-TCM_Data.plot_window(3);
if abs(h) < 10^-6
    h = 1;
    TCM_Data.plot_window(3:4) = TCM_Data.plot_window(3:4)+[-1/2 1/2];
end
TCM_Data.plot_window(3:4) = [min(min(y)-h/8,-1) max(1,max(y)+h/8)];
reset;
end

% refresh all data from UI
function reset(varargin)
% Disable Zoom/Pan
window_disable.Label = 'Disable pan';
axis_context(window_disable);
TCM_Data.properties.status = 0;
set(TCM_UI.max_value_text,'String','');
set(TCM_UI.min_value_text,'String','');
TCM_UI.next.Enable = 'On';
myplot;
end

% mouse button pressed 
function next_property(varargin)
if TCM_Data.properties.status == 5
    TCM_UI.next.Enable = 'Off';
end
if TCM_Data.properties.status < 6
    TCM_Data.properties.status = TCM_Data.properties.status + 1;
end
myplot;
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% kernel functions %%%%%%%%%%%%%%%%%%%%%%%%%%%

% calculate all properties which are needed for the analysis
function function_analysis
% zeros
TCM_Data.properties.zeros = rat_poly_roots(TCM_Data.nom,TCM_Data.denom);
% extrema
[der_nom,der_denom,extrema] = rat_crit_points(TCM_Data.nom,TCM_Data.denom);
TCM_Data.properties.maxima = extrema{1};
TCM_Data.properties.minima = extrema{2};
if isempty(TCM_Data.properties.minima)
    TCM_Data.properties.minima = [];
end
if isempty(TCM_Data.properties.maxima)
    TCM_Data.properties.maxima = [];
end
% inflection
[~,~,TCM_Data.properties.inflection] = rat_crit_points(der_nom,der_denom);
TCM_Data.properties.inflection = cell2mat(TCM_Data.properties.inflection(~cellfun(@isempty,TCM_Data.properties.inflection)));
% pole
TCM_Data.properties.poles = unique(rat_poly_roots(TCM_Data.denom,TCM_Data.nom));
TCM_Data.properties.allpoles = TCM_Data.properties.poles;
for i=1:numel(TCM_Data.properties.poles)
    TCM_Data.properties.maxima(abs(TCM_Data.properties.maxima-TCM_Data.properties.poles(i))...
        < 10^-6) = [];
    TCM_Data.properties.minima(abs(TCM_Data.properties.minima-TCM_Data.properties.poles(i))...
        < 10^-6) = [];
    TCM_Data.properties.inflection(abs(TCM_Data.properties.inflection-TCM_Data.properties.poles(i))...
        < 10^-6) = [];
end
% asymptote
TCM_Data.properties.asymptote = deconv(TCM_Data.nom,TCM_Data.denom);
if numel(TCM_Data.properties.asymptote) > 2 
    TCM_Data.properties.asymptote = [];
end

% delete points not in the interval
TCM_Data.properties.zeros = TCM_Data.properties.zeros(TCM_Data.properties.zeros >= TCM_Data.int(1)-10^-6 & ...
    TCM_Data.properties.zeros <= TCM_Data.int(2)+10^-6);
TCM_Data.properties.maxima = TCM_Data.properties.maxima(TCM_Data.properties.maxima >= TCM_Data.int(1)-10^-6 & ...
    TCM_Data.properties.maxima <= TCM_Data.int(2)+10^-6);
TCM_Data.properties.minima = TCM_Data.properties.minima(TCM_Data.properties.minima >= TCM_Data.int(1)-10^-6 & ...
    TCM_Data.properties.minima <= TCM_Data.int(2)+10^-6);
TCM_Data.properties.inflection = TCM_Data.properties.inflection(TCM_Data.properties.inflection >= TCM_Data.int(1)-10^-6 & ...
    TCM_Data.properties.inflection <= TCM_Data.int(2)+10^-6);
TCM_Data.properties.poles = TCM_Data.properties.poles(TCM_Data.properties.poles >= TCM_Data.int(1)-10^-6 & ...
    TCM_Data.properties.poles <= TCM_Data.int(2)+10^-6);

der_roots = rat_poly_roots(der_nom,der_denom);
% left boundary extrema
if ~isinf(TCM_Data.int(1))
    boundary_der = polyval(der_nom,TCM_Data.int(1))/polyval(der_denom,TCM_Data.int(1));
    if boundary_der < 0
        TCM_Data.properties.maxima = [TCM_Data.properties.maxima TCM_Data.int(1)];
    elseif boundary_der > 0
        TCM_Data.properties.minima = [TCM_Data.properties.minima TCM_Data.int(1)];
    else
        midpoint = 1/2*(der_roots(find(der_roots > TCM_Data.int(1),1,'first'))+TCM_Data.int(1));
        if polyval(der_nom,midpoint)/polyval(der_denom,midpoint) < 0
            TCM_Data.properties.maxima = [TCM_Data.properties.maxima TCM_Data.int(1)];
        else
            TCM_Data.properties.minima = [TCM_Data.properties.minima TCM_Data.int(1)];
        end
    end
end

% right boundary extrema
if ~isinf(TCM_Data.int(2))
    boundary_der = polyval(der_nom,TCM_Data.int(2))/polyval(der_denom,TCM_Data.int(2));
    if boundary_der > 0
        TCM_Data.properties.maxima = [TCM_Data.properties.maxima TCM_Data.int(2)];
    elseif boundary_der < 0
        TCM_Data.properties.minima = [TCM_Data.properties.minima TCM_Data.int(2)];
    else
        midpoint = 1/2*(TCM_Data.int(2)-der_roots(find(der_roots < TCM_Data.int(2),1,'last')));
        if polyval(der_nom,midpoint)/polyval(der_denom,midpoint) > 0
            TCM_Data.properties.maxima = [TCM_Data.properties.maxima TCM_Data.int(2)];
        else
            TCM_Data.properties.minima = [TCM_Data.properties.minima TCM_Data.int(2)];
        end
    end
end
end

% calculate roots of a rational function
function final_roots = rat_poly_roots(nom,denom)
nom_roots = roots(nom);
denom_roots = roots(denom);
final_roots = delete_roots(sort(nom_roots(nom_roots == real(nom_roots))),...
    sort(denom_roots(denom_roots == real(denom_roots))));
end

% delete roots which are common in numerator and denominator
function roots = delete_roots(roots1,roots2)
[unique_roots1,indices1,~] = unique(roots1);
[unique_roots2,indices2,~] = unique(roots2);
indices1(end+1)=numel(roots1)+1;
indices2(end+1)=numel(roots2)+1;
roots = [];
for i=1:numel(unique_roots1)
    j = find(unique_roots2==unique_roots1(i), 1);
    amount1 = indices1(i+1)-indices1(i);
    if ~isempty(j)
        amount2 = indices2(j+1)-indices2(j);
        roots = [roots; unique_roots1(i)*ones(max(0,amount1-amount2),1)];
    else
        roots = [roots; unique_roots1(i)*ones(amount1,1)];
    end
end
roots = sort(roots)';
end

% calculate the numerator, denominator and critical points of a rational
% function
function [der_nom,der_denom,crit_points] = rat_crit_points(nom,denom)
[der_nom,der_denom] = polyder(nom,denom);
crit_points = rat_poly_roots(der_nom,der_denom);
maxima = []; minima = [];
for i=1:numel(crit_points)
    if sign(polyval(der_nom,crit_points(i)-10^-6)/polyval(der_denom,crit_points(i)-10^-6))...
            <sign(polyval(der_nom,crit_points(i)+10^-6)/polyval(der_denom,crit_points(i)+10^-6))
        minima = [minima crit_points(i)];
    elseif sign(polyval(der_nom,crit_points(i)-10^-6)/polyval(der_denom,crit_points(i)-10^-6))...
            > sign(polyval(der_nom,crit_points(i)+10^-6)/polyval(der_denom,crit_points(i)+10^-6))
        maxima = [maxima crit_points(i)];
    end
end
crit_points = {unique(maxima) unique(minima)};
end

% plot function
function myplot
cla(TCM_UI.ax, 'reset'); 
hold(TCM_UI.ax, 'on');
% evaluating and plotting of the input function on a square containing the
% current window for smoothless panning and zooming
h = TCM_Data.plot_window(2) - TCM_Data.plot_window(1);
x = linspace(TCM_Data.plot_window(1)-h,TCM_Data.plot_window(2)+h,2000);
fx = TCM_Data.fun(x);

j = 1;
for i=1:numel(TCM_Data.properties.allpoles)
    I = find(x<TCM_Data.properties.allpoles(i) & x>=x(j));
    plot(TCM_UI.ax,x(I),fx(I),'-','linewidth',2,'color',[0,0,.6]);
    if ~isempty(I)
        j = I(end)+1;
    end
end
plot(TCM_UI.ax,x(j:end),fx(j:end),'-','linewidth',2,'color',[0,0,.6]); 
plot(TCM_UI.ax,[x(1) x(end)],[0 0],'black');
xlim(TCM_UI.ax,TCM_Data.plot_window(1:2));
ylim(TCM_UI.ax,TCM_Data.plot_window(3:4));

int_inf = find(~isinf(TCM_Data.int));
box_height = .05*(TCM_Data.plot_window(4)-TCM_Data.plot_window(3));
alpha = 0.5;
if numel(int_inf) == 2
    box = fill(TCM_Data.int([1 2 2 1]),box_height*[-1 -1 1 1],.5*[1,1,1],'FaceAlpha',alpha);
    uistack(box,'bottom');
elseif int_inf == 1
    box = fill([TCM_Data.int(1) x([end end]) TCM_Data.int(1)],box_height*[-1 -1 1 1],.5*[1,1,1],'FaceAlpha',alpha);
    uistack(box,'bottom');
elseif int_inf == 2
    box = fill([x(1) TCM_Data.int(2) TCM_Data.int(2) x(1)],box_height*[-1 -1 1 1],.5*[1,1,1],'FaceAlpha',alpha);
    uistack(box,'bottom');
else
    box = fill(x([1 end end 1]),box_height*[-1 -1 1 1],.5*[1,1,1],'FaceAlpha',alpha);
    uistack(box,'bottom');
end

plots = gobjects(1,6);
for i=1:TCM_Data.properties.status
    switch i
        % zeros
        case 1
            if ~isempty(TCM_Data.properties.zeros)
                plots(1) = plot(TCM_Data.properties.zeros,0*TCM_Data.properties.zeros,'or','LineWidth',1,'MarkerSize',7);
            end
        % maxima
        case 2
            if ~isempty(TCM_Data.properties.maxima)
                if max(TCM_Data.fun(x(x>=TCM_Data.int(1) & x<=TCM_Data.int(2)))) > max(TCM_Data.fun(TCM_Data.properties.maxima))+10^-1 
                    set(TCM_UI.max_value_text,'String','Global maximal value: Inf');
                elseif numel(TCM_Data.properties.asymptote) == 1 && max(TCM_Data.fun(x(x>=TCM_Data.int(1) & x<=TCM_Data.int(2))))<TCM_Data.properties.asymptote(1)
                    set(TCM_UI.max_value_text,'String','Global maximal value:');
                else 
                    set(TCM_UI.max_value_text,'String',sprintf('Global maximal value: %.3f',max(TCM_Data.fun(TCM_Data.properties.maxima))));
                end
                plots(2) = plot(TCM_Data.properties.maxima,TCM_Data.fun(...
                    TCM_Data.properties.maxima),'^r','LineWidth',1,'MarkerSize',8);
            elseif numel(TCM_Data.properties.asymptote) == 1 && max(TCM_Data.fun(x(x>=TCM_Data.int(1) & x<=TCM_Data.int(2))))<TCM_Data.properties.asymptote(1)
                set(TCM_UI.max_value_text,'String','Global maximal value:');
            else 
                set(TCM_UI.max_value_text,'String','Global maximal value: Inf');
            end
        % minima
        case 3
            if ~isempty(TCM_Data.properties.minima)
                if min(TCM_Data.fun(x(x>=TCM_Data.int(1) & x<=TCM_Data.int(2)))) < min(TCM_Data.fun(TCM_Data.properties.minima))-10^-1 
                    set(TCM_UI.min_value_text,'String','Global minimal value: -Inf');
                elseif numel(TCM_Data.properties.asymptote) == 1 && min(TCM_Data.fun(x(x>=TCM_Data.int(1) & x<=TCM_Data.int(2))))>TCM_Data.properties.asymptote(1)
                    set(TCM_UI.min_value_text,'String','Global minimal value:');
                else
                    set(TCM_UI.min_value_text,'String',sprintf('Global minimal value: %.3f',min(TCM_Data.fun(TCM_Data.properties.minima))));
                end
                plots(3) = plot(TCM_Data.properties.minima,TCM_Data.fun(...
                    TCM_Data.properties.minima),'vr','LineWidth',1.1,'MarkerSize',8);
            elseif numel(TCM_Data.properties.asymptote) == 1 && min(TCM_Data.fun(x(x>=TCM_Data.int(1) & x<=TCM_Data.int(2))))>TCM_Data.properties.asymptote(1)
                set(TCM_UI.min_value_text,'String','Global minimal value:');
            else
                set(TCM_UI.min_value_text,'String','Global minimal value: -Inf');
            end
        % inflection
        case 4
            if ~isempty(TCM_Data.properties.inflection)
                plots(4) = plot(TCM_Data.properties.inflection,TCM_Data.fun(...
                    TCM_Data.properties.inflection),'xr','LineWidth',1.25,'MarkerSize',8);
            end
        % pole
        case 5
            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);
            for j=1:numel(TCM_Data.properties.poles)
                plots(5) = plot(TCM_Data.properties.poles(j)*ones(1,numel(y)),y,'--r','linewidth',1);
            end
        % asymptote
        case 6
            if ~isempty(TCM_Data.properties.asymptote) && (isinf(TCM_Data.int(1)) || isinf(TCM_Data.int(2)))
                asymptote = polyval(TCM_Data.properties.asymptote,x(x>=TCM_Data.int(1) & x<=TCM_Data.int(2)));
                plots(6) = plot(x(x>=TCM_Data.int(1) & x<=TCM_Data.int(2)),asymptote,'-.r','linewidth',1);           
            end
    end
end
nonempty_plots = arrayfun(@(x) ~isa(x, 'matlab.graphics.GraphicsPlaceholder'), plots);
legend_strings = {'zeros','maxima','minima','inflection points','poles','asymptote'};
if max(nonempty_plots) == 1
    legend(plots(nonempty_plots),legend_strings(nonempty_plots),'Location','Best');
end
SetWindowLimits;
UpdateHinttext
end

% Update the Hinttext
function UpdateHinttext
names = fieldnames(TCM_Data.properties);
if TCM_Data.properties.status == 3
    LmbString = 'Next = Calculate the inflection points. ';
elseif TCM_Data.properties.status < 6
    LmbString = ['Next = Calculate the ' names{TCM_Data.properties.status+2} '. '] ;
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)  
switch varargin{numel(varargin)}
    case 1
        TCM_UI.nominput.String = '[1 0 -3 -2]';
        TCM_UI.denominput.String = '1';
        function_update;
        TCM_UI.leftinput.String = '-Inf';
        TCM_UI.rightinput.String = '3';
        interval_update;        
    case 2
        TCM_UI.nominput.String = '[1 -10 9]';
        TCM_UI.denominput.String = '[1 0]';
        function_update;
    case 3
        TCM_UI.nominput.String = '[1 0 -5 0 4]';
        TCM_UI.denominput.String = '[1 1 1 0 0]';
        function_update;
        TCM_UI.leftinput.String = '-4';
        TCM_UI.rightinput.String = '4';
        interval_update;
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,'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.69,...
	'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
