function TCM_mean_value_theorem
% TCM_MEAN_VALUE_THEOREM Interactive illustration of the two basic mean value
% theorems
%
% TCM_MEAN_VALUE_THEOREM illustrates that for a differentiable function
% there exists a point in a given interval at which the tangent of the
% function is parallel to the secant connecting the function values at
% the endpoints of the interval. In a second window, the mean value theorem
% for definite integrals is illustrated. The plot shows a rectangle, with
% height equal to an intermediate function value, which matches the signed
% area bounded by the function and the x-axis for the chosen interval.
%
% EXAMPLES
%
% Parabola: basic example for which the mean values have simple explicit
% formulas.
%
% Trigonometric function: oscillating function with less intuitive
% position of the mean values.
%
% Rational function: nonexistence of mean values due to singularities.
%
% To investigate other cases, insert a function and its derivative
% in the corresponding fields. If the AutoDiff toolbox is installed,
% the derivative can be computed automatically by leaving the field
% blank.
%
% TCM_MEAN_VALUE_THEOREM is part of the TCM project.
% Related course materials can be found at Mathematics Online.
% Authors: Florian Martin, Lars von Wolff
% Last updated: 27.06.2016
% Initialize data
TCM_Data.move = 0;
TCM_Data.maxsteps = 0;
TCM_Data.int = [NaN NaN];
TCM_Data.fun = @(x) exp(-x)-sin(x);
TCM_Data.funstring = '@(x) exp(-x)-sin(x)';
TCM_Data.der = @(x) -exp(-x)-cos(x);
TCM_Data.derstring = '@(x) -exp(-x)-cos(x)';
% Do not delete these Variables
TCM_Data.plot_window = [-1 10 -2 3];
TCM_Data.LmbMenuString = '';
TCM_Data.examples = {'Parabola','Trigonometric function','Rational function'};
% 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 = 'Mean value theorem';
TCM_UI.mono = get(0,'FixedWidthFontName');
% Strings for generating the information window
TCM_UI.Author = 'F. Martin and L. von Wolff';
TCM_UI.Date = '27.06.2016';
TCM_UI.Web = 'http://www.mathematics-online.org/inhalt/aussage/aussage172';
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,.5,.30],...
'String','Function:',...
'Style','text');
% Input for Function
TCM_UI.funinput=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',[.27,.70,.68,.30],...
'String','',...
'Style','edit',...
'Callback',@function_update);
% Text for Derivative
TCM_UI.dertext = uicontrol('Units','normalized', 'Parent',TCM_UI.settingspanel, ...
'BackgroundColor',get(gcf,'Color'), ...
'FontUnits','normalized','FontSize',0.67,...
'HorizontalAlignment','left', ...
'Position',[.01,.35,.5,.30],...
'String','Derivative:',...
'Style','text');
% Input for Derivative
TCM_UI.derinput=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',[.27,.35,.68,.30],...
'String','',...
'Style','edit','Callback',@function_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');
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% UI-Elements in Feedback %%%%%%%%%%%%%%%%%%%%
% Text for new interval
TCM_UI.intertext=uicontrol('Units','normalized','Parent',TCM_UI.feedbackpanel,...
'BackgroundColor',get(gcf,'Color'), ...
'HorizontalAlignment','left', ...
'FontUnits','normalized','FontSize',0.7,...
'Position',[.1,.7,.30,.3],...
'String',sprintf('Interval:'),...
'Style','text');
% Input for new interval
TCM_UI.interinput=uicontrol('Units','normalized','Parent',TCM_UI.feedbackpanel,...
'BackgroundColor',get(gcf,'Color'), ...
'HorizontalAlignment','left', ...
'FontUnits','normalized','FontSize',0.7,...
'Position',[.40,.7,.5,.3],...
'String','[NaN, NaN]',...
'Style','edit',...
'Callback',@inter_update);
% Text for current slope value
TCM_UI.slopeval=uicontrol('Units','normalized','Parent',TCM_UI.feedbackpanel, ...
'BackgroundColor',get(gcf,'Color'), ...
'HorizontalAlignment','left', ...
'FontUnits','normalized','FontSize',0.7,...
'Position',[.1,.35,.9,.3],...
'String',sprintf('Slope: %5.3e',NaN),...
'Style','text');
% Text for area in the marked rectangle
TCM_UI.areaval=uicontrol('Units','normalized','Parent',TCM_UI.feedbackpanel, ...
'BackgroundColor',get(gcf,'Color'), ...
'HorizontalAlignment','left', ...
'FontUnits','normalized','FontSize',0.7,...
'Position',[.1,.01,.9,.3],...
'String',sprintf('Area: %5.3e',NaN),...
'Style','text');
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% UI-Elements in Axis %%%%%%%%%%%%%%%%%%%%%%%%
% Axis for Function display
TCM_UI.ax=axes('Position',[.05,.1,.42,.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;
TCM_UI.ax2=axes('Position',[.55,.1,.42,.85],'Parent',TCM_UI.axispanel);
TCM_UI.ax2.UIContextMenu = TCM_UI.modification;
linkaxes([TCM_UI.ax, TCM_UI.ax2]);
% 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)
funchange = strcmp(TCM_Data.funstring,get(TCM_UI.funinput,'String'));
derchange = strcmp(TCM_Data.derstring,get(TCM_UI.derinput,'String'));
TCM_Data.funstring = get(TCM_UI.funinput,'String');
if (isempty(TCM_UI.derinput.String) || strcmp(TCM_UI.derinput.String,'automatic')...
|| (~funchange && derchange)) && exist('ainit','file')
var = TCM_Data.funstring(find(TCM_Data.funstring=='(',1)+1:...
find(TCM_Data.funstring==')',1)-1);
TCM_Data.fun = eval([TCM_Data.funstring '+0*' var]);
TCM_Data.der = adiff(TCM_Data.fun,1);
set(TCM_UI.derinput,'String','automatic');
else
TCM_Data.fun = eval(TCM_Data.funstring);
TCM_Data.derstring = get(TCM_UI.derinput,'String');
TCM_Data.der = eval(TCM_Data.derstring);
end
setplotwindow(0);
reset;
end
% create a plot window around a point x
function setplotwindow(x)
x = linspace(x-2,x+2);
f = TCM_Data.fun(x); f(abs(f) > 10^3) = 0;
h = max(f)-min(f);
TCM_Data.plot_window = [x(1) x(end) min(f)-h/8 max(f)+h/8];
if abs(TCM_Data.plot_window(3)-TCM_Data.plot_window(4)) < 10^-8
if abs(x(end)-2)<10^-8
TCM_Data.plot_window(3) = min(TCM_Data.plot_window(3)-1,-1);
TCM_Data.plot_window(4) = max(TCM_Data.plot_window(4)+1,1);
else
setplotwindow(0);
end
else
TCM_Data.plot_window(3) = min(TCM_Data.plot_window(3),-1);
TCM_Data.plot_window(4) = max(TCM_Data.plot_window(4),1);
end
end
% new initial value input
function inter_update(varargin)
TCM_Data.int = eval(get(TCM_UI.interinput,'String'));
window_disable.Label = 'Disable pan';
axis_context(window_disable);
if TCM_Data.int(1) < TCM_Data.plot_window(1)
TCM_Data.plot_window(1) = TCM_Data.int(1)-1/2;
end
if TCM_Data.int(2) > TCM_Data.plot_window(2)
TCM_Data.plot_window(2) = TCM_Data.int(2)+1/2;
end
myplot;
end
% refresh all data from UI
function reset(varargin)
set(TCM_UI.areaval,'String',sprintf('Area: %5.3e',NaN));
set(TCM_UI.areaval,'ForegroundColor','black');
set(TCM_UI.slopeval,'String',sprintf('Slope: %5.3e',NaN));
set(TCM_UI.slopeval,'ForegroundColor','black');
set(TCM_UI.interinput,'String','[NaN, NaN]');
TCM_Data.int = [NaN NaN];
window_disable.Label = 'Disable pan';
axis_context(window_disable);
myplot;
end
% mouse button pressed
function myDownfct(varargin)
p = getClickPosition(TCM_UI.ax);
if(isnan(p))
p = getClickPosition(TCM_UI.ax2);
if(isnan(p))
return;
end
end
if isnan(TCM_Data.int(1)) && strcmp(get(gcf,'SelectionType'),'normal');
TCM_Data.int(1) = p(1);
myplot;
elseif isnan(TCM_Data.int(2)) && strcmp(get(gcf,'SelectionType'),'normal');
if p(1) < TCM_Data.int(1)
return
end
TCM_Data.int(2) = p(1);
myplot;
elseif strcmp(get(gcf,'SelectionType'),'normal');
for i=1:2
dist(i) = norm(window_transformation(p)-window_transformation(...
[TCM_Data.int(i); 0]),2);
end
[dist,j] = min(dist);
if dist < 0.05
TCM_Data.int(j) = p(1);
TCM_Data.move = j;
myplot;
end
end
end
% transformation to a square unit window
function p = window_transformation(p)
ax_pos = TCM_UI.ax.Position;
p = [ax_pos(3)/(TCM_Data.plot_window(2)-TCM_Data.plot_window(1)) 0;...
0 ax_pos(4)/(TCM_Data.plot_window(4)-TCM_Data.plot_window(3))] *...
(p(:) - [TCM_Data.plot_window(1); TCM_Data.plot_window(3)]);
end
% moving the mouse in the axis
function myMovefct(varargin)
if TCM_Data.move > 0
p = getClickPosition(TCM_UI.ax);
if(isnan(p))
p = getClickPosition(TCM_UI.ax2);
if(isnan(p))
return;
end
end
TCM_Data.int(TCM_Data.move) = p(1);
myplot;
end
end
% releasing a mouse button
function myUpfct(varargin)
TCM_Data.move = 0;
myplot;
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 %%%%%%%%%%%%%%%%%%%%%%%%%%%
% calculating the point c from the mean value theorem
function c = mean_value(index)
a = TCM_Data.int(1); b = TCM_Data.int(2);
if abs(b-a) < 10^(-10)
c = (b-a)/2;
return
end
if index == 1
fun = @(x) TCM_Data.der(x)-(TCM_Data.fun(b)-TCM_Data.fun(a))/(b-a);
else
fun = @(x) mean_value_integral(x,a,b);
end
c = b; h = (b-a)/2;
state = warning;
warning('off','all');
for k=1:10
[s,ind] = min(fun(a)*fun(c));
if s<=0
try
c = fzero(fun,[a,c(ind)]);
if abs(fun(c)) > 10^-1
c = NaN;
end
warning(state);
return
catch
c = NaN;
warning(state);
return
end
end
c = [a+h/2:h:b];
h = h/2;
end
[~,ind] = min(abs(fun(c)));
c = c(ind);
if abs(fun(c)) > 10^-1
c = NaN;
end
warning(state);
end
% function for calculating the second mean value point
function y = mean_value_integral(x,a,b)
[q,e] = quadgk(TCM_Data.fun,a,b);
if e > 10^-4
y = Inf;
else
y = TCM_Data.fun(x)*(b-a)-q;
end
end
% plot function
function myplot
subplot(TCM_UI.ax)
cla(TCM_UI.ax, 'reset');
hold(TCM_UI.ax, 'on');
cla(TCM_UI.ax2, 'reset');
hold(TCM_UI.ax2, 'on');
[TCM_Data.int,I] = sort(TCM_Data.int);
if (TCM_Data.move > 0) && (I(1) == 2)
TCM_Data.move = 3 - TCM_Data.move;
end
a = TCM_Data.int(1); b = TCM_Data.int(2);
% 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,1000);
plot(x,TCM_Data.fun(x),'-','linewidth',2,'color',[0,0,.6]);
plot([x(1) x(end)],[0 0],'black');
xlim([TCM_Data.plot_window(1) TCM_Data.plot_window(2)]);
ylim([TCM_Data.plot_window(3) TCM_Data.plot_window(4)]);
plot(TCM_UI.ax2,x,TCM_Data.fun(x),'-','linewidth',2,'color',[0,0,.6]);
plot(TCM_UI.ax2,[x(1) x(end)],[0 0],'black');
xlim(TCM_UI.ax2,[TCM_Data.plot_window(1) TCM_Data.plot_window(2)]);
ylim(TCM_UI.ax2,[TCM_Data.plot_window(3) TCM_Data.plot_window(4)]);
if ~isnan(a)
plot(a,0,'+black');
text(a,0,'a','VerticalAlignment','Top','Clipping','on');
plot(TCM_UI.ax2,a,0,'+black');
text(a,0,'a','VerticalAlignment','Top','Parent',TCM_UI.ax2,'Clipping','on');
plot([a;a],[0;TCM_Data.fun(a)],'r:','LineWidth',1.5);
set(TCM_UI.interinput,'String',strcat('[',sprintf('%.3f',TCM_Data.int(1)),...
',NaN]'));
if ~isnan(b)
set(TCM_UI.interinput,'String',strcat('[',sprintf('%.3f',TCM_Data.int(1)),...
',',sprintf('%.3f',TCM_Data.int(2)),']'));
cs = mean_value(1);
plot(b,0,'+black');
text(b,0,'b','VerticalAlignment','Top','Clipping','on');
plot([a;b],TCM_Data.fun([a,b]),'red','linewidth',1.25);
plot([b;b],[0;TCM_Data.fun(b)],'r:','LineWidth',1.5);
if isnan(cs)
set(TCM_UI.slopeval,'String','Slope: no mean value found');
set(TCM_UI.slopeval,'ForegroundColor','red');
else
set(TCM_UI.slopeval,'String',sprintf('Slope: %5.3e',TCM_Data.der(cs)));
set(TCM_UI.slopeval,'ForegroundColor','black');
plot(cs,0,'+black');
text(cs,0,'c_s','VerticalAlignment','Top','Clipping','on');
t = linspace(a,b);
tangent = TCM_Data.der(cs)*(t-cs)+TCM_Data.fun(cs);
plot(t,tangent,'Color',[0 0.5 0],'linewidth',1.25);
plot([cs;cs],[0;TCM_Data.fun(cs)],':','LineWidth',1.5,'Color',[0 0.5 0]);
end
ca = mean_value(2);
plot(TCM_UI.ax2,b,0,'+black');
text(b,0,'b','VerticalAlignment','Top','Parent',TCM_UI.ax2,'Clipping','on');
if isnan(ca)
set(TCM_UI.areaval,'String','Area: no mean value found');
set(TCM_UI.areaval,'ForegroundColor','red');
else
plot(TCM_UI.ax2,[a a b b],[0 TCM_Data.fun([ca ca]) 0],'red','LineWidth',2);
plot(TCM_UI.ax2,ca,0,'+black');
plot(TCM_UI.ax2,[ca ca],[0 TCM_Data.fun(ca)],'black:','LineWidth',1.5);
text(ca,0,'c_a','VerticalAlignment','Top','Parent',TCM_UI.ax2,'Clipping','on');
ind = x>a & x 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