BeginPackage["Student`"]

(* 
Copyright 1990 Robert Campbell 
Berkeley
campbell@garnet.berkeley.edu
*)

Bezier::usage = "Bezier[{{x0,y0},...,{x3,y3}}]
computes the cubic bezier curve with control points
{xi,yi}, prints out its parametric description, and
then draws the curve and its control polygon, and
returns the parametric description.
Bezier[{{x0,y0},...},nodraw] returns a list of the two
components of the parametric description."

Midpoint::usage = "Midpoint[f,{x,xmin,xmax},n] will compute the numeric
integral of the function f(x) on the interval [xmin,xmax],
using the midpoint method with n divisions. The numeric
result and the error, or difference from the system command
NIntegrate, will be printed and the result returned will
be a graph of the	process. Midpoint[f,{x,xmin,xmax},n,nodraw]
prints the error and returns the numeric result without graphing it."
	
MultiPlot3D::usage = "MultiPlot3D[{f1,...},{x_,x0_,x1_},{y_,y0_,y1_}]
extends the function Plot3D by allowing the user to plotseveral functions of two variables on the same set of axes.The result is a Graphics3D object, not a SurfaceGraphics object.
The function can use the same options as Plot3D (Not entirely true at
present 14 Feb, 1990). THIS IS A VERY SLOW AND EXPENSIVE PROGRAM."

	
Newton::usage = "Newton[f,{x,xstart},n] will use n steps
of Newton's method to try to compute a root of f(x),
starting at the point x=xstart and will return a graph of
the process. Newton[f,{x,xstart},n,nodraw] will print the
result at each step of the process, and will return a list
of the values."

Polyapprox::usage = "Polyapprox[f,x,{x0,...,xn}]
will compute the polynomial of degree (n-1) which interpolates
f(x) at the points {x0,...}. It will print this polynomial
and then graph it on the same plot with f along with f."

Trapezoid::usage = "Trapezoid[f,{x,xmin,xmax},n] will
compute the numeric integral of f(x) on the interval
[xmin,xmax], using the trapezoid method with n divisions.
The numeric result and the error, or difference from the
system command NIntegrate, will be printed and a
graph will be returned. Trapezoid[f,{x,xmin,xmax},n,nodraw]
prints the error and returns the numeric result without graphing it."

nodraw;

Begin["`Private`"]

Bezier[{{x0_,y0_},{x1_,y1_},{x2_,y2_},{x3_,y3_}}]:=
(* Compute and display the cubic Bzier curve with the specified control pts.*)
Block[{xcoord,ycoord,plot,points,poly,paramname,t},
	(* Define the x and y coordinates of the point on the curve	with parameter un. Generate a new name, of the form un, for the	parametric variable used in Bezier. *)
	paramname = Unique["u"];
	xcoord=x0*(1-paramname)^3 + 3*x1*paramname*(1-paramname)^2 \
		+ 3*x2*paramname^2*(1-paramname) + x3*paramname^3;
	ycoord=y0*(1-paramname)^3 + 3*y1*paramname*(1-paramname)^2 \
		+ 3*y2*paramname^2*(1-paramname) + y3*paramname^3;
	Print["The Bzier curve is:"];
	Print["x(",paramname,") = ",xcoord];
	Print["y(",paramname,") = ",ycoord];
	(* recompute the curve using an internal variable parameter *)
	xcoord=x0*(1-t)^3 + 3*x1*t*(1-t)^2 + 3*x2*t^2*(1-t) + x3*t^3;
	ycoord=y0*(1-t)^3 + 3*y1*t*(1-t)^2 + 3*y2*t^2*(1-t) + y3*t^3;
	(* Now draw the curve invisibly *)
	plot=ParametricPlot[{xcoord,ycoord},{t,0,1},DisplayFunction->Identity];
	(* Prepare to draw the control points *)
	points={Point[{x0,y0}],Point[{x1,y1}],Point[{x2,y2}],Point[{x3,y3}]};
	(* Connect the control points to get the control polygon. *)
	poly=Line[{{x0,y0},{x1,y1},{x2,y2},{x3,y3}}];
	(* combine the graph with the control points, and display them. *)
	Show[Graphics[{PointSize[0.02],points,poly}],plot,\
		DisplayFunction->$DisplayFunction]
]

Bezier[{{x0_,y0_},{x1_,y1_},{x2_,y2_},{x3_,y3_}},nodraw]:=
(* Compute the cubic Bzier curve with the specifiedcontrol pts and return it as a parametric curve in "un".*)
Block[{xcoord,ycoord,paramname},
	(* Generate a new name, of the form un, for the parametric	variable used in Bezier. *)
	paramname = Unique["u"];
	xcoord=x0*(1-paramname)^3 + 3*x1*paramname*(1-paramname)^2 \
		+ 3*x2*paramname^2*(1-paramname) + x3*paramname^3;
	ycoord=y0*(1-paramname)^3 + 3*y1*paramname*(1-paramname)^2 \
		+ 3*y2*paramname^2*(1-paramname) + y3*paramname^3;
	{xcoord,ycoord}
]	

Midpoint[f_,{x_,xmin_,xmax_},n_]:=
(* This routine will compute the numeric integral of the
function f(x) on the interval [xmin,xmax], using themidpoint method with n divisions. The numeric result and
the error, or difference from the system NIntegrate, will
be printed and the result returned will be a graph of the
process.*)
Block[{mp,error,i,j,rectlist,linelist,val,step,plot},	mp=0;
	step=(xmax-xmin)/n;
	For[i=0,i<n,i++,
		val[i]=f/. x-> xmin+(i+0.5) step;
		mp=mp+val[i] step;
	];
	rectlist=Table[Rectangle[{xmin+j*step,0},{xmin+(j+1) step,val[j]}]\
		,{j,0,n-1}];
	linelist=Table[Line[{{xmin+j*step,0},{xmin+j*step,val[j]},\
		{xmin+(j+1) step,val[j]},{xmin+(j+1) step,0}}],{j,0,n-1}];
	rectlist=Prepend[rectlist,GrayLevel[0.6]];
	linelist=Prepend[linelist,GrayLevel[0]];
	plot=Plot[f,{x,xmin,xmax},DisplayFunction->Identity];
	error=Abs[N[mp-NIntegrate[f,{x,xmin,xmax}]]];
	Print["The integral is approximately ",N[mp]];
	Print["error=",error];
	Show[Graphics[Join[rectlist,linelist]],plot,\
		DisplayFunction->$DisplayFunction]
]

Midpoint[f_,{x_,xmin_,xmax_},n_,nodraw]:=
(* This routine will compute the numeric integral of the
function f(x) on the interval [xmin,xmax], using themidpoint method with n divisions. The error, ordifference from the system NIntegrate, will be printedand the result returned will be the numeric result. *)
Block[{mp,error,i,j,rectlist,val,step},	mp=0;
	step=(xmax-xmin)/n;
	For[i=0,i<n,i++,
		val[i]=f/. x-> xmin+(i+0.5) step;
		mp=mp+val[i] step;
	];
	error=Abs[N[mp-NIntegrate[f,{x,xmin,xmax}]]];
	Print["error=",error];
	N[mp]
]

MultiPlot3D[fcns_List,{x_,x0_,x1_},{y_,y0_,y1_},opts___]:=
(* This routine extends the function Plot3D by allowing the
user to plot several functions of two variables on the same
set of axes. The result is a Graphics3D object, not aSurfaceGraphics object. At present those options which are
valid for Graphics3D, as well as the Mesh option, are not
properly passed or acted on *)
Block[{plotopts,showopts,plotoptstest},
	(* Strip out those options which are unique to Plot3D, as
	opposed to those options understood by Graphics3D *)
	plotoptstest=(MatchQ[#,PlotPoints->_] ||		MatchQ[#,ClipFill->_] || MatchQ[#,HiddenSurface->_] ||
		MatchQ[#,Mesh->_] || MatchQ[#,MeshStyle->_])&;
	plotopts=Cases[{opts},_?plotoptstest];
	showopts=Cases[{opts},_?(Not[plotoptstest[#]]&)];
	Show[Map[Graphics3D,Map[Plot3D[#,{x,x0,x1},{y,y0,y1},plotopts,\
		DisplayFunction->Identity]&,fcns]],showopts,\
		DisplayFunction->$DisplayFunction]
]
	
Newton[f_,{x_,xstart_},n_,nodraw]:=
(* This routine will use Newton's method to solve the equation f(x)=0 for x.
The initial guess, xstart, is supplied by the user, as is the desired number
of iterations, n. At each step the routine prints out the new estimate, xi, the
value of the function at xi, and the amount the new estimate differs from the
previous estimate. *)
Block[{fprime,xi,xiold,xlist},
	xlist = {xstart};	
	fprime:=D[f,x];
	xi=xstart;                (* We start out with our first guess *)
	For[i=1,i<n,i++,
		xiold=xi;                (* Keep track of our last guess *)
		(* Use Newton's Method to find a better guess.		We keep track of 60 digits internally. *)
		xi=xi-N[((f/fprime)/.x->xi),60];		AppendTo[xlist,xi];   (* And add it to our list of guesses *)
		Print[i];
		Print["xi=",N[xi,10]];         (* Print guess to 10 digits *)
		Print["f(xi)=",N[(f /. x->xi),10]];(* Print function value *)
		Print["xi-x(i-1)=",N[xi-xiold,10]];(* Change in guess *)
		(* Print out the relevant information *)
	];
	N[xlist,10]                   (* Return 10 digits as a final value *)
]

Newton[f_,{x_,xstart_},n_]:=
(* This routine will use Newton's method to solve the equation f(x)=0 for x.
The initial guess, xstart, is supplied by the user, as is the desired number
of iterations, n. The output is a graph which shows a plot of the function f,
each successive estimate, xi, and the value of f at xi. The plot also draws
the tangent line to f at {xi,f(xi)} which is used to get the next estimate. *)
Block[{fprime,xi,xiold,i,pointlist,linelist,xmin,xmax,plot},
	(* Keep track of max and min x to get the plot bounds *)
	xmin = xstart;
	xmax = xstart;
	fprime := D[f,x];
	xi = N[xstart];
	(* Put in the initial elements of the graphics lists *)
	pointlist={Point[N[{xstart,f/.x->xstart}]]};
	linelist={};
	For[i=1,i<=n,i++,
		xiold = xi;             (* Save the old value of xi *)
		xi = xi-((f/fprime)/.x->xi);		(* Use Newton's Method to find a better guess *)
		(* Draw the line and the new point *)
		pointlist=Append[pointlist,Point[{xi,f/.x->xi}]];
		linelist=Append[linelist,Line[{{xiold,f/.x->xiold},{xi,0}}]];
		(* Keep track of max and min x to get the plot bounds *)
		If[xi<xmin,xmin=xi,];
		If[xi>xmax,xmax=xi,];
	];
	(* First we plot the graph invisibly, adding some space on each end *)
	plot=Plot[f,{x,xmin-0.2(xmax-xmin),xmax+0.2(xmax-xmin)},\
		DisplayFunction->Identity];
	(* Then we plot all the desired information *)
	Show[Graphics[Join[{PointSize[0.02]},pointlist,linelist]],plot\
		,DisplayFunction->$DisplayFunction]
]

Polyapprox[f_,x_,p_]:=
(* This function will find the polynomial of least degree which
passes through the points {x0,f(x0)},{x1,f(x1},... It will print
this polynomial and then plot the polynomial on the same graphwith f(x). Actually, the command Fit can be used to do the same thing.*)
Block[{i,soln,pts,points,a,plot},
	pts=Table[{p[[i]],f/.x->p[[i]]},{i,Length[p]}];
	poly=Fit[pts,Table[x^i,{i,0,Length[p]-1}],x];
	points=Table[Point[{p[[i]],poly/.x->p[[i]]}],{i,1,Length[p]}];
	plot=Plot[{f,poly},{x,Min[p]-1,Max[p]+1},DisplayFunction->Identity];
	Show[Graphics[{PointSize[0.02],points}],plot,\
		DisplayFunction->$DisplayFunction];
	poly
	]
	
Trapezoid[f_,{x_,xmin_,xmax_},n_]:=
(* This routine will compute the numeric integral of the
function f(x) on the interval [xmin,xmax], using thetrapezoid method with n divisions. The numeric result and
the error, or difference from the system NIntegrate, will
be printed and the result returned will be a graph of the
process.*)
Block[{trap,error,i,j,rectlist,linelist,val,step,plot},
Array[val,n,0];
	mp=0;
	step=(xmax-xmin)/n;
	val[0]=f/. x->xmin;
	For[j=0,j<n,j++,
		val[j+1]=f/. x-> xmin+(j+1)*step];
		j=.;
	trap=Sum[(val[i]+val[i+1])*step/2,{i,0,n-1}];
	rectlist=Table[Polygon[{{xmin+j*step,0},{xmin+j*step,val[j]}\
		,{xmin+(j+1) step,val[j+1]},{xmin+(j+1) step,0}}],{j,0,n-1}];
	linelist=Table[Line[{{xmin+j*step,0},{xmin+j*step,val[j]},\
		{xmin+(j+1) step,val[j+1]},{xmin+(j+1) step,0}}],{j,0,n-1}];
	rectlist=Prepend[rectlist,GrayLevel[0.6]];
	linelist=Prepend[linelist,GrayLevel[0]];
	plot=Plot[f,{x,xmin,xmax},DisplayFunction->Identity];
	error=Abs[N[trap-NIntegrate[f,{x,xmin,xmax}]]];
	Print["The integral is approximately ",N[trap]];
	Print["error=",error];
	N[trap];
	Show[Graphics[Join[rectlist,linelist]],plot,\
		DisplayFunction->$DisplayFunction]
]

Trapezoid[f_,{x_,xmin_,xmax_},n_,nodraw]:=
(* This routine will compute the numeric integral of the
function f(x) on the interval [xmin,xmax], using thetrapezoid method with n divisions. The error, ordifference from the system NIntegrate, will be printedand the result returned will be the numeric result. *)
Block[{trap,error,j,val,step},
Array[val,n,0];
	mp=0;
	step=(xmax-xmin)/n;
	val[0]=f/. x->xmin;
	For[j=0,j<n,j++,
		val[j+1]=f/. x-> xmin+(j+1)*step];
		j=.;
	trap=Sum[(val[i]+val[i+1])*step/2,{i,0,n-1}];
	error=Abs[N[trap-NIntegrate[f,{x,xmin,xmax}]]];
	Print["error=",error];
	N[trap]
]

End[]
EndPackage[]


