Tutorial Maxima
***************

Version du Friday 21 May 2021 à 15H25 (Le manuel n'est pas encore
complet).

See Quel tutorial ?

Quel tutorial ?
===============

Les tutorials sur Maxima ne manquent pas.  On en ajoute ici un
Modeste Tutorial qui est bien incomplet et ne présente que
l'intérêt de décrire les éléments de base de la syntaxe.

Par contre on a ajouté une section Techniques utiles qui a
pour ambition de donner des informations sur l'algorithimique ad hoc
pour manipuler les expressions symboliques.

1 Divers tutorials Maxima
*************************

Documentation de Maxima (Maxima) peut suffire mais il y
aussi :

<http://michel.gosse.free.fr/documentation/fr/maxima.html>

<https://maxima.sourceforge.io/docs/tutorial/en/gaertner-tutorial-revision/Contents.htm>

puis

1.1 Modeste Tutorial
====================

1.1.1 Lire cela
---------------

Avant de commencer, voici quelques informations utiles.

La seule fonction de CDE qui est utilisée est (se placer entre les
balises '%%%;' et taper '<S-return>')
     %%%;
     nettoie();
     %%%;
Elle réinitialise certaines des variables utilisateur et permet ainsi
de faire en sorte que les éléments de scripts illustratifs
n'influent pas les uns sur les autres.

Il n'est pas impossible de mettre Maxima dans un état où il semble
ne plus fonctionner sur un mode de dialogue interactif.  Dans ce cas
on dispose de deux remèdes (pour les touches
Bouée de secours (NeoAppariteur))
   o- l'envoi d'un signal 'SIGINT' par la combinaison de touche '<C-x
     c>' ;

     le prompt 'MXM>' est regénéré par '<S-C p>' ;

   o- l'arrêt de Maxima par '<alt-PageDown>' suivi de son
     redémarrage par '<alt-PageUp>'

Par exemple, voici un calcul qui ne se termine pas
     %%%;
     i:1$while(i>0) do i:i+1;
     %%%;
l'arrêter par 'C-x c'.

Et finalement Maxima est documenté (see Documentation de Maxima (Maxima))
; il n'est pas interdit, voire recommandé, de s'y
référer pour plus de précision sur les fonctions utilisée dans ce
tutorial.

1.1.2 Instructions de base
--------------------------

1.1.2.1 La séparation des instructions
.......................................

Elle est assurée par les symboles '$' et ';'

     %%%; nettoie()$ 1/(1+x^2); 1/(1+x^2)$ %%%;

Dans les deux cas Maxima prend en compte l'instruction mais elle
n'affiche le résultat que si on met ';' et pas '$'.

L'utilisation d'Appariteur comme interface à Maxima permet
l'utilisation d'un autre séparateur, le symbole '@'

     %%%; nettoie()$ 1/(1+x^2)@ %%%;

Avec '@' l'affichage se fait sous forme d'une image de l'expression à
afficher qui est a priori plus esthétique.

1.1.2.2 L'affectation de valeurs
................................

Un symbole peut être affecté à une valeur.  L'instruction
d'affectation est ':' (pas '=')

     %%%; nettoie()$ x:1;x+1; %%%;
Inversement la 'désaffectation' de valeur se fait par l'affectation
de la variable à elle-même mais 'quotée'

     %%%; nettoie()$ x:1; x:'x;x+1; %%%;

Une question peut se poser : si on affecte un symbole comme valeur à
un autre symbole, la valeur du symbole affecté est-elle le symbole
affecté ou la valeur de ce symbole s'il en a une ?

et le mieux pour répondre à cette question est d'essayer

     %%%; nettoie()$ x:1; y:x; x:'x; y; %%%;

On voit que la valeur finale de 'y' reste 1 alors que x a été
désaffectée.  Et donc ce qui est affecté par ':' est la valeur du
symbole (s'il en a une) et nom le symbole lui-même.

Sur le plan purement sémantique on a utilisé le mot 'symbole' pour
désigner la lettre à laquelle peut être affectée une valeur.  Dans
la suite on parlera plutôt de 'variable'.

Et la syntaxe des variables ne se limite pas à une lettre mais à une
suite de lettres qui, de préférence, ne comportent pas d'accents et
où sont exclus des symboles comme ':', ' ', ';'...

1.1.2.3 Les listes
..................

Une liste est définie comme

     %%%; nettoie()$
     l:[a,b,c,d];
     %%%;
les éléments syntaxiques sont les crochets ('[' et ']') et la
virgule ',' entre lesquels sont inséré des objets de Maxima.  Soit
des variables, des nombres, ou encore des listes, par exemple

     %%%; nettoie()$
     l:[a,1,1/2,1.0e-3,1.23,[g,k,2]];
     %%%;

La manipulation des listes se fait comme suit.

Un élément de la liste

     %%%; nettoie()$
     l:[a,b,c]$ l[2];
     %%%;

La longueur de la liste

     %%%; nettoie()$
     l:[a,b,c]$ length(l);
     %%%;

La liste privée de son premier élément

     %%%; nettoie()$
     l:[a,b,c]$ L:rest(l);
     %%%;

Le 1ier élément de la liste

     %%%; nettoie()$
     l:[a,b,c]$ L:first(l);
     %%%;
On peut avoir les éléments suivant par 'second', 'third',...  mais
bien évidemment 'l[1]', 'l[2]',...  restent valables pour obtenir des
éléments d'une liste.

La liste renversée

     %%%; nettoie()$
     l:[a,b,c]$ L:reverse(l);
     %%%;
La liste trié (ici par l'ordre lexicographique sur les noms de
variables)

     %%%; nettoie()$
     l:[b,c,a]$ L:sort(l);
     %%%;

Fusion de 2 listes

     %%%; nettoie()$
     l1:[a,b,c]$ l2:[1,2,3]$ L:append(l1,l2);
     %%%;

Destruction d'un élément de la liste

     %%%; nettoie()$
     l:[a,b,c]$ L:delete(b,l);
     %%%;

Arithmétique

     %%%; nettoie()$
     [a,b,c]+[A,B,C];
     [a,b,c]-[A,B,C];
     [a,b,c]*[A,B,C];
     [a,b,c]/[A,B,C];
     [a,b,c].[A,B,C];
     %%%;

les opérations se font terme à terme pour des listes de même
longueur.  '.'  est un produit scalaire.

Affectation terme à terme entre deux listes

     %%%; nettoie()$
     [a,b,c]:[1,2,3]$ a;b;c;
     %%%;

1.1.2.4 Groupement d'instructions (block)
.........................................

On est souvent amené à souhaite qu'une séquence de plusieurs
instructions forme une seule instruction.  Ça se fait avec 'block'

La syntaxe est

     %%%; nettoie()$
     block([],x:1,y:2,x+y);
     %%%;
la séquence d'instruction est 'x:1', 'y:1', 'x+y' et seul 'x+y' est
fourni en sortie de block.

la liste vide '[]' qui est en premier argument de block sert à
définir des variables locales.  Dans l'exemple, les valeur de 'x' et
de 'y' sont affectées par le passage dans 'block'

     %%%; nettoie()$
     block([],x:1,y:2,x+y); x; y;
     %%%;

mais pas avec

     %%%; nettoie()$
     block([x,y],x:1,y:2,x+y); x; y;
     %%%;
'x' et 'y' sont des variables locales au block.

1.1.2.5 Fonctions
.................

Une fonction est un mode opératoire (le corps de la fonction) qui
s'applique à ses arguments pour produire une valeur en sortie.

Le mode opératoire est attaché à un symbole et utilise pour être
décrit des arguments.  Une syntaxe possible (il y en a d'autres)
utilise l'opérateur ':='

Définition de fonctions à un seul argument et comportant une seule
instruction

À gauche de ':=' on place un nom de symbole (ici 'f') suivi
d'arguments placés entre parenthèse (ici seulement x)

À droite de ':=' on place une expression des arguments (ici de x)

     %%%; nettoie()$
     f(x):=x^2; f(2);
     %%%;

Pour que la fonction puisse comporter plusieurs instructions dans son
corps, on utilise un block.

     %%%; nettoie()$
     f(x):=block([a],a:1/x,a+x);
     f(2);
     %%%;

et donc on peut ainsi définir des fonctions de complexité
quelconque.

Définition de fonctions à plusieurs arguments

Les fonctions peuvent comporter plusieurs arguments.  La syntaxe est
alors comme

     %%%; nettoie()$
     f(x,y):=block([a],a:x/(x+y),x/(a+y)); f(u,v);
     %%%;

Il y a énormément de choses à dire sur les fonctions et les façons
de les définir.

Là on se contente de l'utilisation de l'opérateur ':=' en remarquant
que le corps de la fonction n'est pas évalué ; ce qui permet entre
autre que l'argument reste un symbole dans la définition de la
fonction plutôt qu'une valeur

     %%%; nettoie()$
     x:1; f(x):=x^2;
     %%%;

1.1.2.6 Tracé de graphes
.........................

Tracé d'expressions
....................

Plusieurs façons pour tracer une expression.

** Directement l'expression
     %%%; nettoie()$
     x:'x$
     plot2d(x^2,[x,0,1.5])$
     %%%;

** L'expression est rangée dans une fonction f, qui est évaluée
lors de l'appel de plot2d
     %%%; nettoie()$
     f(x):=x^2;
     plot2d(f(x),[x,0,1.5])$
     %%%;

** L'expression est encore rangée dans une fontion f mais c'est
plot2d qui se charge de l'évaluer
     f(x):=x^2;
     plot2d(f,[x,0,1.5])$
     %%%;
de ce fait, le nom de la variable (muette) servant définir les bornes
est indifférent
     %%%; nettoie()$
     plot2d(f,[xx,0,1.5])$
     %%%;

Tracé de listes de points
..........................

La structure est [discrete,[[x1,y1],...]]]  ou [discrete [x1,...]
[y1,..]].

4 points seuls
     %%%; nettoie()$
     plot2d([discrete,[[0,0],[1/2,1/4],[1,1],[3/2,9/4]]],[style, [points,2,5,1]])$
     %%%;

3 segments reliant 4 points
     %%%; nettoie()$
     plot2d([discrete,[[0,0],[1/2,1/4],[1,1],[3/2,9/4]]])$
     %%%;

Tracés multiples
.................

Une expression et 4 points
     %%%; nettoie()$
     plot2d([x^2,[discrete,[[0,0],[1/2,1/4],[1,1],[3/2,9/4]]]],[x,0,3/2])$
     %%%;

     %%%; nettoie()$
     x:'x$
     plot2d([x^2,[discrete,[[0,0],[1/2,1/4],[1,1],[3/2,9/4]]]],[x,0,1.5],
     [style,none,style,[points,2,5,1]])$
     DCQ("une expression et 4 points liés");
     %%%;

1.1.2.7 Boucles
...............

boucle for
..........

     %%%; nettoie()$
     total:0$
     for i:1 step 1 thru 10 do block([],total:total+i)$
     total;
     %%%;

boucle while (et unless)
........................

While
     %%%; nettoie()$
     total:0$
     i:0$ while i<11 do block([],total:total+i,i:i+1)$
     total;
     %%%;

Unless
     %%%; nettoie()$
     total:0$
     i:0$ unless i>10 do block([],total:total+i,i:i+1)$
     total;
     %%%;

1.1.2.8 Conditions
..................

Condition simple
     %%%; nettoie()$
     i:1$
     if i=1 then print("i vaut 1") else print("i=",i)$
     %%%;

Conditions multiples
     %%%; nettoie()$
     i:4;
     if i=1 then print("i vaut 1")
            else if i<3 then print("i<3")
            else if i<5 then print("i<5")
            else print("i=",i)$
     %%%;

1.1.2.9 Fraction vs flottants
.............................

Évaluation d'une fraction en nombre flottant
     %%%; float(1/2); %%%;

Transformation d'un flottant en fraction (positionner ratprint:false
pour éviter que ratsimp soit trop verbeux)
     %%%; ratsimp(0.5)@ %%%;

Constantes prédéfinies
     %%%; %pi=float(%pi);%e=float(%e);%gamma=float(%gamma); %%%;

Problème des grands nombres : des nombres peuvent devenir trop grand
pour les possiblités de 'float'.  Ce nombre
     %%%; nettoie()$
     x:123123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789;
     float(x);
     %%%;
n'excède pas les possibilités de float.  Par contre
     %%%;
     x:x*10$
     float(x);
     %%%;
les excède.  Dans ce cas on peut utiliser 'bfloat'
     %%%;
     x:x*10$
     bfloat(x);
     %%%;

1.1.2.10 Nombres complexes
..........................

Le symbole de 'i'
     %%%; nettoie()$
     %i^2;
     %%%;

Parties imaginaire et réelle, conjugaison
     %%%; nettoie()$
     realpart(x+%i*y);
     imagpart(x+%i*y);
     conjugate(x+%i*y);
     %%%;
(les symboles non déclarés comme complexe sont supposés réels)

1.1.2.11 Simplifications d'expressions rationnelles
...................................................

** Factorisation d'expressions polynomiales et rationnelles
     %%%; nettoie()$
     factor(x^17-1);
     %%%;

** Simplification de fractions rationnelles
     %%%; nettoie()$
     ratsimp((x^17-1)/(x^2-1));
     %%%;

** Développement d'expressions polynomiales

     %%%; nettoie()$
     expand((x-y)*(x+y));
     %%%;

1.1.2.12 Simplifications trigonométriques
..........................................

Sans exponentiation (apparente)
...............................

Développement
     %%%; nettoie()$
     trigexpand(cos(23*x));
     %%%;

Réduction (noter la nécessité de faire suivre trigreduce d'un
ratsimp)
     %%%; nettoie()$
     y:trigexpand(cos(23*x))$
     y:trigreduce(y)$
     ratsimp(y);
     %%%;

Autre réduction
     %%%; nettoie()$
     y:trigexpand(cos(23*x))$
     trigrat(y);
     %%%;
(elle est faite par passage à l'exponentiation)

Exponentiation
..............

C'est
     %%%; nettoie()$
     exponentialize(cos(t))@
     %%%;
et inversement
     %%%; nettoie()$
     x:exponentialize(cos(t))$
     demoivre(x)@
     %%%;

1.1.2.13 Evaluation simple
..........................

Une liste de données
     %%%; nettoie()$
     data:[k=1.3806488e-23,N=6.0221413e23];
     data:append(data,[R=ev(k*N,data)]);
     %%%;

Une évaluation de formule en prenant cette liste de donnée en compte
     %%%;
     ev(R*T,data,T=300);
     %%%;

1.1.2.14 Dérivation
....................

Dérivée première et seconde d'une expression
     %%%; nettoie()$
     diff(x^2,x);
     diff(x^2,x,2);
     %%%;

Dérivation de fonction ('remfunction' tue une éventuelle définition
de 'f')
     %%%; nettoie()$ remfunction(f)$
     diff(f(x),x);
     f(u):=u^2;
     diff(f(x),x);
     %%%;

1.1.2.15 Intégration
.....................

Primitive
     %%%; nettoie()$
     integrate(1/(1+x^2),x);
     %%%;

Intégrale définie
     %%%; nettoie()$
     integrate(1/(1+x^2),x,0,1);
     %%%;

Intégrale généralisée ('inf' = + infini)
     %%%; nettoie()$
     integrate(1/(1+x^2),x,0,inf);
     integrate(1/(1+x^2),x,-inf,inf);
     integrate(log(x),x,0,1);
     %%%;

1.1.2.16 Résolution d'équations algébriques
..............................................

Système linéaire
     %%%; nettoie()$
     sol:solve([a*x+b*y=X,c*x+d*y=Y],[x,y])@
     %%%;
On obtient la solution mais elle n'est pas affectée aux variables par
rapport auxquelles est faite la résolution.  Pour cela
     %%%; nettoie()$
     sol:solve([a*x+b*y=X,c*x+d*y=Y],[x,y])$
     [x,y]:ev([x,y],sol)$
     x y %%%;

Équation polynomiales pour un polynome de degré < 5
     %%%; nettoie()$
     solve(x^4+x^2+x+1,x);
     %%%;
au delà, on trouve ou non les racines.  Par exemple là on ne les
trouve pas
     %%%; nettoie()$
     solve(x^5+x^4+x^2+x+1,x);
     %%%;
et là on les trouve
     %%%; nettoie()$
     solve(x^5+x^2+x+1,x);
     %%%;

2 Techniques utiles
*******************

2.1 Exploration d'une expression
================================

Voici une fonction qui transforme une expression Maxima en chaîne de
caractères qui est une expression Lisp (quasiment)
     %%%; ok;
     MetEnLisp(expr):=block([],
      if atom(expr) then expr
      else block([%car:op(expr),%cdr:args(expr)],
       %cdr:map(MetEnLisp,%cdr),
       apply(printf,
        flatten([false,apply(concat,append(["(~a"],makelist(" ~a",i,1,length(%cdr)),[")"])),%car,%cdr]))
        ))$

     MetEnLisp(sin(u)*a+b+c/d+a^x);
     %%%;
Ça ne sert à rien, vu que les expressions Maxima sont codées en Lisp
de manière interne.

Par contre ça fournit un exemple de fonction explorant l'intérieur
d'une expression.

Examinons déjà la structure de la fonction.
   * 'if atom(expr) then expr' : si l'expression est un atome, on la
     renvoie Function and Variables for Expressions (Maxima);

   * sinon, on sépare l'opérateur des arguments avec
     '[%car:op(expr),%cdr:args(expr)]' : si l'expression n'est pas un
     atome alors elle se présente comme l'application d'un opérateur
     à des arguments.  'op, args' renvoient ces opérateur et
     arguments op (Maxima).
     ;

     puis

        * on appelle (récursivement) la fonction 'MetEnLisp' avec
          chacun des éléments de la liste des arguments séparément
          ; et on range le résultat dans la variable qui contenait
          cette liste des arguments.  C'est la fonction *map* qui
          permet cela
        * *map* See Functions and Variables for Program Flow (Maxima)
          ;

        * on renvoie ce qu'on a obtenu sous la forme '(op
          liste-des-arguments-passés par MetEnLisp)' ; l'instruction
          qui fait cela est un peu compliquée, faisons le dans un
          nouveau paragraphe.

Le plus simple est de réécrire la fonction en décomposant
l'instruction complexe qu'elle comporte et lui faisant écrire les
résultats intermédiaires l'instruction
       apply(printf,
        flatten([false,apply(concat,append(["(~a"],makelist(" ~a",i,1,length(%cdr)),[")"])),%car,%cdr]))
comme
     %%%; ok;
     MetEnLisp(expr):=block([],
      if atom(expr) then expr
      else block([%car:op(expr),%cdr:args(expr)],
       %cdr:map(MetEnLisp,%cdr),
       x1:makelist(" ~a",i,1,length(%cdr)), print("makelist(...) = ",x1),
       x2:append(["(~a"],x1,[")"]), print("append(...) = ",x2),
       x3:apply(concat,x2), print("apply(...) = ",x3),
       x4:[false,x3,%car,%cdr], print("arguments de flatten = ",x4),
       x5:flatten(x4), print("flatten(...) = ",x5),
       apply(printf,x5)
       ))$

     MetEnLisp(a+b);
     %%%;
   * *makelist(f(i),i,imin,imax* Functions and Variables for Lists (Maxima)
     fabrique une liste de longueur 'imax-imin+1' et
     d'argument 'f(i)' pour 'i=imin,...,imax'.  Par exemple
          %%%; makelist(i^2,i,-4,4); %%%;
     Ici elle fabrique une liste de " ~a" de longueur le nombre
     d'arguments de 'expr' (qui n'est pas un atome) ;

   * *append(l1,l2,..)*  Functions and Variables for Lists (Maxima)
     ajoute des listes à des listes.  Par exemple
          %%%; append([a,b],[c,d]); %%%;
     Ici elle ajoute les listes '["(~a"]', le résultat de makelist et
     '[")"]' ;

   * *apply(op,liste)* Functions and Variables for Function Definition (Maxima)
     applique un opérateur à ses arguments
     présentés sous forme d'une liste les contenant tous.  Par
     exemple
          %%%; apply("+",[a,b,c]); %%%;
     Ici l'opérateur est *concat* Functions and Variables for Strings (Maxima)
     qui est appliqué à la liste résultant
     de l'appel à 'append' ;

   * * *concat(x1,x2,...)*  Functions and Variables for Strings (Maxima)
     crée des symboles à partir d'autres symboles.  Par
     exemple
          %%%; concat(a,b); %%%;
     Mais si le premier de ses arguments est une chaîne de
     caractères, c'est une chaîne de caractères qu'il crée.  Par
     exemple
          %%%; concat("a",b); %%%;
     Ici le premier des arguments est une chaîne de caractères, donc
     la sorte de 'apply(concat,...)' aussi (même si les guillements
     n'apparaissent pas du fait du fonctionnement de 'print'
     Functions and Variables for Display (Maxima) ;

   * *flatten* Functions and Variables for Sets (Maxima) change
     une liste de liste en liste simple, par exemple
          %%%; flatten([a,[b,c],[d]]); %%%;
     Ici, l'examen de la liste qui lui est passée montre que c'est
     nécessaire à cause du dernier élément qui est '[b,a]' ;

   * *printf(false,format,x1,x2,...)*  Input and Output (Maxima)
     crée une chaîne de caractères à partir d'un 'format'
     et des arguments 'x1,...'.  Si on utilise '~a' pour l'élément
     à remplacer, il importe peu que l'argument soit une chaîne de
     caractères ou un symbole.  Par exemple
          %%%; printf(false,"Un ~asseur sa~ant ~asser",cha,"cha",cha); %%%;
     Cela crée la chaîne de caractères finale.

2.2 Qu'est ce que une fonction ?
================================

On peut faire quelque chose d'amusant avec 'MetEnLisp', c'est de
mettre en Lisp le code de cette fonction
     %%%; ok;
     MetEnLisp(expr):=block([],
      if atom(expr) then expr
      else block([%car:op(expr),%cdr:args(expr)],
       %cdr:map(MetEnLisp,%cdr),
       apply(printf,
        flatten([false,apply(concat,append(["(~a"],makelist(" ~a",i,1,length(%cdr)),[")"])),%car,%cdr]))
        ))$

     MetEnLisp(fundef(MetEnLisp));
     %%%;
La fonction *fundef(fct)* Functions and Variables for Function Definition (Maxima)
renvoie l'expression Maxima qui correspond au
symbole 'fct' qui est utilisé pour nommer une fonction :
     %%%; fundef(MetEnLisp); %%%;
L'application de 'MetEnLisp' à cette expression montre que
   * L'opérateur de l'expression qui correspond à une fonction est
     '":="' ;

   * Le premier des arguments est une expression de la forme
     'f(x,y,..)' où 'f' est le nom de la fonction, 'x,y,...' sont ses
     arguments ;

   * le second argument est une expression Maxima qui réalise le
     calcul de la fonction (on dit le corps de la fonction),

Si cela est juste, on doit pouvoir définir une fonction avec 'apply'
     %%%;
     remfunction(f);
     f(a,b);
     apply(":=",[f(x,y),x+y]);
     f(a,b);
     %%%;
QED !

*remfunction(f)* Functions and Variables for Function Definition (Maxima)
détruit la fonction de nom 'f'.

2.3 Manipulation de map
=======================

*map* Functions and Variables for Program Flow (Maxima) est une
fonction des plus utiles.  Voici quelques exemple de ce qu'elle permet
de faire.

Grouper deux à deux les éléments de listes
     %%%;
     map("[",[A,B,C],[a,b,c]);
     %%%;
Ça permet de construire des matrices
     %%%;
     apply(matrix,map("[",[A,B,C],[a,b,c],[1,2,3]))@
     %%%;

Réaliser un produit dyadique matriciel
     %%%;
     lver:[A,B,C];
     lhor:[a,b,c];
     apply(matrix,map(lambda([%u],map(lambda([%v],%u*%v),lver)),lhor))@
     %%%;
On a utilisé un *lambda* Functions and Variables for Function Definition (Maxima)
qui permet d'utiliser le corps d'une fonction
sans devoir nommer celle-ci.

Une esquisse de fonction 'derive' Dérivation (NeoCDE)
     %%%;
     ok$
     drv(expr):=block([%lfc:[[sin,cos(%u)],[cos,-sin(%u)],[log,1/%u],[exp,exp(%u)]]],
      if numberp(expr) then 0
      else if constantp(expr) then 0
      else if atom (expr) then d(expr)
      else block([%car:op(expr),%cdr:args(expr)],
       if %car="+" then apply("+",map(drv,%cdr))
       else if %car="-" then -drv(%cdr[1])
        else if %car="*" then
          apply("+",makelist(block([%fc:first(%cdr),%rc:rest(%cdr)],
                            %cdr:append(%rc,[%fc]),
     		       apply("*",cons(drv(%fc),%rc))),i,1,length(%cdr)))
       else if %car="/" then drv(%cdr[1])/%cdr[2]-%cdr[1]*drv(%cdr[1])/%cdr[2]^2
       else if not((%res:assoc(%car,%lfc))=false) then ev(%res,%u=%cdr[1])*drv(%cdr[1])
       else d(expr)))$

     drv(2*a*cos(x)/sin(x))@

     %%%;
On voit que c'est assez facile à faire.

Le seul point un peu délicat est celui de la dérivée du produit
         apply("+",makelist(block([%fc:first(%cdr),%rc:rest(%cdr)],
                            %cdr:append(%rc,[%fc]),
     		       apply("*",cons(drv(%fc),%rc))),i,1,length(%cdr)))
'%cdr' est la liste des termes du produit.  On fait une boucle
implicite avec un 'makelist' dans laquelle on sélectionne '%fc', le
premier argument de '%cdr', et '%rc', le reste des arguments.

Cela permet d'obtenir le terme de la multiplication de la dérivée de
'%fc' par ce reste '%rc'.  La boucle contient également une
instruction de permutation circulaire de '%cdr' pour obtenir
successivement tous les termes qu'il reste alors à additionner (avec
'apply').

2.4 Dessins
===========

Le package draw draw (Maxima) permet de faire des dessins et
des animations.  On peut à partir de lui fabriquer des canevas utiles
et prêts à servir.

     %%%;
     /*Dessine([largs]):=block([*/

     draw2d(file_name = "mypdf",
                            dimensions = 100*[12.0,8.0],
                            explicit(x^2,x,-1,1),
                            terminal  = 'pdf)$
     %%%;

3 Fonctions (non triviales) utiles
**********************************

Les références arrivent dans la section où est décrite la fonction
mais pas directement sur celle-ci.  Utiliser un '<C-s>' ou 'i'.

   * *fundef* See Functions and Variables for Function Definition (Maxima)
   * *map* See Functions and Variables for Program Flow (Maxima)
   * *apply* See Functions and Variables for Function Definition (Maxima)
   * *flatten* See Functions and Variables for Sets (Maxima)
   * *member* See Functions and Variables for Lists (Maxima)
   * *makelist* See Functions and Variables for Lists (Maxima)
   * *concat* See Functions and Variables for Strings (Maxima)
   * *append* See Functions and Variables for Lists (Maxima)
   * *remfunction(f)* See Functions and Variables for Function Definition (Maxima)
   * *lambda* See Functions and Variables for Function Definition (Maxima)

4 Index
*******

Concepts
--------

Fonctions et variables
----------------------