# FunctionAnalyzer Maplet
# Copyright Maplesoft, a division of Waterloo Maple Inc. 2003
#
# Last updated May, 2005
# 
# The FunctionAnalyzer maplet provides a convenient user interface for
# the analysis necessary to prepare the graph of a function. Of course,
# the plot can be obtained with the Maplet, the purpose is to provide
# support for gathering the information needed to produce a sketch of
# the plot by hand.
# 
# The user enters a function (y) to be analyzed, the Maplet determines
# the first (Dy) and second derivative (D2y) of this function. Sign plots
# and ordinary plots for any combination of the function and its first two
# derivatives can be produced. User-specified expressions involving y, Dy,
# and D2y, and the independent variable can be evaluated or solved.
# 
# To run this maplet, click the Execute (!!!) button in the context bar.
# 
restart;
# ############################################################
# 
# Supporting procedures for the Maplet
# 
# ############################################################
FuncAnalEval := proc( EXPR, f, Df, D2f, t, T, cmd )
  local str, val1, val2;
  try
    val1 := eval( EXPR, [ y=f, Dy=Df, D2y=D2f ] );
    val2 := eval( val1, t = T );
    if nargs>6 then
      if cmd="simplify" then val2 := simplify( val2 ) end if;
      if cmd="normal"   then val2 := normal( val2 ) end if;
      if cmd="expand"   then val2 := expand( val2 ) end if;
      if cmd="evalf"    then val2 := evalf( val2 ) end if;
    end if;
    str := sprintf( "%a = %a, when %a = %a", EXPR, val2, t, T );
  catch:
    str := "Error during evaluation, please check and retry.";
  end try;
  return str
end proc:
FuncAnalSolve := proc( EQN, f, Df, D2f, t, cmd )
  local eqn, sol, str;
  try
    eqn := eval( EQN, [ y=f, Dy=Df, D2y=D2f ] );
    sol := { solve( eqn, t ) };
    if nargs>5 then
      if cmd="simplify" then sol := simplify( sol ) end if;
      if cmd="normal"   then sol := normal( sol ) end if;
      if cmd="expand"   then sol := expand( sol ) end if;
      if cmd="evalf"    then sol := evalf( sol ) end if;
    end if;
    str := sprintf( "%a, for %a=%a", EQN, t, sol);
  catch:
    str := "Error during solve, please check and retry.";
  end try;
  return str
end proc:
SignPlot := proc( f, dom, desc, Y, C )
  local neg_part, pos_part, P1, P2, x, xhi, xlo;
  pos_part := proc( f, y )
    if nargs=1 then y := 1 end if;
    if type(f,numeric) then
      return `if`(signum(f)=1,y,undefined)
    else
      return 'procname'( 'args' )
    end if
  end proc:
  neg_part := proc( f, y )
    if nargs=1 then y := 1 end if;
    if type(f,numeric) then
      return `if`(signum(f)=-1,y,undefined)
    else
      return 'procname'( 'args' )
    end if
  end proc:
  x, xlo, xhi := lhs(dom), op(evalf(rhs(dom)));
  P1 := plot( [pos_part(f,Y),neg_part(f,Y)], dom, color=C, thickness=3 );
  P2 := plots[textplot]( [(xlo+39*xhi)/40, Y, desc], align=ABOVE );
  return plots[display]( [P1, P2] );
end proc:
FuncAnalPlot := proc( f, Df, D2f, t, T, Y, EXEC )
  local p_list, c_list, s_list, ls_list, cmd, hi, lo, P_fns, P_Df_sign, P_D2f_sign;\

  p_list := NULL;
  c_list := NULL;
  s_list := NULL;
  ls_list := NULL;
  if f[2] then
    p_list := p_list, f[1];
    c_list := c_list, f[3];
    if f[4]='POINT' then
      s_list := s_list, f[4];
      ls_list := ls_list, 'SOLID';
    else
      s_list := s_list, 'LINE';
      ls_list := ls_list, f[4];
    end if
  end if;
  if Df[2] then
    p_list := p_list, Df[1];
    c_list := c_list, Df[3];
    if Df[4]='POINT' then
      s_list := s_list, Df[4];
      ls_list := ls_list, 'SOLID';
    else
      s_list := s_list, 'LINE';
      ls_list := ls_list, Df[4];
    end if
  end if;
  if D2f[2] then
    p_list := p_list, D2f[1];
    c_list := c_list, D2f[3];
    if D2f[4]='POINT' then
      s_list := s_list, D2f[4];
      ls_list := ls_list, 'SOLID';
    else
      s_list := s_list, 'LINE';
      ls_list := ls_list, D2f[4];
    end if
  end if;

  try
    if p_list<>NULL then
      cmd := sprintf( "plot( %a, %a=%a, view=[%a,%a], color=%a, style=%a, linestyle=%a )",\

                       [p_list], t, T, T, Y, [c_list], [s_list], [ls_list] );
    else
      cmd := sprintf( "plot( 'undefined', %a=%a, view=[%a,%a] )", t, T, T, Y );
    end if;
    P_fns := parse( cmd, statement );
    lo,hi := op(Y);
    P_Df_sign := NULL;
    P_D2f_sign := NULL;
    if Df[5] then
      P_Df_sign := SignPlot( Df[1], t=T, `y'`, lo+(hi-lo)/10, Df[6] )
    end if;
    if D2f[5] then
      P_D2f_sign := SignPlot( D2f[1], t=T, `y''`, lo+(hi-lo)/20, D2f[6] )
    end if;
    return plots[display]( [P_fns, P_Df_sign, P_D2f_sign] )
  catch:
    return NULL
  end try;
end proc:
_COLORS := ['blue',    'cyan',   'brown',     'gold',   'green', 'khaki',
            'magenta', 'maroon', 'orange',    'pink',   'plum',  'red',
            'sienna',  'tan',    'turquoise', 'violet', 'wheat', 'yellow']:
_STYLES := ['SOLID', 'POINT','DOT','DASH','DASHDOT']:
# ############################################################
# 
# Definition of the Maplet
# 
# ############################################################
use Maplets, Maplets:-Elements in

FunctionAnalyzer := Maplet(

################ window layout ################
 Window(
        'title' = "Graphical Function Analysis",
        layout=BoxRow( 
         'hscroll' = as_needed,
         'vscroll' = as_needed,
         [   # c 1 - plot window and control
          [  # r 1
           Plotter['PL1']( 'height' = 300,
                           plot(undefined,x=-10..10,view=[-10..10,-10..10]) )
          ], # r 1
          [  # r 2
           GridLayout(
            [   # c 1
             [  # r 1
              "Horizontal axis",
              TextField['X0']( 'value'=-10, 'width'=4 ),
              " <= ",
              TextField['X']( 'value'="x", 'width'=4,
                              'onchange'='new_indep_var' ),
              " <= ", 
              TextField['X1']( 'value'=10, 'width'=4 )
             ], # r 1
             [  # r 2
              "Vertical axis",
              TextField['Y0']( 'value'=-10, 'width'=4 ),
              " <= ",
              TextField['Y']( 'value'="y", 'width'=4 ),
              " <= ",
              TextField['Y1']( 'value'= 10, 'width'=4 )
             ]  # r 2
            ]   # c 1
           )
          ],  # r 2
          [   # r 3 - plot command
           BoxLayout( 'border'=true, 'caption'="Evaluate Expression",
            [
             [
              "Evaluate the expression (use y, Dy, D2y)",
              TextField['expr']( 'value'="", 'width'=15 )
             ],
             [
              "when the independendent variable (",
              TextField['X2']( 'value'="x = ",
                               'width'=3,
                               'editable'=false ),
              ") has the value",
              TextField['x_value']( 'value'="",
                                    'width'=5 )
             ],
             [
              TextBox['result_eval']( 3..30,
                                      "",
                                      'popupmenu' = 'PM_eval',
                                      'editable' = false )
             ],
             [
              Button( "Evaluate expression at specified value",
                      'onclick'='eval_expr'
                    )
             ]
            ]
           )
          ],  # r 3
          [   # r 4
           Button( "Update plot",
                   'onclick' =
                     Evaluate('PL1'
                               = 'FuncAnalPlot( [y,y_plot,y_color,y_style], [Dy,Dy_plot,Dy_color,Dy_style,Dy_sign,[Dy_pos,Dy_neg]], [D2y,D2y_plot,D2y_color,D2y_style,D2y_sign,[D2y_pos,D2y_neg]], X, X0..X1, Y0..Y1, true )')\

                 ),
           Button( "Reset all fields",
                   'onclick' = 'reset'
                 ),
           Button( "Close Window",
                   Shutdown()
                 )
          ]   # r 4
         ],
         [ # c 2 - y, Dy, D2y
          [  # r 1 - function
           BoxLayout( 'border'=true, 'caption'="Function",
            [
             [
              "y = ",
              TextField['y']('value'='undefined',
                             'popupmenu'='PM_y',
                             'onchange'=Action(Evaluate( 'Dy'  = 'diff( y,X)' ),
                                               Evaluate( 'D2y' = 'diff(Dy,X)' ) ),\

                             'width'=30 ),
              Button["Btn_Invisible"]("", Action(), visible=false)
             ],
             [
              CheckBox['y_plot']( 'value'=false, 'caption'="Include in plot" ),
              "Color:",
              ComboBox['y_color']( 'magenta', _COLORS ),
              "Style:",
              ComboBox['y_style']( 'SOLID', _STYLES )
             ]
            ]
           )
          ], # r 1
          [  # r 2 - 1st derivative
           BoxLayout( 'border'=true, 'caption'="First Derivative",
            [
             [
              "y' = Dy =",
              TextField['Dy']( 'value'='undefined',
                               'popupmenu'='PM_Dy',
                               'onchange'=Action(Evaluate( 'y' = 'undefined' ),
                                                 Evaluate( 'D2y'='diff(Dy,X)' )
                                                ),
                               'width'=30 )
             ],
             [
              CheckBox['Dy_plot']( 'value'=false, 'caption'="Include in plot" ),
              "Color:",
              ComboBox['Dy_color']( 'gold', _COLORS ),
              "Style:",
              ComboBox['Dy_style']( 'SOLID', _STYLES )
             ],
             [
              CheckBox['Dy_sign']( 'value'=false, 'caption'="Show sign chart" ),
              "Colors: increasing:",
              ComboBox['Dy_pos']( 'red', _COLORS ),
              "decreasing:",
              ComboBox['Dy_neg']( 'blue', _COLORS )
             ]
            ]
           )
          ], # r 2
          [  # r 3 - 2nd derivative
           BoxLayout( 'border'=true, 'caption'="Second Derivative",
            [
             [
              "y'' = D2y =",
              TextField['D2y']( 'value'='undefined',
                                'popupmenu'='PM_D2y',
                                'width'=30 )
             ],
             [
              CheckBox['D2y_plot']( 'value'=false, 'caption'="Include in plot" ),\

              "Color:",
              ComboBox['D2y_color']( 'cyan', _COLORS ),
              "Style:",
              ComboBox['D2y_style']( 'SOLID', _STYLES )
             ],
             [
              CheckBox['D2y_sign']( 'value'=false,
                                    'caption'="Show sign chart" ),
              "Colors: concave up:",
              ComboBox['D2y_pos']( 'green', _COLORS ),
              "concave down:",
              ComboBox['D2y_neg']( 'orange', _COLORS )
             ]
            ]
           )
          ], # r 3
          [  # r 4 - evaluate or solve
           BoxLayout( 'border'=true,
                      'caption'="Solve an Equation or Inequality",
            [
             [
              "Enter equation to be solved (use y, Dy, D2y):",
              TextField['eqn']( 'value'="", 'width'=20 )
             ],
             [
              TextBox['result_solve']( 3..30, "", 'popupmenu' = 'PM_solve',
                                       'editable'=false )#, 'quotedtext' = true )

             ],
             [
              Button( "Attempt to solve equation",
                      'onclick'='solve_eqn'
                    )
             ]
            ]
           )
          ] # r 4
         ]
        ),
        defaultbutton="Btn_Invisible"
       ),   # end Window

################ action definitions ################
       Action['reset'](
        SetOption( 'X0' =-10 ),
        SetOption( 'X1' = 10 ),
        SetOption( 'Y0' =-10 ),
        SetOption( 'Y1' = 10 ),
        SetOption( 'Y'  = "y" ),
        SetOption( 'y' = 'undefined' ),
        SetOption( 'Dy'  = 'undefined' ),
        SetOption( 'D2y'  = 'undefined' ),
        SetOption( 'y_plot' = false ),
        SetOption( 'Dy_plot' = false ),
        SetOption( 'D2y_plot' = false ),
        SetOption( 'y_color' = "magenta" ),
        SetOption( 'Dy_color' = "gold" ),
        SetOption( 'D2y_color' = "cyan" ),
        SetOption( 'y_style' = "SOLID" ),
        SetOption( 'Dy_style' = "SOLID" ),
        SetOption( 'D2y_style' = "SOLID" ),
        SetOption( 'Dy_sign' = false ),
        SetOption( 'D2y_sign' = false ),
        SetOption( 'Dy_neg' = blue ),
        SetOption( 'D2y_neg' = orange ),
        SetOption( 'Dy_pos' = red ),
        SetOption( 'D2y_pos' = green ),
        SetOption( 'result_eval' = " " ),
        SetOption( 'expr' = "" ),
        SetOption( 'x_value' = "" ),
        SetOption( 'eqn' = "" ),
        SetOption( 'result_solve' = "" ),
        SetOption( 'X' = "x" ),
        SetOption( 'X2' = "x = " ),
        NULL 
                      ),
       Action['eval_expr'](
        Evaluate('result_eval'='FuncAnalEval( expr, y, Dy, D2y, X, x_value )')
                          ),

       Action['solve_eqn'](
        Evaluate('result_solve'='FuncAnalSolve( eqn, y, Dy, D2y, X )')
                          ),

       Action['new_indep_var'](
        Evaluate('X2'='sprintf("%s =",X)'),
        Evaluate('Dy' = 'diff(y,X)' ),
        Evaluate('D2y' = 'diff(y,X$2)' )
                              ),

################ popup menu definitions ################
       PopupMenu['PM_y'](
        Menu("Manipulate",
         MenuItem("simplify",
                  Evaluate('y'='simplify(y)')),
         MenuItem("normal",
                  Evaluate('y'='normal(y)')),
         MenuItem("expand",
                  Evaluate('y'='expand(y)')),
         MenuItem("convert to floating-point",
                  Evaluate('y'='map(evalf,y)')),
         NULL
         ),
        MenuSeparator(),
        MenuItem("Clear Field",
                 SetOption('y'=""))
        ),

       PopupMenu['PM_Dy'](
        Menu("Manipulate",
         MenuItem("simplify",
                  Evaluate('Dy'='simplify(Dy)')),
         MenuItem("normal",
                  Evaluate('Dy'='normal(Dy)')),
         MenuItem("expand",
                  Evaluate('Dy'='expand(Dy)')),
         MenuItem("convert to floating-point",
                  Evaluate('Dy'='map(evalf,Dy)')),
         NULL
         ),
        MenuSeparator(),
        MenuItem("Clear Field",
                 SetOption('Dy'=""))
        ),

       PopupMenu['PM_D2y'](
        Menu("Manipulate",
         MenuItem("simplify",
                  Evaluate('D2y'='simplify(D2y)')),
         MenuItem("normal",
                  Evaluate('D2y'='normal(D2y)')),
         MenuItem("expand",
                  Evaluate('D2y'='expand(D2y)')),
         MenuItem("convert to floating-point",
                  Evaluate('D2y'='map(evalf,D2y)')),
         NULL
         ),
        MenuSeparator(),
        MenuItem("Clear Field",
                 SetOption('D2y'=""))
        ),

   PopupMenu['PM_eval'](
      Menu("Manipulate Results",
         MenuItem("simplify",
                  Evaluate( 'result_eval'
                  = 'FuncAnalEval( expr, y, Dy, D2y, X, x_value, "simplify" )' )),\

         MenuItem("normal",
                  Evaluate( 'result_eval'
                  = 'FuncAnalEval( expr, y, Dy, D2y, X, x_value, "normal" )' )),
         MenuItem("expand",
                  Evaluate( 'result_eval'
                  = 'FuncAnalEval( expr, y, Dy, D2y, X, x_value, "expand" )' )),
         MenuItem("evalf",
                  Evaluate( 'result_eval'
                  = 'FuncAnalEval( expr, y, Dy, D2y, X, x_value, "evalf" )' ))
      ),
      MenuSeparator(),
      MenuItem("Clear Field",
               SetOption( 'result_eval' = "" ))
   ),

   PopupMenu['PM_solve'](
      Menu("Manipulate Results",
         MenuItem("simplify",
                  Evaluate( 'result_solve'
                           = 'FuncAnalSolve( eqn, y, Dy, D2y, X, "simplify" )' )),\

         MenuItem("normal",
                  Evaluate( 'result_solve'
                           = 'FuncAnalSolve( eqn, y, Dy, D2y, X, "normal" )' )),
         MenuItem("expand",
                  Evaluate( 'result_solve'
                           = 'FuncAnalSolve( eqn, y, Dy, D2y, X, "expand" )' )),
         MenuItem("evalf",
                  Evaluate( 'result_solve'
                           = 'FuncAnalSolve( eqn, y, Dy, D2y, X, "evalf" )' ))
      ),
      MenuSeparator(),
      MenuItem("Clear Field",
               SetOption( 'result_solve' = "" ))
   )

):

end use:
# ############################################################
# 
# Launch the Maplet
# 
# ############################################################
Maplets:-Display(FunctionAnalyzer);


