#
#--> lintransplot( A, ... )
#
# Generates a visual display of the the linear transformation of a real 2 by 2 matrix A.
# The 2 dimensional plot returned graphs the unit circle on the left and the transformed
# circle on the right.  The mapping is indicated in color.  The point [1,0] is red and
# by looking for the red point in the transformed object one can see where each unit vector
# is trasnformed to.
# The code computes the determinant, singular values, and eigenvalues of A
# and displays these values in text.
# The determinant measures the change in area under A applied to the unit disk.
# The singular values measure the relative magnitudes of the axes (singular vectors).
# The singular vectors (axes of the ellipse) are drawn as lines in black.
# The eigen vectors are drawn as arrows in color.
#
# Optional arguments are as follows
#
#   numvectors=n   n+1 is the number of unit vectors used (default 32)
#   digits=d  number of decimal places for output of eigenvalues and eigenvectors (default 3)
#
# Special defaults chosen are
#   scaling=constrained
#   axes=none
#
# The default scaling=constrained means you can read off what the eigenvalues are
# by visually estimating the magnitude of the eigenvectors relative to the unit circle.
# These special defaults will be overridden by specifying optional arguments.
# Also, other optional arguments e.g. title=..., view=[a..b,c..d], which are the same
# as those for plot, are passed on to plots[display] .
#
# Author MBM August/2001.
#

lintransplot := proc(AA::{matrix(2,2,realcons),Matrix(2,2,realcons)})
local A, opts, s, d, maxx, maxy, minx, miny, fix, angle, det, sigma,
      svect, lambda, v, v1, v2, uv1, uv2, ec1, ec2, matrixinfo, eigenvectors,
      N, pi, U, V, tminx, tmaxx, tminy, tmaxy, OFF, transobjectplot,
      transxaxis, transyaxis, objectplot, objects, eigencase; 


   if type(AA,matrix) then A := Matrix(AA) else A := AA fi;
   opts := [args[2..nargs]];
   s := circle;
   if hasoption(opts,'digits=posint','d','opts') then 
      else d := 3 fi;

   maxx := 1.5; maxy := 1.5; minx := -1.5; miny := -1.5;

   fix := proc(x)
      if type(x,float) then fnormal(x,d)
      elif type(x,complex(float)) then
           if fnormal(Im(x))=0.0 then fnormal(Re(x),d) else fnormal(x,d) fi
      elif hastype(x,radical) then evalf(x,d);
      else x
      fi
   end;

   angle := proc(u) local pi,theta;
      pi := evalf(Pi);
      theta := arccos(u[1]);
      if u[2]<0 then 2*pi-theta else theta fi;
   end;

use LinearAlgebra in

   # Compute the determinant of A
   det := Determinant(A); if det=0.0 then det := 0 fi;
   # Compute the singular values and vectors of A
   sigma := evalf( Svd(convert(A,matrix),'U','left') );
   svect := convert(linalg[col](U,1),list),
            convert(linalg[col](U,2),list);

   # Compute and process eigenvalues and eigenvectors using input
   lambda,v := Eigenvectors(A);
   lambda   := map(fix,lambda);
   (v1,v2)  := Column(v,1),Column(v,2);
   (v1,v2)  := map(fix,v1),map(fix,v2);

   matrixinfo := cat(     "det=", convert(evalf(det,d),string),
                      "  sigma=", convert(evalf(sigma[1],d),string),
                             ",", convert(evalf(sigma[2],d),string),
                     "  lambda=", convert(evalf(lambda[1],d),string),
                             ",", convert(evalf(lambda[2],d),string) );

   # Generate text for the eigenvalues and eigenvectors of A.
   eigenvectors := NULL;
   if type(lambda[1],realcons) and type(lambda[2],realcons) then
      # Test for repeated eigenvalues with geometric multiplicity 1
      eigencase := not evalb( lambda[1]=lambda[2] and
                             ( v2[1]=0 or v2[1]=0.0) and
                             ( v2[2]=0 or v2[2]=0.0) );
      uv1 := convert(op(1,GramSchmidt([evalf(v1)],normalized)), list);
      ec1 := COLOUR(HUE, evalf( angle(uv1)/2/Pi ) ),
             COLOUR(HUE, evalf( angle(-uv1)/2/Pi ) );
      if eigencase=true then
      uv2 := convert(op(1,GramSchmidt([evalf(v2)],normalized)), list);
      ec2 := COLOUR(HUE, evalf(angle(uv2)/2/Pi) ),
             COLOUR(HUE, evalf(angle(-uv2)/2/Pi) );
      fi;
   else eigencase := FAIL; # complex
   fi;

   if s='circle' then # default

         # Construct the unit circle with colour.
         N := 49; pi := evalf(Pi);
         U := [seq( [cos(2*i*pi/N), sin(2*i*pi/N)], i=0..N )];

         # For the transformed object
         V := map(convert, [seq( A.Vector(u), u=U )], list);
         tminx := min( seq(v[1],v=V) ); tmaxx := max( seq(v[1],v=V) );
         tminy := min( seq(v[2],v=V) ); tmaxy := max( seq(v[2],v=V) );
         OFF := [-max(3,abs(tmaxx-tminx)+1), 0];

         transobjectplot := 
             seq( plottools[line]( V[(i mod N)+1], V[(i+1 mod N)+1],
                   thickness=2, color=COLOUR(HUE,evalf(i/N)) ), i=0..N ); 

         # Now map the axes
         transxaxis := map( convert, [A.<0,-1.5>, A.<0,1.5>], list );
         transyaxis := map( convert, [A.<-1.5,0>, A.<1.5,0>], list );
         transxaxis := transxaxis[1], transxaxis[2];
         transyaxis := transyaxis[1], transyaxis[2];
         transobjectplot := transobjectplot,
              plottools[line]( transxaxis[1], [0,0], color=COLOUR(HUE,0.75) ),
              plottools[line]( transxaxis[2], [0,0], color=COLOUR(HUE,0.25) ),
              plottools[line]( transyaxis[1], [0,0], color=COLOUR(HUE,0.50) ),
              plottools[line]( transyaxis[2], [0,0], color=COLOUR(HUE,0.00) );

         # Include singular vectors
         transobjectplot := transobjectplot,
              plottools[line]( [0,0], sigma[1]*svect[1], color=black, thickness=2 ),
              plottools[line]( [0,0], sigma[2]*svect[2], color=black, thickness=2 ),
              plottools[line]( [0,0], -sigma[1]*svect[1], color=black, thickness=2 ),
              plottools[line]( [0,0], -sigma[2]*svect[2], color=black, thickness=2 );

         # Include eigenvectors
     if eigencase<>FAIL then 
         eigenvectors := eigenvectors,
              plottools[arrow]([0,0], lambda[1]*uv1,0.1,0.2,0.15,color=ec1[1]),
              plottools[arrow]([0,0],-lambda[1]*uv1,0.1,0.2,0.15,color=ec1[2]);
         if eigencase=true then
              eigenvectors := eigenvectors,
              plottools[arrow]([0,0], lambda[2]*uv2,0.1,0.2,0.15,color=ec2[1]),
              plottools[arrow]([0,0],-lambda[2]*uv2,0.1,0.2,0.15,color=ec2[2]);
         fi;
     fi;

         # Now generate the unit circle
         objectplot := 
             seq( plottools[line]( U[(i mod N)+1]+OFF, U[(i+1 mod N)+1]+OFF,
                   thickness=2, color=COLOUR(HUE,evalf(i/N)) ), i=0..N ); 

         # Include singular vectors
         objectplot := objectplot,
              plottools[line]( OFF,  svect[1]+OFF, color=black, thickness=2 ),
              plottools[line]( OFF, -svect[1]+OFF, color=black, thickness=2 ),
              plottools[line]( OFF,  svect[2]+OFF, color=black, thickness=2 ),
              plottools[line]( OFF, -svect[2]+OFF, color=black, thickness=2 );

	 # Include eigenvectors
    if eigencase<>FAIL then
         eigenvectors := eigenvectors,
              plottools[arrow](OFF,OFF+uv1,0.1,0.2,0.15,color=ec1[1]),
              plottools[arrow](OFF,OFF-uv1,0.1,0.2,0.15,color=ec1[2]);
         if eigencase=true then
         eigenvectors := eigenvectors,
              plottools[arrow](OFF,OFF+uv2,0.1,0.2,0.15,color=ec2[1]),
              plottools[arrow](OFF,OFF-uv2,0.1,0.2,0.15,color=ec2[2]);
         fi;
    fi;

         # Include scaled axes
         objectplot := objectplot,
              plottools[line]( OFF+[0,-1.5], OFF, color=COLOUR(HUE,0.75) ),
              plottools[line]( OFF, OFF+[0,1.5], color=COLOUR(HUE,0.25) ),
              plottools[line]( OFF+[-1.5,0], OFF, color=COLOUR(HUE,0.50) ),
              plottools[line]( OFF, OFF+[1.5,0], color=COLOUR(HUE,0.00) );

         # Include tick marks
         objectplot := objectplot,
              plots[textplot]( [OFF[1], 1,"1.0 "],align={ABOVE,LEFT} ),
              plots[textplot]( [OFF[1],-1,"-1.0 "],align={BELOW,LEFT} ),
              plots[textplot]( [OFF[1]+1,0," 1.0"],align={BELOW,RIGHT} ),
              plots[textplot]( [OFF[1]-1,0,"-1.0 "],align={BELOW,LEFT} );

   fi;

end use;

   objects := [objectplot,transobjectplot,eigenvectors];
   objects := subs( POLYGONS([])=NULL, objects ); # bug fix for plots[display]
   plots[display](objects,scaling=CONSTRAINED,op(opts),axes=none,title=matrixinfo);


end proc:
