<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="pmathml.xsl"?>
<!DOCTYPE html [
  <!ENTITY rightarrow "&#8594;">
  <!ENTITY Rightarrow "&#8658;">
  <!ENTITY Leftrightarrow "&#8660;">
  <!ENTITY Element "&#8712;">
  <!ENTITY Exists "&#8707;">
  <!ENTITY Sum "&#8721;">
  <!ENTITY setminus "&#8726;">
  <!ENTITY VerticalBar "&#8739;">
  <!ENTITY approx "&#8776;">
  <!ENTITY leq "&#8804;">
  <!ENTITY geq "&#8805;">
  <!ENTITY prec "&#8826;">
  <!ENTITY succ "&#8827;">
  <!ENTITY succeq "&#8829;">
  <!ENTITY preceq "&#8828;">
  <!ENTITY asymp "&#8781;">
  <!ENTITY subseteq "&#8838;">
  <!ENTITY Intersection "&#8898;">
  <!ENTITY Kscr "&#61242;">
  <!ENTITY Ropf "&#8477;">
  <!ENTITY Nopf "&#8469;">
  <!ENTITY Zopf "&#8484;">
  <!ENTITY pm "&#177;">
  <!ENTITY epsi "&#949;">
  <!ENTITY Hscr "&#8459;">
  <!ENTITY Lscr "&#8466;">
  <!ENTITY % htmlDTD PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" >
  %htmlDTD;
]>

<html xmlns="http://www.w3.org/1999/xhtml"><head><title>Automatische Berechnung von Grenzwerten und Implementierung in Mathematica</title><style type="text/css">
     .mathcell {
       padding-left:2%;
     }
     .mathlabel {
       width:4em;
       float:left;
       padding-top:3px;
       color: #454F99;
       font-size: 10px;
       font-family: Helvetica;
     }
     .mathinput {
       position:relative;
       width:90%;
       margin-top:5px;
       border-width:1px 1px 0px 1px;
       border-style:solid;
       padding:0.5em;
       font-weight: bold;
       font-size: 14px;
       left:4em;
       background-color:#dddddd;
     }
     .mathoutput {
       position:relative;
       width:90%;
       margin-bottom:5px;
       border-width:0px 1px 1px 1px;
       border-style:solid;
       padding:0.5em;
       font-size: 14px;
       left:4em;
       background-color:#dddddd;
     }
     .mathprint {
       position:relative;
       width:90%;
       border-width:0px 1px 0px 1px;
       border-style:solid;
       padding:0.5em;
       font-size: 14px;
       left:4em;
       background-color:#cccccc;
     }
     .mathmessage {
       position:relative;
       width:90%;
       border-width:0px 1px 0px 1px;
       border-style:solid;
       padding:0.5em;
       font-size: 14px;
       left:4em;
       background-color:#ffffff;
     }
     .mathcellblock {
       margin-top:1em;
       margin-bottom:1em;       
     }
     .mmldisplay {
       text-align:center;
       margin-top:0.5em;
       margin-bottom:0.5em;       
     }
   </style></head><body>

<a href="Algorithmus.xml">Zurück</a> - <a href="Inhalt.xml">Inhalt</a> - <a href="index.html">Übersicht</a> - <a href="Praxis.xml">Vorwärts</a><br />


<a name="DetailsDerImplementierung" /><h2>5 Details der Implementierung</h2>

<a href="Paket.xml">Zurück</a> - <a href="Inhalt.xml">Inhalt</a> - <a href="index.html">Übersicht</a> - <a href="Praxis.xml">Vorwärts</a><br />

<a name="Quelltextdokumentation" /><h3>5.2 Quelltextdokumentation</h3>

Kommen wir zum primären Ergebnis der Diplomarbeit, dem Mathematica-Paket MrvLimit.
Der folgende Quelltext steht selbstverständlich zum 
<a href="http://urichter.cjb.net/MrvLimit/">Download</a> bereit.<br /><br />

Die hier wiedergegebene Version 1.0 des MrvLimit-Pakets enthält neben den normalen 
Kommentaren noch zusätzliche weiterführende Hinweise.<br /><br />

<a name="Paketanfang" /><h4>5.2.1 Paketanfang</h4>

<pre>
(* MrvLimit v1.0 (c) 2005 by Udo Richter           *)
(* mail:<a href="mailto:udo_richter@gmx.de">udo_richter(at)gmx.de</a>                      *)
(* web:<a href="http://urichter.cjb.net/MrvLimit">http://urichter.cjb.net/MrvLimit</a>            *)
(* Released under the GNU General Public License.  *)
(* "$Id: MrvLimit.m 97 2005-05-11 00:57:18Z udo $" *)



BeginPackage["MrvLimit`"];
</pre>

Mit BeginPackage beginnt der öffentliche Teil des Pakets. Alle nun folgenden Symbole
stehen nach dem Laden des Pakets zur Verfügung. Deswegen folgt als nächstes eine 
Aufzählung aller öffentlichen Symbole. Um bei Namensähnlichkeiten keine Warnungen
auszulösen, werden vorübergehend die General::spell Warnungen deaktiviert.

<pre>
Off[General::spell1];
Off[General::spell];
{power,pwr,log,sin,cos,tan,arcsin,arccos,arctan,gamma,loggamma,polygamma,
    Output,output,direction,term,Limit,LeadTerm,SignedZero,Debug,Automatic,
    Direction,ClearCache,MrvLimitGetCacheStats,MrvLimitClearCache,CacheHits,
    CacheMisses,ω,MrvF,MrvSeriesHead,$MrvSeriesHead,$DebugPrint,
    IndentPrint,MrvTestZero,$MrvTestZero,
    MrvTestZeroInterval,$MrvTestZeroInterval,$OutputProcessor,
    MrvLimitLoadSilent,Zero,FromInternal,ToInternal};
On[General::spell1];
On[General::spell];
</pre>

Es folgen die Hilfetexte für die exportierten Funktionen. Die Hilfetexte können mit ?MrvLimit
usw. abgefragt werden.

<pre>
ToInternal::usage=
    "ToInternal[f,x] translates f[x] to internal package form, making sure 
that no critical automatic transformations will be done on f any more. If 
ToInternal succeeds, return value will be free of ToInternal calls (check 
using FreeQ). If ToInternal fails, an UnsupportedFunction error is issued, 
and the failing ToInternal call will be encapsulated in HoldForm.";

FromInternal::usage=
    "FromInternal[f,x] translates internal representation of f[x] back to 
general Mathematica representation. See ToInternal[f,x] for details.";

MrvLimit::usage=
    "MrvLimit[f,x-&gt;lim,options] calculates the limit of f for x approaching 
lim. If MrvLimit succeeds, the limit value will be returned. In  case of 
two-sided limits, a list of two results may be returned, in case that they 
are different. If MrvLimit fails, an error message will be issued, and 
MrvLimit will return the whole call encapsulated in HoldForm. nPossible 
Options:n Direction: One of -1, +1, 0 or Automatic. Defaults to Automatic. 
+1 will calculate limits from below, -1 from above, 0 both sided. Automatic 
does both sided limits for finite limit processes, and matching sides for 
infinite.n Output: Limit, LeadTerm or SignedZero. Default Limit. LeadTerm 
will return a LeadTerm[] object describing the found asymptotic. See LeadTerm 
help. SignedZero will return Zero or -Zero if the limit is 0.n Debug: 
positive integer n or Automatic. Default Automatic. Enables debugging output 
down to nested level n in recursive calls. Automatic mode will detect 
recursive calls and inherit predecessor's debug level minus 1.";


LeadTerm::usage = 
    "Output-&gt;LeadTerm is an option to MrvLimit, to return the found asymptotic 
behavior of the function. To preserve its structure, the result will be 
in internal form, see ToInternal.nGeneral result form is LeadTerm[c,a], 
with a of form <math xmlns='http://www.w3.org/1998/Math/MathML'>
<mo>{</mo>
<mi>pwr</mi><mo>[</mo><msub><mi>&omega;</mi> <mn>1</mn></msub><mo>,</mo><msub><mi>e</mi> <mn>1</mn></msub><mo>]</mo><mo>,</mo><mi>pwr</mi><mo>[</mo><msub><mi>&omega;</mi> <mn>2</mn></msub><mo>,</mo><msub><mi>e</mi> <mn>2</mn></msub><mo>]</mo><mo>,</mo><mo>.</mo><mo>.</mo><mo>}</mo>
</math>.<math xmlns='http://www.w3.org/1998/Math/MathML'>
<msub><mi>&omega;</mi> <mi>i</mi></msub>

</math> is an asymptotic with 
<math xmlns='http://www.w3.org/1998/Math/MathML'>
<msub><mi>&omega;</mi> <mi>i</mi></msub>
<mo>&rightarrow;</mo><mn>0</mn><mo>,</mo><msub><mi>&omega;</mi> <mi>i</mi></msub><mi>&gt;</mi><mn>0</mn>
</math> for <math xmlns='http://www.w3.org/1998/Math/MathML'>
<mi>x</mi>
<mo>&rightarrow;</mo><mn>&infin;</mn>
</math>. <math xmlns='http://www.w3.org/1998/Math/MathML'>
<msub><mi>e</mi> <mi>i</mi></msub>

</math> is the exponent order of the asymptotic, and may be 
0. The a list is ordered by dominance, dominating asymptotics first. c is 
the leading term constant.nThe general asymptotic behavior is 
c*Apply[Times,a].pwr-&gt;Power.";
</pre>

Es folgen die Texte der diversen Fehlermeldungen, die mittels Message ausgegeben werden.

<pre>
MrvLimit::"LimitFail"="Limit failed at `1` on `2`";
MrvLimit::"UnknownSign"="Cannot determine sign: `1`";
MrvLimit::"SeriesFail"="Series failed at `1`";
MrvLimit::"SeriesNoTerms"=
    "Series failed at `1`: No leading term found, possibly undetected Zero 
function";
MrvLimit::"ZeroFunction"="Unexpected Zero function detected.";
MrvLimit::"UnknownOutput"="Unknown output type Output→`1`";
MrvLimit::"MrvSetFailed"="MrvSet failed at `1`";
MrvLimit::"UnsupportedFunction"=
    "MrvLimit does not support this function: `1`";
MrvLimit::"UnsupportedArgument"=
    "MrvLimit does not support `1` for `2`, appearing in `3`";
MrvLimit::"RecursiveCall"="Recursive Limit call encountered on `1`";
MrvLimit::"LostContext"=
    "Lost Link to aborted MrvLimit context detected. Skipping.";
MrvLimit::"UnknownOption"="Unknown Option `1`";
MrvLimitInf::"UnknownOption"="Unknown Option `1`";
MrvLimit::"ConditionCheck"="Cannot check important condition `1`";
MrvLimit::"Assert"="Assertion check failed: `1` on `2`";

Begin["`Private`"];
</pre>

Damit endet der öffentliche Teil des MrvLimit-Pakets. Alle folgenden Deklarationen
sind Teil des privaten Symbolkontextes und werden durch das Laden des Pakets nicht
direkt verfügbar. Zunächst folgen wieder einige Symboldeklarationen, wobei General::spell
Warnungen wieder deaktiviert sind.

<pre>
Off[General::spell1];
Off[General::spell];
{Res,ResA,ResB};
On[General::spell1];
On[General::spell];

(* Greetings *)
If[!(MrvLimitLoadSilent===True)
    (*then*),
    Print[
        "MrvLimit v1.0 (c) 2005 by Udo Richtern mail: 
udo_richter(at)gmx.den web: http://urichter.cjb.net/MrvLimitn Released 
under the GNU General Public License."];
];(* end if *)
</pre>

Die Begrüßungsnachricht wird beim Laden des Pakets ausgegeben, es sei denn,
die Variable MrvLimitLoadSilent wurde vorab gesetzt. <br /><br />


<a name="InterneDarstellung" /><h4>5.2.2 Interne Darstellung</h4>

Es folgt der erste echte Code. Die Funktion ToInternal dient dazu, Formelterme
in die 'sichere' interne Darstellung zu überführen. Das bedeutet in der
Regel, einige kritische Mathematica-Symbole durch gleichlautende Symbole in
Kleinbuchstaben zu ersetzen. Weitere Parametertests finden hier nicht statt.<br /><br />

ToInternal nutzt Mathematicas Spezialität, Funktionen für bestimmte
Muster in den übergebenen Parametern separat zu definieren. Damit wird der
Aufruf von ToInternal rekursiv auf die Argumente weiter gereicht, nachdem 
jeweils die äußerste Funktion durch die interne Darstellung ersetzt wurde.<br /><br />


<pre>
(* Translate term to internal restricted function space *)

ToInternal[f_,x_]:=f/;FreeQ[f,x];
ToInternal[x_,x_]:=x;
ToInternal[g_*h_,x_]:=ToInternal[g,x]*ToInternal[h,x];
ToInternal[g_+h_,x_]:=ToInternal[g,x]+ToInternal[h,x];
ToInternal[Power[g_,c_],x_]:=
    power[ToInternal[g,x],ToInternal[c,x]]/;FreeQ[c,x];
ToInternal[Power[E,g_],x_]:=power[E,ToInternal[g,x]];
ToInternal[Power[f_,g_],x_]:=power[E,log[ToInternal[f,x]]*ToInternal[g,x]];
ToInternal[Log[g_],x_]:=log[ToInternal[g,x]];

ToInternal[Sin[f_],x_]:=sin[ToInternal[f,x]];
ToInternal[Cos[f_],x_]:=cos[ToInternal[f,x]];
ToInternal[Tan[f_],x_]:=tan[ToInternal[f,x]];
ToInternal[Sec[f_],x_]:=1/cos[ToInternal[f,x]];
ToInternal[Csc[f_],x_]:=1/sin[ToInternal[f,x]];
ToInternal[Cot[f_],x_]:=1/tan[ToInternal[f,x]];

ToInternal[ArcSin[f_],x_]:=arcsin[ToInternal[f,x]];
ToInternal[ArcCos[f_],x_]:=arccos[ToInternal[f,x]];
ToInternal[ArcTan[f_],x_]:=arctan[ToInternal[f,x]];
ToInternal[ArcSec[f_],x_]:=arccos[1/ToInternal[f,x]];
ToInternal[ArcCsc[f_],x_]:=arcsin[1/ToInternal[f,x]];
ToInternal[ArcCot[f_],x_]:=arctan[1/ToInternal[f,x]];

ToInternal[Gamma[f_],x_]:=gamma[ToInternal[f,x]];
ToInternal[LogGamma[f_],x_]:=loggamma[ToInternal[f,x]];
ToInternal[PolyGamma[n_,f_],x_]:=polygamma[n,ToInternal[f,x]];

(* Pass-through if already in internal form: *)
ToInternal[power[f_,g_],x_]:=power[ToInternal[f,x],ToInternal[g,x]];
ToInternal[log[f_],x_]:=log[ToInternal[f,x]];
ToInternal[sin[f_],x_]:=sin[ToInternal[f,x]];
ToInternal[cos[f_],x_]:=cos[ToInternal[f,x]];
ToInternal[tan[f_],x_]:=tan[ToInternal[f,x]];
ToInternal[arcsin[f_],x_]:=arcsin[ToInternal[f,x]];
ToInternal[arccos[f_],x_]:=arccos[ToInternal[f,x]];
ToInternal[arctan[f_],x_]:=arctan[ToInternal[f,x]];
ToInternal[gamma[f_],x_]:=gamma[ToInternal[f,x]];
ToInternal[loggamma[f_],x_]:=loggamma[ToInternal[f,x]];
ToInternal[polygamma[n_,f_],x_]:=polygamma[n,ToInternal[f,x]];

(* Catch all remaining: *)
ToInternal[f_,x_]:=(
      Message[MrvLimit::"UnsupportedFunction",f];
      HoldForm[ToInternal[f,x]]
);
</pre>

Die abschließende allgemeine Definition für ToInternal wird immer dann angewendet, 
wenn keine der vorherigen spezielleren Formen auf den Term passt. Da für alle
unterstützten Terme eine spezielle Form vorliegt, tritt diese Definition nur 
für nicht unterstützte Funktionsaufrufe in Kraft.<br /><br />

Als Reaktion wird eine Warnmeldung ausgegeben und der Aufruf 'nicht ausgewertet'
zurück gegeben, d.h. in HoldForm eingebettet, um den sofortigen rekursiven Aufruf zu verhindern.
Um zu testen, ob ein Aufruf von ToInternal erfolgreich war, empfiehlt sich, mit FreeQ auf das
Vorhandensein des Symbols ToInternal zu testen.

<pre>
(* Remove all internal stuff: *)
FromInternal[f_,x_]:=
    f/.{log→Log,power→Power,sin→Sin,cos→Cos,
        tan→Tan,arcsin→ArcSin,arccos→ArcCos,
        arctan→ArcTan,gamma→Gamma,loggamma→LogGamma,
        polygamma→PolyGamma
    };
</pre>

FromInternal ist das Gegenstück zu ToInternal. Auf eine komplexe Analyse wird hier verzichtet,
da die übergebenen Terme normalerweise nicht vom Anwender stammen und auch keine unbekannten
Symbole behandelt werden müssen.

<pre>
(* Some very basic simplification rules *)
SimpleSimplifications={
      power[z_,0]→1,
      power[z_,1]→z,
      power[E,log[z_]]→z,
      log[power[E,z_]]→z,
      power[power[z_,e_],-1]→power[z,-e],
      power[-power[z_,e_],-1]→-power[z,-e]
};
</pre>

Da in interner Darstellung keine der normalen Vereinfachungen Gültigkeit hat, 
bleiben selbst Terme wie <math xmlns='http://www.w3.org/1998/Math/MathML'>
<msup><mi>x</mi> <mn>0</mn></msup>

</math>, <math xmlns='http://www.w3.org/1998/Math/MathML'>
<msup><mi>x</mi> <mn>1</mn></msup>

</math>,
<math xmlns='http://www.w3.org/1998/Math/MathML'>
<msup><mi>e</mi> <mrow><mo lspace="0em" rspace="thinmathspace">log</mo>
<mrow><mi>x</mi>
</mrow></mrow></msup>

</math>, <math xmlns='http://www.w3.org/1998/Math/MathML'>
<mo lspace="0em" rspace="thinmathspace">log</mo>
<msup><mi>e</mi> <mi>x</mi></msup>
</math>, <math xmlns='http://www.w3.org/1998/Math/MathML'>
<mo>(</mo>
<msup><mi>x</mi> <mi>k</mi></msup><msup><mo>)</mo> <mrow><mo lspace="thinthinmathspace" rspace="0em">-</mo>
<mn>1</mn></mrow></msup>
</math>
und <math xmlns='http://www.w3.org/1998/Math/MathML'>
<mo>(</mo>
<msup><mi>x</mi> <mrow><mo lspace="thinthinmathspace" rspace="0em">-</mo>
<mn>1</mn></mrow></msup><msup><mo>)</mo> <mi>k</mi></msup>
</math> unverändert. Diese sehr einfachen Transformationen
kann man in geeigneten Momenten durch manuelles Anwenden der SimpleSimplifications erreichen.<br /><br />

Als nächstes wird die visuelle Darstellung der internen Funktionen verbessert. Damit
bei Fehlermeldungen und der Debugausgabe nicht power[E,x], sondern <math xmlns='http://www.w3.org/1998/Math/MathML'>
<msup><mi>e</mi> <mi>x</mi></msup>

</math>
ausgegeben wird, muss ein geeigneter Aufruf für MakeBoxes definiert werden, 
der die Darstellung der internen Funktionsnamen erzeugt.<br /><br />

Um das Rad nicht neu erfinden zu müssen, wird der Aufruf einfach an den
MakeBoxes-Aufruf für das öffentliche Pendant der Funktion weiter gereicht.<br /><br />

<pre>
(* Display internal functions like public functions *)

log/:MakeBoxes[log[x_],form_]:=MakeBoxes[Log[x],form];
power/:MakeBoxes[power[x_,y_],form_]:=MakeBoxes[Power[x,y],form];

Unprotect[Times];
Times/:MakeBoxes[HoldPattern[Times[t___]],form_]:=Module[{h,i},
        h=HoldComplete[MakeBoxes[Times[t],form]];
        Do[
          If[h[[1,1,i,0]]===power,
              h[[1,1,i,0]]=Power;
          ];(*end if*)
          (*do for*),
          {i,1,Length[{t}]}
        ];
        ReleaseHold[h]
]/;MemberQ[{t},power[___]];
Protect[Times];
</pre>

Eine Besonderheit bleibt: Mathematica stellt bekanntlich Brüche intern als
Multiplikation mit Kehrwerten dar. <math xmlns='http://www.w3.org/1998/Math/MathML'>
<mfrac><mrow><mi>a</mi>
<mo>&sdot;</mo><mi>b</mi></mrow><mrow><mi>c</mi>
<mo>&sdot;</mo><mi>d</mi></mrow></mfrac>

</math> hat so die
interne Darstellung Times[a,b,Power[c,-1],Power[d,-1]]. Damit dessen Darstellung
nicht ebenfalls <math xmlns='http://www.w3.org/1998/Math/MathML'>
<mi>a</mi>
<mo>&sdot;</mo><mi>b</mi><mo>&sdot;</mo><mfrac><mrow><mn>1</mn>
</mrow><mrow><mi>c</mi>
</mrow></mfrac><mo>&sdot;</mo><mfrac><mrow><mn>1</mn>
</mrow><mrow><mi>d</mi>
</mrow></mfrac>
</math> ist, wird bereits
bei der Times-Darstellung auf eventuell vorhandene Potenzen geachtet. Deswegen
muss auch für 'power' eine Darstellung von Times definiert werden.<br /><br />

Erschwert wird das Ganze durch die Tatsache, dass MakeBoxes unter HoldComplete-Bedingungen
aufgerufen wird. Das übergebene Argument muss daher sorgfältig in HoldComplete 
aufbewahrt werden, Veränderungen können nur durch Ersetzungsregeln oder direkten Zugriff
erfolgen. Keinesfalls darf es zur mathematischen Auswertung des Times-Terms oder der
tiefer gelegenen Terme kommen.<br /><br />

Abschließend wird noch über die Bedingung festgelegt, dass diese Definition nur dann Gültigkeit
hat, wenn mindestens ein power-Term in Times direkt vorkommt.

<pre>
sin/:MakeBoxes[sin[x_],form_]:=MakeBoxes[Sin[x],form];
cos/:MakeBoxes[cos[x_],form_]:=MakeBoxes[Cos[x],form];
tan/:MakeBoxes[tan[x_],form_]:=MakeBoxes[Tan[x],form];
arcsin/:MakeBoxes[arcsin[x_],form_]:=MakeBoxes[ArcSin[x],form];
arccos/:MakeBoxes[arccos[x_],form_]:=MakeBoxes[ArcCos[x],form];
arctan/:MakeBoxes[arctan[x_],form_]:=MakeBoxes[ArcTan[x],form];
gamma/:MakeBoxes[gamma[x_],form_]:=MakeBoxes[Gamma[x],form];
loggamma/:MakeBoxes[loggamma[x_],form_]:=MakeBoxes[LogGamma[x],form];
polygamma/:MakeBoxes[polygamma[n_,x_],form_]:=MakeBoxes[PolyGamma[n,x],form];
</pre>



<a name="Tools" /><h4>5.2.3 Tools</h4>

Es folgen einige allgemeine nützliche Mathematica-Hilfsfunktionen.

<pre>
(* General Tools *)

(* Extract context part of module variables *)
GetSymbolModuleContext[a_Symbol]:=Module[{n,p},
      n=SymbolName[Unevaluated[a]];
      p=StringPosition[n,"$"];
      If[p=={},"",StringDrop[n,p[[1,1]]-1]]
];
SetAttributes[GetSymbolModuleContext,HoldFirst];

(* Extract variable part of module variables *)
GetSymbolModuleName[a_Symbol]:=Module[{n,p},
      n=StringJoin[Context[Unevaluated[a]],SymbolName[Unevaluated[a]]];
      p=StringPosition[n,"$"];
      If[p=={},n,StringTake[n,p[[1,1]]-1]]
];
SetAttributes[GetSymbolModuleName,HoldFirst];
</pre>

GetSymbolModuleContext erlaubt es, den Kontext-Teil von lokalen Variablen
innerhalb von Module[] oder von Variablen von Unique[] zu extrahieren und als
String zurück zu geben. Der Kontext-Teil hat dann die Form '$nnn'.
Da das Attribut HoldFirst gesetzt wird, kann der Variablen sogar bereits 
ein Wert zugewiesen worden sein.<br /><br />

GetSymbolModuleName ist das Gegenstück zur vorherigen Funktion
und liefert den Symbolnamen ohne den Kontext-Teil zurück.

<pre>
(* Translate module symbol name to global symbol name *)
Public[a_Symbol]:=Symbol[GetSymbolModuleName[Unevaluated[a]]];
SetAttributes[Public,HoldFirst];

(* Change a symbol's module context to a different one *)
SetModuleContext[a_Symbol,context_]:=
  Symbol[StringJoin[GetSymbolModuleName[Unevaluated[a]],context]]
SetAttributes[SetModuleContext,HoldFirst];
</pre>

Public ist eine Anwendung der vorherigen Funktionen. Public erzeugt
zu einem lokalen Modulsymbol ein passendes globales Symbol gleichen Namens.
Dies ist zum Beispiel dann sinnvoll, wenn in einem Modul ein Term ausgegeben
werden soll, der lokale Symbole beinhaltet, welche andernfalls mit sichtbarem $nnn-Teil
ausgegeben würden.<br /><br />

SetModuleContext ersetzt den Kontext-Teil eines lokalen Modulsymbols durch einen anderen Kontext.
Dadurch wird der Zugriff auf lokale Symbole anderer Modul-Kontexte ermöglicht.
Als Beispiel kann der folgende Code-Schnipsel dienen:
<pre>
(* Nicht Teil des Quelltextes! *)

         Module[{Local,Link},
             Local = "Modul 1";
             Link = GetSymbolModuleContext[Local];
             Module[{Local},
                 Local = "Modul 2";
                 Print[Local];
                 (* Gibt "Modul 2" aus *)
                 Print[SetModuleContext[Local, Link]];
                 (* Gibt "Modul 1" aus *)
             ];
         ];
</pre>

Im zweiten Modul überdeckt zunächst das lokale Symbol Local das gleichnamige
Symbol des ersten Moduls. Da aber die Kontextkennung des ersten Moduls
über Link bekannt ist, kann auch das zweite Modul auf die Variablen des
ersten Moduls lesend zugreifen. Ein Schreibzugriff ist so allerdings nicht möglich.
Wichtig ist auch, dass sämtliche lokalen Variablen am Ende des Moduls ihre zugewiesenen
Werte wieder verlieren.<br /><br />

Sinnvoll ist eine solche Verbindung bei rekursiven Aufrufen. Hat ein vorheriger Aufruf
seine Modulkennung in einer globalen Variable zurück gelassen, können rekursive
Aufrufe des gleichen Moduls auf die lokalen Symbole des Vorgängers lesend zugreifen.<br /><br />

Es folgen einige Hilfsfunktionen, um Funktionen mit optionalen Parametern der Form
Name → Wert zu unterstützen:

<pre>
(* Variable options handler *)
(* Handles optional parameters of name→value form *)
(* Example: *)
(* MyFunction::"UnknownOption"=                              *)
(*   "MyFunction does not support this option: `1`";         *)
(* Options[MyFunction]={Foo→True,Bar→False};                *)
(* MyFunction[Baz_,Options___] := Module[{MyFoo,MyBar},      *)
(*   ReadOptions[MyFunction,{Foo→MyFoo,Bar→MyBar},Options]; *)
(*   Print["Foo=",MyFoo," Bar=",MyBar];                      *)
(* ];                                                        *)

Clear[ReadOptions];
ReadOptions[Caller_,OptionList_,OptionSeq___]:=Module[{FullOptions,pos},
      (* List of Option,Destination,Default *)
      FullOptions=Map[{#[[1]],Null,#[[2]]}&amp;,Options[Caller]];
      
      (* Add Destinations *)
      Map[
        (FullOptions[[Position[FullOptions,{#[[1]],_,_}][[1,1]],2]]=#[[2]])&amp;
        ,OptionList
      ];
      
      (* Process sequence of options, replace defaults if found *)
      Map[
        (
          If[!MatchQ[#,_→_],
            Message[Caller::"UnknownOption",#];
            (*else*),
            pos=Position[FullOptions,{#[[1]],_,_}];
            If[Length[pos]==0,
              Message[Caller::"UnknownOption",#]
              (*else*),
              FullOptions[[pos[[1,1]],3]]=#[[2]]
            ];
          ];
        )&amp;,
        {OptionSeq}
      ];
      
      (* Assign results *)
      Map[(Evaluate[#[[2]]]=#[[3]])&amp;,
        Select[FullOptions,!MatchQ[#,{_,Null,_}]&amp;]];
];</pre>


Erster Parameter des Aufrufs von ReadOptions ist der Funktionsname. Dieser wird für den Zugriff auf
die Standardoptionen und für den Text der Fehlermeldung verwendet. Deshalb sollte vor der Benutzung
für jede Option in Options[Funktionsname] ein Default-Wert hinterlegt werden. Mehr dazu in der 
Mathematica-Hilfestellung zu Options[]. Außerdem sollte ein Fehlertext in der Form 
MyFunction::"UnknownOption"="Fehlertext" angelegt werden. 
Mehr dazu in der Mathematica-Hilfestellung zu Message[]. Diese Fehlermeldung wird immer dann 
ausgegeben, wenn eine Option angegeben wurde, die nicht in Options[] aufgeführt ist.<br /><br />

Zweiter Parameter ist eine Liste von Optionszuweisungen zu (lokalen) Symbolen in der Form 
{Optionsname→LokalerName, ...}. Nicht jeder optionale Parameter muss
hier aufgeführt sein, entscheidend für die Anerkennung als gültige Option ist das Vorhandensein
eines Default-Wertes in Options[]. ReadOptions wird in den angegebenen lokalen Symbolen die angegebenen
Optionen oder die Default-Optionen hinterlegen. Den lokalen Symbolen dürfen vor dem Aufruf
von ReadOptions noch keine Werte zugewiesen worden sein!<br /><br />

Ab dem dritten Parameter folgen die auszuwertenden Optionen. Wurde die aufrufende Funktion mit 
Options__ oder Options___ deklariert,
kann diese Sequenz direkt an ReadOptions weitergegeben werden.


<pre>(* Remove some options from a sequence of options *)
Clear[DropOptions];
DropOptions[DropOpts_,Options___]:=Module[{opt,i},
      opt={Options};
      Do[
        opt=Select[opt,(!MatchQ[#,DropOpts[[i]]→_])&amp;];
        (*do on*),
        {i,1,Length[DropOpts]}
      ];
      Apply[Sequence,opt]
];</pre>

Diese Hilfsfunktion dient dazu, bestimmte Optionen aus einer Sequenz herauszufiltern,
um dann beispielsweise die verbleibenden Optionen an eine andere Funktion weiterzugeben.<br /><br />

Erster Parameter ist eine einfache Liste von Optionsnamen, die entfernt werden sollen.
Alle weiteren Parameter werden als Optionen betrachtet und gefiltert. Zurückgegeben
wird wieder eine Sequenz von Optionen.

<pre>
(* Debug Print function *)
(* IndentPrint allows to indent print output *)

IndentPrint[Spacing_,Text__]:=
    Print[DisplayForm[AdjustmentBox[ToBoxes[SequenceForm[Text]],BoxMargins-&gt;
                 {{Spacing,0}, {0, 0}}]]];

(* Interface to set own debug printing routines *)

$DebugPrint=IndentPrint;

(* Interface to modify debug and message output *)

$OutputProcessor=Identity;
</pre>

Dies ist der Standard-Handler für Debugausgaben. IndentPrint akzeptiert als ersten Parameter 
einen Einrückwert, um den die Print-Ausgabe eingerückt wird. Dies wird in MrvLimit
die Rekursionsstufe sein. Die weiteren Parameter werden wie bei Print[] gehandhabt.<br /><br />

Für $OutputProcessor ist kein besonderer Standard-Handler vorgesehen.

<pre>
(* Global Zero Test code *)

MrvTestZero[term_,x_]:=Simplify[FromInternal[term,x]==0];

$MrvTestZero=MrvTestZero;
(* test if constant is 0 *)

MrvTestZeroInterval[term_,x_→∞]:=
    Simplify[FromInternal[term,x]==0];

$MrvTestZeroInterval=MrvTestZeroInterval;
(* test if function is identical 0 in interval (x0,∞) for some x0. *) 

(* Sorry, no idea how to check this properly. *)
</pre>

Dies sind die Standard-Handler für Nulltests, siehe <a href="Paket.xml#Erweiterungsschnittstellen">Kapitel 5.1.5</a>.<br /><br />



<a name="MrvSet" /><h4>5.2.4 MrvSet</h4>

Als nächstes folgt die Implementierung der Funktion MrvSet, vgl. <a href="Algorithmus.xml#StaerkstesWachstum">Kapitel 4.5</a>.<br /><br />

MrvSet liefert eine Liste von Objekten der Form MrvF[f, lc, lim] zurück, 
die die Teilausdrücke der stärksten Wachstumsklasse repräsentieren.
f stellt den entsprechenden Teilausdruck der Funktion dar, lc ist gleich LeafCount[f], 
und lim ist das Grenzverhalten für x→∞. Die Liste ist immer nach lc sortiert,
so dass das MrvF-Objekt mit dem kleinsten lc zuerst kommt.<br /><br />

Alle Funktionen rund um MrvSet liefern im Fehlerfall Null zurück, der 
Aufrufer kann aber davon ausgehen, dass die Ursache für den Fehler bereits durch eine
Meldung bekannt gegeben wurde.<br /><br />

Zunächst kommen einige Hilfsfunktionen.


<pre>
(* MrvSet is a helper function to calculate the set of most varying sub 
expressions. *)
(* Call MrvSet[f,
      x] with function f in internal representation and variable x. *)

(* Result is a list of MrvF[f,lc,lim] objects. *)
(* f represents one of the most varying sub expressions. f is either x, 
  or of e_ form. *)
(* lc is LeafCount[f]. *)
(* lim is the limit behavior of f for x→∞. 
          This can only be +∞ or 0. *)
(* The resulting list is always sorted by lc, lower (simpler) results first. *)

(* Compare two terms regarding varying class. 
      Optimized for results of MrvSet *)
MrvCompareMrvSet[exp1_,exp2_,x_]:=Module[{l,e1,e2},
      If[exp1===exp2,Return[0]];
      (* This esp. catches exp1=x, exp2=x *)
      
      If[exp1===x ,
        (*then*)
        e1=log[x],
        (*else*)
        If[!MatchQ[exp1,power[E,_]],
          Message[MrvLimit::"Assert","Should be exp:",exp1]];
        e1=exp1[[2]];
      ];
      
      If[exp2===x ,
        (*then*)
        e2=log[x],
        (*else*)
        If[!MatchQ[exp2,power[E,_]],
          Message[MrvLimit::"Assert","Should be exp:",exp2]];
        e2=exp2[[2]];
      ];
      
      (*calculate limit of Log[exp1]/Log[exp2] recursively*)
      l=MrvLimitInf[e1*power[e2,-1],x];
      If[l===Null,
        Message[MrvLimit::"LimitFail",MrvCompareMrvSet,e1/e2];
        Return[Null];
      ];
      If[$MrvTestZero[l,x],Return[1]];
      If[Abs[l]==∞,Return[-1]];
      Return[0];
];
</pre>

Die Hilfsfunktion MrvCompareMrvSet vergleicht die Wachstumsklasse zweier Terme
durch rekursive Grenzwertaufrufe. Die Funktion ist dabei nur für Terme geeignet,
die die Form <math xmlns='http://www.w3.org/1998/Math/MathML'>
<msup><mi>e</mi> <mrow><mi>f</mi>
<mo>(</mo><mi>x</mi><mo>)</mo></mrow></msup>

</math> haben oder direkt <i>x</i> sind.<br /><br />

Zurückgegeben wird -1,0,1 je nach Vergleichsergebnis oder Null im Fehlerfall. 
1 bedeutet, die Wachstumsklasse des zweiten Arguments ist größer, -1 bedeutet,
die Wachstumsklasse des ersten Arguments ist größer. 0 bedeutet, die Wachstumsklassen
sind gleich.<br /><br />

Wichtig ist auch die erste Zeile. Der Vergleich exp1===exp2 fängt den trivialen Fall ab,
dass exp1=exp2=x ist, der stur nach Algorithmus zu einer unendlichen Rekursion
führen würde.

<pre>
(*Calculate union of two MrvSet sets. 
      Drop all elements that are not of max varying class. *)

Clear[MrvSetMax];
MrvSetMax[Set1_,Set2_,x_]:=Module[{l,e1,e2},
      If[Head[Set1]=!=List||Head[Set2]=!=List,
        Return[Null];
      ];
      
      If[Set1==={},Return[Set2]];
      If[Set2==={},Return[Set1]];
      
      (* Now both sets have at least one member *)
      
      Switch[MrvCompareMrvSet[Set1[[1,1]],Set2[[1,1]],x],
        1   ,Set2,
        -1  ,Set1,
        0   ,Sort[Union[Set1,Set2],(#1[[2]]&lt;#2[[2]])&amp;],
        Null,Null (* error indicator *)
      ]
];
</pre>

MrvSetMax vergleicht das Wachstumsverhalten von zwei MrvSet-Ergebnismengen.
Zum Vergleich wird, falls vorhanden, das erste Element der Menge herangezogen.
Da die Mengen nach LeafCount sortiert sind, wird so auch der einfachste Term
der Menge zum Vergleich benutzt.<br /><br />

Zurückgegeben wird die stärker wachsende Menge bzw. die Vereinigung der Mengen, 
falls beide gleich stark wachsen. Die Menge wird natürlich korrekt sortiert zurückgegeben. 
Im Fehlerfall wird wieder Null zurückgegeben. Insbesondere
behandelt MrvSet den Fall, dass eine oder beide Mengen keine Mengen, sondern 
Null-Fehlermeldungen von rekursiven Aufrufen sind.

<pre>
(* 
  MrvSetRules:
    Apply simple rules to Mrv Set while preserving sort structure etc. 
        Only apply rule sets that dont change the limiting behavior!
  *)

MrvSetRules[MrvF[f_,lc_,lim_],Rules_List]:=Module[{f2},
      (* Apply rule and re-calculate leaf count *)
      f2=f/.Rules;
      MrvF[f2,LeafCount[f2],lim]
];

MrvSetRules[S_List,Rules_List]:=
    (* Apply rules and re-sort *)
    
    Sort[
      Map[MrvSetRules[#,Rules]&amp;,S],
      (#1[[2]]&lt;#2[[2]])&amp;
    ];
</pre>

MrvSetRules ist eine Hilfsfunktion, um einfache Regeln auf alle Funktionen eines MrvSet anzuwenden.
Nach der Regelanwendung wird der LeafCount erneut ermittelt und die Liste erneut sortiert.
Die Regeln dürfen das Wachstumsverhalten (0 oder ∞) nicht beeinflussen, da diese Information
ungeprüft übernommen wird.<br /><br />

Es folgt die Implementierung von MrvSet selbst, wieder indem MrvSet separat für verschiedene
Funktionsaufrufmuster definiert wird.

<pre>(* Calculate set of most rapidly varying sub expression, main code *)

Clear[MrvSet];
MrvSet[f_,x_]:={}/;FreeQ[f,x];
MrvSet[x_,x_]:={MrvF[x,1,∞]};
MrvSet[g_*h_,x_]:=MrvSetMax[MrvSet[g,x],MrvSet[h,x],x];
MrvSet[g_+h_,x_]:=MrvSetMax[MrvSet[g,x],MrvSet[h,x],x];
MrvSet[power[g_,c_],x_]:=MrvSet[g,x]/;FreeQ[c,x];
MrvSet[log[g_],x_]:=MrvSet[g,x];
MrvSet[power[E,g_],x_]:=Module[{l,m},
      l=MrvLimitInf[g,x];
      If[l==Null,
        Message[MrvLimit::"LimitFail","MrvSet",g];
        Return[Null];(* error indicator *)
      ];
      
      If[FreeQ[l,DirectedInfinity],
        (* Seems const. Pass through Mrv set. *)
        Return[MrvSet[g,x]];
      ];
      
      If[l===∞,
        (* tends to e∞. May contribute to Mrv set *)
        
        m=MrvF[
            power[E,g],
            LeafCount[power[E,g]],
            ∞
          ];
        Return[MrvSetMax[{m},MrvSet[g,x],x]];
      ]; (* end if l===∞ *)
      
      
      If[l===-∞,
        (* tends to e-∞. May contribute to Mrv set *)
        m=MrvF[
            power[E,g],
            LeafCount[power[E,g]],
            0
          ];
        Return[MrvSetMax[{m},MrvSet[g,x],x]];
      ]; (* end if l===-∞ *)
      
      
      Message[MrvLimit::"UnknownSign",l];
      Return[Null];
]; (* end MrvSet[power[E, g_], x_] *)
</pre>

Die Implementierung der Grundfunktionen erfolgt recht geradlinig.
Der Test auf <math xmlns='http://www.w3.org/1998/Math/MathML'>
<mo>&pm;</mo>
<mn>&infin;</mn>
</math> ist nicht sehr elegant,
da er durch die Tatsache erschwert wird, dass <math xmlns='http://www.w3.org/1998/Math/MathML'>
<mo>+</mo>
<mn>&infin;</mn>
</math> Mathematica-intern
als DirectedInfinity[1] dargestellt wird und <math xmlns='http://www.w3.org/1998/Math/MathML'>
<mo lspace="thinthinmathspace" rspace="0em">-</mo>
<mn>&infin;</mn>
</math>
als DirectedInfinity[-1]. Der Test auf "enthält nicht Unendlich"
(FreeQ[l,∞]) wäre deshalb falsch.
Sicherheitshalber wird hier auch mit der
Möglichkeit von komplexer Unendlichkeit oder anderen unbekannten Situationen gerechnet.

<pre>
(* Functions with no essential singularities may pass through: *)
(* Unsupported cases need to be filtered out, of course. *)
MrvSet[sin[f_],x_]:=MrvSet[f,x];
MrvSet[cos[f_],x_]:=MrvSet[f,x];
MrvSet[tan[f_],x_]:=MrvSet[f,x];
MrvSet[arcsin[f_],x_]:=MrvSet[f,x];
MrvSet[arccos[f_],x_]:=MrvSet[f,x];
MrvSet[arctan[f_],x_]:=MrvSet[f,x];
MrvSet[gamma[f_],x_]:=MrvSet[f,x];
MrvSet[loggamma[f_],x_]:=MrvSet[f,x];
MrvSet[polygamma[n_,f_],x_]:=MrvSet[f,x];
</pre><pre>
(* Catch all remaining *)
MrvSet[f_,x_]:=(
      Message[MrvLimit::"MrvSetFailed",f];
      Null
);
</pre>

Die restlichen Definitionen für MrvSet sind wieder unspektakulär.


<pre>
(* Calculate the complexity of a term *)

Clear[MrvComplexitySet];
MrvComplexitySet[f_,x_]:={}/;FreeQ[f,x];
MrvComplexitySet[x_,x_]:={x};
MrvComplexitySet[g_*h_,x_]:=
    Union[MrvComplexitySet[g,x],MrvComplexitySet[h,x]];
MrvComplexitySet[g_+h_,x_]:=
    Union[MrvComplexitySet[g,x],MrvComplexitySet[h,x]];
MrvComplexitySet[power[g_,c_],x_]:=MrvComplexitySet[g,x]/;FreeQ[c,x];
MrvComplexitySet[log[g_],x_]:=Append[MrvComplexitySet[g,x],log[g]];
MrvComplexitySet[power[E,g_],x_]:=Append[MrvComplexitySet[g,x],power[E,g]];

(* Functions with no essential singularities may pass through: *)
MrvComplexitySet[sin[f_],x_]:=MrvComplexitySet[f,x];
MrvComplexitySet[cos[f_],x_]:=MrvComplexitySet[f,x];
MrvComplexitySet[tan[f_],x_]:=MrvComplexitySet[f,x];
MrvComplexitySet[arcsin[f_],x_]:=MrvComplexitySet[f,x];
MrvComplexitySet[arccos[f_],x_]:=MrvComplexitySet[f,x];
MrvComplexitySet[arctan[f_],x_]:=MrvComplexitySet[f,x];
MrvComplexitySet[gamma[f_],x_]:=MrvComplexitySet[f,x];
MrvComplexitySet[loggamma[f_],x_]:=MrvComplexitySet[f,x];
MrvComplexitySet[polygamma[n_,f_],x_]:=MrvComplexitySet[f,x];

(* catch all remaining *)
MrvComplexitySet[f_,x_]:=(
      Message[MrvLimit::"UnsupportedFunction",f];
      HoldForm[MrvComplexitySet[f,x]]
);

MrvComplexity[f_,x_]:=Module[{c},
      c=MrvComplexitySet[f,x];
      If[!FreeQ[c,MrvComplexitySet],Return[∞]];
      Return[Length[c]];
];
</pre>

Der Programmcode für die Bestimmung der Komplexität eines Terms im Sinne der
Beweisführung für die Terminierung rekursiver Aufrufe ist hier nur der Vollständigkeit
halber angegeben. Benutzt wird er derzeit nicht, zumal Mathematicas Series-Kommando nicht 
unbedingt nach den Regeln des Beweises spielt...



<a name="MrvSeriesHead" /><h4>5.2.5 MrvSeriesHead</h4>

Es folgt die Standard-Implementierung von $MrvSeriesHead, die auf
das Series-Kommando von Mathematica zurückgreift.

<pre>
(* Calculate head of power series of f, dominant variable ω, 
  secondary variable x. *)
(* logω is a replacement term for Log[ω] and can be substituted 
at any time *)

(* Returns {u,ex} if leading term is u*ωex *)
(* May return u=0 only if f is identical 0. *)
</pre>

Zunächst wird aber die Hilfsfunktion SeriesHead definiert, die die Ausgabe des
Series[]-Kommandos analysiert und aufbereitet. Normalerweise liefert Series
als Ergebnis ein SeriesData-Objekt ab. Gelegentlich handelt es sich beim
Ergebnis aber auch um eine Konstante, ein komplettes Polynom (ohne Abbruch-Potenz)
oder auch Summe oder Produkt von Polynomterm mit einem SeriesData-Objekt.<br /><br />

SeriesHead ermittelt in all diesen Fällen den führenden Term der Potenzreihe.
Zurückgegeben wird eine Menge {c,e}, die den Term
<math xmlns='http://www.w3.org/1998/Math/MathML'>
<mi>c</mi>
<mo>&sdot;</mo><msup><mi>x</mi> <mi>e</mi></msup>
</math> repräsentiert, oder im Fehlerfall ein relativ 
unveränderter Term, der noch das Symbol SeriesHead enthält.

<pre>
(* SeriesHead *)
(* Tries hard to interprete the output of the Series[] command. *)
(* Returns {c,e} for leading term c*xe *)

Clear[SeriesHead];
SeriesHead[HoldPattern[SeriesData[x_,0,c_List,nmin_,nmax_,den_]],x_]:=
    If[$MrvTestZero[c[[1]],x],
        SeriesHead[SeriesData[x,0,Drop[c,1],nmin+1,nmax,den]]
        (*else*),
        {c[[1]],nmin/den}
        (*undetermined*),
        {c[[1]],nmin/den}
    ]/;Length[c]&gt;0;

SeriesHead[f_,x_]:={f,0}/;FreeQ[f,x];
SeriesHead[c_.*x_e_.,x_]:={c,e}/;FreeQ[c,x];&amp;&amp;FreeQ[e,x];
</pre>

Bei SeriesData-Objekten bestimmt SeriesHead direkt den führenden Term.
Sollte dieser sich als 0 herausstellen, wird er aus dem SeriesData-Objekt
entfernt, und ein rekursiver Aufruf ermittelt den nächsten führenden
Term. Die Bedingung am Ende stellt dabei sicher, dass das SeriesData-Objekt
stehen bleibt, wenn die bekannten Terme aufgebraucht sind.<br /><br />

Die nächsten zwei Definitionen leiten Konstanten und einfache polynomielle
Terme durch SeriesHead durch.

<pre>
SeriesHead[f_*g_,x_]:=Module[{sf,sg},
      sf=SeriesHead[f,x];
      sg=SeriesHead[g,x];
      If[!FreeQ[sf,SeriesHead]||!FreeQ[sg,SeriesHead],
        Return[sf*sg]
      ];
      Return[{sf[[1]]*sg[[1]],sf[[2]]+sg[[2]]}];
];

SeriesHead[f_+g_]:=Module[{sf,sg},
      sf=SeriesHead[f,x];
      sg=SeriesHead[g,x];
      If[!FreeQ[sf,SeriesHead]||!FreeQ[sg,SeriesHead],
        Return[sf+sg]
      ];
      If[sf[[2]]&lt;sg[[2]],Return[sf]];
      If[sf[[2]]&gt;sg[[2]],Return[sg]];
      If[sf[[2]]==sg[[2]],Return[{sf[[1]]+sg[[1]],sf[[2]]}]];
      Return[sf+sg];
];
</pre>

Diese beiden Definitionen dienen zum Auflösen von Produkten und Summen. Bei Produkten
werden die führenden Terme multipliziert, bei Summen gewinnt jeweils die kleinere Ordnung
bzw. die Summe bei gleicher Ordnung.<br /><br />

Es folgt der eigentliche Code von MrvSeriesHead.<br /><br />

Der allgemeine Aufruf von MrvSeriesHead lautet MrvSeriesHead[f, x, ω, logω, dprint], 
und dabei ist:

<ul>
<li>f die zu untersuchende Funktion,</li>
<li>x die Variable, die nur die niedrigeren Wachstumsklassen
repräsentiert und im Sinne unserer Potenzreihenentwicklung nur logarithmische
Singularitäten darstellt,</li>
<li>ω die Variable, die die relevante Wachstumsordnung
repräsentiert und in der die Potenzreihe ermittelt werden soll 
(in <math xmlns='http://www.w3.org/1998/Math/MathML'>
<mi>&omega;</mi>
<mo>=</mo><mn>0</mn>
</math> mit <math xmlns='http://www.w3.org/1998/Math/MathML'>
<mi>&omega;</mi>
<mi>&gt;</mi><mn>0</mn>
</math>),</li>
<li>logω der Wert von <math xmlns='http://www.w3.org/1998/Math/MathML'>
<mo lspace="0em" rspace="thinmathspace">ln</mo>
<mi>&omega;</mi>
</math> als Term in x und </li>
<li>dprint ein Verweis auf die Debugausgabe-Funktion von MrvLimit für Debugmeldungen.</li>
</ul><br /><br />

MrvSeriesHead liefert wie SeriesHead eine Liste {c,e} zurück, die 
den Term <math xmlns='http://www.w3.org/1998/Math/MathML'>
<mi>c</mi>
<mo>&sdot;</mo><msup><mi>&omega;</mi> <mi>e</mi></msup>
</math> repräsentiert oder im Fehlerfall Null.
Die Komponenten c,e sind im Gegensatz zu SeriesHead aber in interner Darstellung.

<pre>Clear[MrvSeriesHead];
MrvSeriesHead[f_,x_,ω_,logω_,dprint_]:=
    Module[{ser,serh,i,u,ex},
      
      (* Define some local auto simplifications *)
      Unprotect[Log];Unprotect[Power];
      Log[ω]=FromInternal[logω,x];
      Log[1/ω]=-Log[ω];
      Power[E,Log[ω]]=ω;
      Power[E,Log[1/ω]]=1/ω;
      Protect[Log];Protect[Power];</pre>

Durch das direkte Definieren von <math xmlns='http://www.w3.org/1998/Math/MathML'>
<mo lspace="0em" rspace="thinmathspace">ln</mo>
<mi>&omega;</mi>
</math>, <math xmlns='http://www.w3.org/1998/Math/MathML'>
<mo lspace="0em" rspace="thinmathspace">ln</mo>
<mn>1</mn><mo>/</mo><mi>&omega;</mi>
</math>,
<math xmlns='http://www.w3.org/1998/Math/MathML'>
<msup><mi>e</mi> <mrow><mi>log</mi>
<mi>&omega;</mi></mrow></msup>

</math> und <math xmlns='http://www.w3.org/1998/Math/MathML'>
<msup><mi>e</mi> <mrow><mn>1</mn>
<mo>/</mo><mi>log</mi><mi>&omega;</mi></mrow></msup>

</math> wird erreicht, dass
während der Potenzreihenentwicklung eventuell auftretende logarithmische Singularitäten
von ω in Terme in x abgebaut werden können und exponentielle Terme in x
korrekt zu Termen in ω aufgebaut werden können, wobei letzteres bisher nur im 
Zusammenhang zur PolyGamma-Funktion aufgetreten ist.<br /><br />

Als nächstes wird die Series-Funktion bemüht. Da vorab keine Ordnung abschätzbar ist, 
wird solange die Abbruchordnung erhöht, bis ein brauchbarer führender Term entsteht.
Leider ist das ein problematisches Verfahren ohne brauchbare Alternative: Hat der führende Term
extrem niedrige Ordnung, ermittelt der erste Aufruf viel zu viele unnötige Terme, 
ist die Ordnung dagegen extrem hoch, wird nie ein brauchbares Ergebnis gefunden. Handelt
es sich gar um eine versteckte Nullfunktion, so wird nie ein führender Term zu finden sein,
wobei man aber nie sicher sein kann, ob nicht doch noch ein Term kommt.

<pre>      (* Call Series with increasing order to find terms *)
      (* Ugly, but not to avoid. *)
      Do[
        ser=Series[FromInternal[f,x],{ω,0,i}];
        serh=SeriesHead[ser,ω];
        
        (* check for series term that run out of precision *)
        If[FreeQ[serh,HoldPattern[SeriesData[_,_,{},_,_,_]]],
          (* No: done. *)
          Break[];
        ];
        
        (* Increase precision and continue *)
        
        (* do for *)
        ,{i,1,30}
      ]; (* end do *)</pre>

Die Schleife bricht bei 30 ab. Eine gute Methode, den Algorithmus zum stolpern zu bringen, ist also,
eine Ordnung größer als 30 zu konstruieren...<br /><br />

In der Schleife wird getestet, ob ein SeriesData-Objekt vorliegt, das
keine Terme enthält. So ein Objekt entsteht, wenn SeriesHead alle Terme
aus SeriesData extrahiert hat, oder SeriesData schon keine Terme liefern konnte.
Falls solch ein Objekt vorhanden ist, wird die Ordnung weiter erhöht. Andernfalls
wird die Schleife beendet.<br /><br />

<pre>      
      (* Remove local auto simplifications *)
      Unprotect[Log];Unprotect[Power];
      Power[E,Log[ω]]=.;
      Power[E,Log[1/ω]]=.;
      Log[ω]=.;
      Log[1/ω]=.;
      Protect[Log];Protect[Power];
      
      dprint["Series expansion: ",$OutputProcessor[ser]];
      
      If[!FreeQ[serh,SeriesHead],
        (* SeriesHead could not interprete the result *)
        If[!FreeQ[serh,HoldPattern[SeriesData[_,_,{},_,_,_]]],
          (* No leading terms found *)
          Message[MrvLimit::"SeriesNoTerms",$OutputProcessor[f]];
          Return[Null];
        ];
        Message[MrvLimit::"SeriesFail",$OutputProcessor[f]];
        Return[Null];
      ]; (* end if *)
</pre>

Der Test auf das Vorhandensein eines SeriesHead-Terms deutet auf einen generellen Fehler
der SeriesHead-Funktion hin. Es geht im Folgenden dann nur noch darum, welche passende
Fehlermeldung ausgegeben werden soll.

<pre>
      {u,ex}=serh;
      
      (* Check if Series succeded in eliminating ω *)
      If[!FreeQ[u,ω],
        Message[MrvLimit::"SeriesFail",$OutputProcessor[f]];
        Return[Null];
      ];
      
      u=ToInternal[u,x];
      If[!FreeQ[u,ToInternal],
        Return[Null];
      ];
      ex=ToInternal[ex,x];
      If[!FreeQ[ex,ToInternal],
        Return[Null];
      ];
      {u,ex}
]; (* end MrvSeriesHead *)

$MrvSeriesHead=MrvSeriesHead;
</pre>

Der restliche Code führt noch ein paar Kontrollen durch, konvertiert das
Ergebnis in interne Darstellung und liefert das Ergebnis schließlich zurück.



<a name="MrvLimitTools" /><h4>5.2.6 MrvLimit Tools</h4>

Die Funktion MrvLimitOutput analysiert LeadTerm-Objekte, wie sie 
von MrvLimit mit der Option Limit → LeadTerm ausgegeben werden. Diese Objekte 
werden auch intern von MrvLimit verwendet. Zuvor aber noch eine Hilfsfunktion:

<pre>
(* Helper that interprets leading term results *)

MrvLimitLeadTermDominant[asympt_List]:=Module[{i},
      i=1;
      While[i≤Length[asympt] &amp;&amp; asympt[[i,2]]==0,i++];
      If[i&gt;Length[asympt],Return[Null]];
      Return[i];
];
</pre>

Diese Funktion sucht in einer Liste von pwr-Objekten das erste mit
Ordnung ungleich 0 heraus oder liefert Null, wenn alle die Ordnung 0 haben.

<pre>
Clear[MrvLimitOutput];
MrvLimitOutput[LeadTerm[const_,asympt_List],Output→Limit]:=
    Module[{i,ex},
      i=MrvLimitLeadTermDominant[asympt];
      
      If[i===Null,Return[const]]; (* All exponents are 0, 
        const is the result. *)
      ex=asympt[[i,2]]; (* Exponent of leading term *)
      If[ex&gt;0,Return[0]];(* tends to 0 in all cases *)
      If[ex&lt;0 ,
         (* limit result is +/- ∞ *)
        s=Sign[const];
        If[s===0,
          Message[MrvLimit::"ZeroFunction"];
          Return[Null];
        ];
        Return[s*∞]
      ]; (* end if ex &lt; 0 *)
      Message[MrvLimit::"UnknownSign",ex];
]; (* end MrvLimitOutput *)
</pre>

Diese Fassung von MrvLimitOutput (man beachte die festgeschriebene Option
Output → Limit) berechnet den konkreten Grenzwert aus dem LeadTerm-Objekt.<br /><br />

Die folgenden zwei Varianten interpretieren die beiden anderen Ausgabevarianten
bzw. lassen sie unverändert passieren.<br /><br />

<pre>
MrvLimitOutput[LeadTerm[const_,asympt_List],Output→LeadTerm]:=
    LeadTerm[const,asympt];

MrvLimitOutput[LeadTerm[const_,asympt_List],Output→SignedZero]:=
    Module[{l},
      l=MrvLimitOutput[LeadTerm[const,asympt],Output→Limit];
      If[l===0,Return[Sign[const]*Zero]];
      Return[l];
];
</pre>

Der folgende Code dient zur Leerung des MrvLimit-Caches. Auf die genaue Funktionsweise
des Caches wird im Hauptalgorithmus noch eingegangen.

<pre>
(* Mrv limit remember cache *)
MrvLimitClearCache[]:=Module[{},
      Clear[MrvLimitCache];
      MrvLimitCacheHits=0;
      MrvLimitCacheMisses=0;
];
MrvLimitClearCache[];
MrvLimitGetCacheStats[]:={CacheHits-&gt;MrvLimitCacheHits,
    CacheMisses-&gt;MrvLimitCacheMisses}
</pre>



<a name="MrvLimitPreProcess" /><h4>5.2.7 MrvLimitPreProcess</h4>

Wir hatten bereits die Funktion ToInternal kennengelernt, die Funktionsterme
in interne Darstellung überführt. Diese Funktion führte aber keine Parameterprüfungen
und keine Umformungen durch, da das nicht unbedingt in jedem Anwendungsfall erforderlich ist.<br /><br />

Für den eigentlichen Grenzwertaufruf ist dagegen eine strengere Darstellung erforderlich.
Einige Funktionen müssen in spezielle Darstellungen überführt werden, bei vielen Funktionen
müssen die Argumente auf Gültigkeit geprüft werden. Dazu dient die Funktion MrvLimitPreProcess.<br /><br />

Zunächst wird aber die Hilfsfunktion TestLimitArgQ definiert. TestLimitArgQ[f, x, test] ermittelt den Grenzwert
von f für x → ∞ und prüft danach die Bedingung test[#] mit dem Ergebnis
der Grenzwertberechnung als Argument. Entsprechend des Ergebnisses der Testfunktion 
wird True oder False zurück geliefert. Falls die Grenzwertberechnung fehlschlägt, wird ebenfalls
False zurück geliefert.<br /><br />

Liefert der Test kein klares True oder False, wird eine Warnmeldung und ebenfalls False 
ausgegeben. Ruft man TestLimitArgQ jedoch mit dem optionalen Parameter Undetermined → True | False
auf, wird keine Meldung ausgegeben, und der Test liefert stattdessen entsprechend True oder False.<br /><br />

<pre>
(* Pre-Processing: 
      Transform expression into processable form and check for limit fail 
conditions. *)

TestLimitArgQ[f_,x_,test_,Undetermined→undet_]:=Module[{ff},
      (* test if limit exists and matches a test criteria *)
      ff=MrvLimitInf[f,x];
      If[ff===Null,Return[False]];
      If[test[ff],
        (*then*)
        Return[True];
        ,(*else*)
        Return[False];
        ,(*undetermined*)
        If[undet===True,Return[True]];
        If[undet===False,Return[False]];
        Message[MrvLimit::"ConditionCheck",test[ff]];
        Return[False];
        ];
      ];
TestLimitArgQ[f_,x_,test_]:=TestLimitArgQ[f,x,test,Undetermined→-1];
</pre>

Die eigentliche Funktion MrvLimitPreProcess ist wieder als Funktion für bestimmte Muster in den
Parametern definiert:

<pre>Clear[MrvLimitPreProcess];
MrvLimitPreProcess[f_,x_]:=f/;FreeQ[f,x];
MrvLimitPreProcess[x_,x_]:=x;
MrvLimitPreProcess[g_*h_,x_]:=
    MrvLimitPreProcess[g,x]*MrvLimitPreProcess[h,x];
MrvLimitPreProcess[g_+h_,x_]:=
    MrvLimitPreProcess[g,x]+MrvLimitPreProcess[h,x];
MrvLimitPreProcess[power[g_,c_],x_]:=
    power[MrvLimitPreProcess[g,x],MrvLimitPreProcess[c,x]]/;FreeQ[c,x];
MrvLimitPreProcess[power[E,g_],x_]:=power[E,MrvLimitPreProcess[g,x]];
MrvLimitPreProcess[power[f_,g_],x_]:=
    power[E,log[MrvLimitPreProcess[f,x]]*MrvLimitPreProcess[g,x]];
</pre>

Allgemeine Terme der Form <math xmlns='http://www.w3.org/1998/Math/MathML'>
<msup><mi>f</mi> <mi>g</mi></msup>

</math> werden in die Form <math xmlns='http://www.w3.org/1998/Math/MathML'>
<msup><mi>e</mi> <mrow><mo lspace="0em" rspace="thinmathspace">log</mo>
<mo>(</mo><mi>f</mi><mo>)</mo><mo>&sdot;</mo><mi>g</mi></mrow></msup>

</math>
gebracht, es sei denn, f ist gleich e, oder g ist konstant.

<pre>MrvLimitPreProcess[log[f_],x_]:=log[MrvLimitPreProcess[f,x]];
MrvLimitPreProcess[sin[f_],x_]:=
    If[TestLimitArgQ[f,x,FreeQ[#,DirectedInfinity]&amp;],
      sin[MrvLimitPreProcess[f,x]],
      Message[MrvLimit::"UnsupportedArgument",Sin[x],x→∞,
        Sin[f]];
      HoldForm[MrvLimitPreProcess[sin[f],x]]
    ];
</pre>

Im Falle von sin[f] sieht man erstmals die Anwendung von TestLimitArgQ:
Gilt FreeQ[ MrvLimit[ sin[f] ], DirectedInfinity], wird die
sin-Funktion durch MrvLimitPreProcess hindurch geleitet und nur das
Argument weiter analysiert. Ist die Testbedingung dagegen falsch, so
wird eine Fehlermeldung ausgegeben und die Grenzwertberechnung in Folge abgebrochen.<br /><br />

Die meisten weiteren Funktionen werden ähnlich behandelt, nur mit wechselnden Bedingungen:

<pre>
MrvLimitPreProcess[cos[f_],x_]:=
    If[TestLimitArgQ[f,x,FreeQ[#,DirectedInfinity]&amp;],
      cos[MrvLimitPreProcess[f,x]]
      (*else*),
      Message[MrvLimit::"UnsupportedArgument",Cos[x],x→∞,
        Cos[f]];
      HoldForm[MrvLimitPreProcess[cos[f],x]]
    ];
MrvLimitPreProcess[tan[f_],x_]:=
    If[TestLimitArgQ[f,x,FreeQ[#,DirectedInfinity]&amp;],
      tan[MrvLimitPreProcess[f,x]]
      (*else*),
      Message[MrvLimit::"UnsupportedArgument",Tan[x],x→∞,
        Tan[f]];
      HoldForm[MrvLimitPreProcess[tan[f],x]]
    ];
MrvLimitPreProcess[arcsin[f_],x_]:=
    If[TestLimitArgQ[f,x,(Abs[#]≤1)&amp;],
      arcsin[MrvLimitPreProcess[f,x]]
      (*else*),
      Message[MrvLimit::"UnsupportedArgument",ArcSin[x],x→∞,
        ArcSin[f]];
      HoldForm[MrvLimitPreProcess[arcsin[f],x]]
    ];
MrvLimitPreProcess[arccos[f_],x_]:=
    If[TestLimitArgQ[f,x,(Abs[#]≤1)&amp;],
      arccos[MrvLimitPreProcess[f,x]]
      (*else*),
      Message[MrvLimit::"UnsupportedArgument",ArcCos[x],x→∞,
        ArcCos[f]];
      HoldForm[MrvLimitPreProcess[arccos[f],x]]
    ];
MrvLimitPreProcess[arctan[f_],x_]:=If[TestLimitArgQ[f,x,(True)&amp;],
      arctan[MrvLimitPreProcess[f,x]]
      (*else*),
      Message[MrvLimit::"UnsupportedArgument",ArcTan[x],x,ArcTan[f]];
      HoldForm[MrvLimitPreProcess[arctan[f],x]]
    ];
MrvLimitPreProcess[gamma[f_],x_]:=
    If[TestLimitArgQ[f,x,FreeQ[#,DirectedInfinity]&amp;],
      gamma[MrvLimitPreProcess[f,x]]
      (*else*),
      If[TestLimitArgQ[f,x,(#===∞)&amp;],
        power[E,loggamma[MrvLimitPreProcess[f,x]]]
        (*else*),
        Message[MrvLimit::"UnsupportedArgument",Gamma[x],
          x→MrvLimitInf[f,x],Gamma[f]];
        HoldForm[MrvLimitPreProcess[gamma[f],x]]
      ]
    ];
</pre>

Bei der Gamma-Funktion tritt erstmals wieder eine Besonderheit auf:
Endliche Argumente dürfen wieder wie üblich passieren.
Im Fall von f → ∞ jedoch wird eine Transformation
zu <math xmlns='http://www.w3.org/1998/Math/MathML'>
<msup><mi>e</mi> <mrow><mtext>LogGamma</mtext>
<mo>[</mo><mi>f</mi><mo>]</mo></mrow></msup>

</math> durchgeführt, um die essenzielle Singularität
der Gamma-Funktion zu kompensieren.

<pre>
MrvLimitPreProcess[loggamma[f_],x_]:=
    If[TestLimitArgQ[f,x,(0≤#≤∞)&amp;],
      loggamma[MrvLimitPreProcess[f,x]]
      (*else*),
      Message[MrvLimit::"UnsupportedArgument",LogGamma[x],
        x→MrvLimitInf[f,x],LogGamma[f]];
      HoldForm[MrvLimitPreProcess[loggamma[f],x]]
    ];

MrvLimitPreProcess[polygamma[n_,f_],x_]:=Module[{},
      If[!IntegerQ[n]||n&lt;0,
        Message[MrvLimit::"UnsupportedArgument",PolyGamma[k,x],k==n,
          PolyGamma[n,f]];
        Return[HoldForm[MrvLimitPreProcess[polygamma[n,f],x]]];
      ];
      If[TestLimitArgQ[f,x,(-∞&lt;#≤∞)&amp;],
        Return[polygamma[n,f]];
      ];
      Message[MrvLimit::"UnsupportedArgument",PolyGamma[n,x],
        x→MrvLimitInf[f,x],PolyGamma[n,f]];
      Return[HoldForm[MrvLimitPreProcess[polygamma[n,f],x]]];
    ];
</pre>
Bei der PolyGamma-Funktion wird zusätzlich darauf getestet, ob 
der n-Parameter ganzzahlig und nicht negativ ist. 

<pre>
(* catch all remaining *)
MrvLimitPreProcess[f_,x_]:=(
      Message[MrvLimit::"UnsupportedFunction",f];
      HoldForm[MrvLimitPreProcess[f,x]]
);
</pre>

Die abschließende 'catch all'-Funktion fängt wie üblich
alle verbleibenden ungültigen Aufrufe kontrolliert ab.


<a name="MrvLimitInf" /><h4>5.2.8 MrvLimitInf</h4>

Damit sind fast alle Vorbereitungen abgeschlossen, der Hauptalgorithmus 
kann beginnen. Der Algorithmus ist implementiert in zwei Teilen, der internen
Funktion MrvLimitInf und der öffentlichen Funktion MrvLimit.
Im Gegensatz zu MrvLimit berechnet MrvLimitInf nur Grenzwerte gegen Unendlich,
und im Fehlerfall wird Null zurück geliefert.

<pre>(* MrvLimitInf: Main algorithm *)

(* Reset Context links *)
MrvLimitCurrentContext=Null;
Clear[MrvLimitRunningTasks];
MrvLimitDefaultDebugLevel=0;

Clear[MrvLimitInf];
Options[MrvLimitInf]={Output→Limit,Debug→Automatic};
MrvLimitInf[ff_,x_,Options___]:=Module[
      {Ω,dprint,f,scaleup,w,g,A,s,t,c,a,ex,asympt,i,LastContext,
        NestLevel,DebugLevel,output},
      
      ReadOptions[MrvLimitInf,{Output→output,Debug→DebugLevel},
        Options];
      
      If[!MemberQ[{Limit,LeadTerm,SignedZero},output],
        Message[MrvLimit::"UnknownOutput", output];
        output=Limit;
      ];
</pre>

Am Anfang erfolgt einfache Optionsverarbeitung und Prüfung auf Gültigkeit.

<pre>      (* Some quick exits for really simple terms *)
      If[FreeQ[ff,x],
        If[output===LeadTerm,Return[LeadTerm[ff,{}]]];
        Return[ff];
      ];</pre>

Konstante Funktionen werden hier sehr frühzeitig abgebrochen.

<pre>      (* Check if previous MrvLimit context is still alive *)
      If[MrvLimitCurrentContext=!=Null,
        If[!NameQ[
                StringJoin[GetSymbolModuleName[LastContext],
                  MrvLimitCurrentContext]],
            (* Last context died unexpected. *)
            Message[MrvLimit::"LostContext"];
            MrvLimitCurrentContext=Null;
            Clear[MrvLimitRunningTasks];
        ];
      ]; (* end if *)

      (* Connect to last context if present. *)
      (* From here on, clean exit code is requred. *)
      LastContext=MrvLimitCurrentContext;
      MrvLimitCurrentContext=GetSymbolModuleContext[LastContext];
</pre>

Über MrvLimitCurrentContext nimmt MrvLimitInf zu früheren rekursiven
Instanzen Kontakt auf, um auf dessen Parameter und Schachtelungstiefe
zugreifen zu können. Um die Gültigkeit des durch MrvLimitCurrentContext
referenzierten Aufrufs zu prüfen, wird getestet, ob in diesem Kontext
die Variable LastContext existiert. Wurde das Modul des referenzierten
Aufrufs bereits beendet, zum Beispiel durch einen unkontrollierten
Abbruch, ist das Symbol LastContext in diesem Kontext nicht mehr existent.
Das wird mit einer Warnmeldung quittiert.<br /><br />

Schließlich wird der vorherige Kontextlink in der lokalen Variable LastContext
gespeichert und MrvLimitCurrentContext auf den aktuellen Kontext umgesetzt.
Bevor diese Instanz von MrvLimitInf beendet wird, muss auf jeden Fall
der ursprüngliche Wert von MrvLimitCurrentContext wieder hergestellt werden.

<pre>      Catch[
          (* 
            Catch/Throw error handling from here. 
                Throw[] uses clean exit code. *)</pre>

Um ein einfaches kontrolliertes Beenden von MrvLimitInf zu ermöglichen,
ist die verbleibende Funktion in Catch[] eingehüllt. Innerhalb dieses
Bereichs kann Throw[] ersatzweise wie Return[] verwendet werden, wobei
nötige Aufräumarbeiten vor dem Beenden automatisch ausgeführt werden.

<pre>          (* Get MrvLimit nest level *)
          
          NestLevel=
            If[LastContext===Null,1,
              SetModuleContext[NestLevel,LastContext]+1];
          
          (* Get Debug level *)
          If[DebugLevel===Automatic,
            
            DebugLevel=
                If[LastContext===Null,MrvLimitDefaultDebugLevel,
                  SetModuleContext[DebugLevel,LastContext]];
          ];
</pre>

Mit diesem Code ermittelt MrvLimitInf die Schachtelungstiefe des rekursiven
Aufrufs und den Debug Level des vorherigen Aufrufs, falls dieser nicht 
bereits durch die Option Debug → n gesetzt wurde.

<pre>
          (* Prepare dprint based on DebugLevel *)
          If[DebugLevel≥NestLevel
            (*then*),
            dprint[s__]:=$DebugPrint[NestLevel-1,s];
            
            dprint["Enter Level ",NestLevel,
              " Call ",$OutputProcessor[
                HoldForm[MrvLimitInf[ff,x,Options]]]];
            (*else*),
            dprint[s__]=Null;
          ];(*end if*)
          SetAttributes[dprint,HoldAll];
</pre>

Ist der Debug Level höher oder gleich der Schachtelungstiefe, so wird
unter dem Namen dprint die Funktion $DebugPrint hinterlegt
und damit die Debugausgabe aktiviert. Danach wird die Einstiegsmeldung
für den rekursiven Aufruf ausgegeben.<br /><br />

Ist der Debug Level niedriger, so wird dprint mit einer leeren Funktion
initialisiert. Damit Mathematica keine unnötige Zeit mit dem Bestimmen 
der Argumente verliert, wird dprint schließlich noch auf HoldAll gesetzt.

<pre>          
          (* Check for cached result *)
          asympt=MrvLimitCache[ff];
          If[Head[asympt]=!=MrvLimitCache,
            (*found in cache*)
            
            MrvLimitCacheHits++;
            dprint["Taking cached result"];
            
            Throw[MrvLimitOutput[asympt,Output→output]];
            
            (*else if Length[i]&gt;0*),
            MrvLimitCacheMisses++;
          ]; (* end if Length[i]&gt;0 *)
          
          (* Check for recursive calls *)
          If[MrvLimitRunningTasks[ff],
            Message[MrvLimit::"RecursiveCall",ff];
            Return[Null];
          ];
          
          (* Mark this task 'in progress' for recursive call checking *)
          MrvLimitRunningTasks[ff]=True;
</pre>

Im Symbol MrvLimitCache[f] werden bereits ermittelte Funktionsergebnisse abgelegt.
Dabei kommt grundsätzlich das LeadTerm-Format zum Einsatz. Ist ein solches 
Ergebnis bereits hinterlegt, so wird dieses Ergebnis an das Ausgabeformat angepasst
und zurück geliefert.<br /><br />

Ist kein passendes Ergebnis im Cache, wird zusätzlich MrvLimitRunningTasks[f]
geprüft. Dieses Symbol ist auf True definiert, solange die Berechnung läuft.
Sollte es zu einem rekursiven Aufruf mit exakt dieser Funktion kommen, so würde das
dadurch bemerkt und unterbunden. Dies ist kein perfekter Schutz gegen unendliche Rekursionen,
aber besser als nichts.

<pre>          
          (*************************************)
          (* Ok, lets start with the real work *)
          (*************************************)
          
          f=ff;
          If[$MrvTestZeroInterval[f,x→∞],
            (* Function is identical 0 around ∞. 
                    Dont start to calculate crap with it... *)
            Throw[0];
          ];</pre>

Hier werden Funktionen abgefangen, die in der Umgebung um Unendlich identisch 0
sind. Sie sprengen das Funktionsmodell und führen zu nicht behebbaren
Ausnahmebedingungen im Algorithmus, deswegen müssen solche Funktionen
frühzeitig erkannt werden.

<pre>          asympt={};
          (* Result is f*Apply[Times,asympt]/.pwr→Power , 
            while asympt is a dominance ordered list of pwr[w,ex], 
            w tends to 0 for x→0.
            *)</pre>

Ab hier gilt, dass LeadTerm[f,asympt] der aussichtsreichste Kandidat für
die Lösung der Grenzwertaufgabe ist, mit der Ausnahme, dass f bisher noch von x
abhängt.

<pre>          While[!FreeQ[f,x],
            (* While f depends on x: Do Mrv limit calculation *)</pre>

Damit beginnt die Hauptschleife, die jeweils die stärkste Wachstumsklasse
bestimmt und auflöst. Dies wird solange fortgesetzt, bis f konstant ist.


<pre>            dprint["Calculating limit of ",$OutputProcessor[f]];
            
            (* First, do pre-processing for some functions *)
            f=MrvLimitPreProcess[f,x];
            If[!FreeQ[f,MrvLimitPreProcess],
              dprint["Pre Processing failed  :",$OutputProcessor[f]];
              Throw[Null];
            ];</pre>

Als Vorbereitung wird die Funktion in die für MrvLimit erforderliche Struktur transformiert,
siehe auch <a href="Algorithmus.xml#Vorverarbeitung">Kapitel 4.3</a>.
Dies muss bei jedem Schleifendurchlauf erneut geschehen, da die Funktion
MrvSeriesHead die Struktur eventuell verändert hat.

<pre>            
            (* Now do 'simple simplifications' *)
            f=f/.SimpleSimplifications;
            
            (* Calculate Mrv set *)
            dprint["Calculating Mrv set of ",$OutputProcessor[f]];
            Ω=MrvSet[f,x];
            If[Ω===Null,Throw[Null]];
            If[Ω==={},
              Message[MrvLimit::"MrvSetFailed",$OutputProcessor[f]];
              Throw[Null];
            ];
            dprint["Ω=",$OutputProcessor[Ω]];
</pre>

Als nächstes folgt die Bestimmung der stärksten Wachstumsklasse inklusive elementarer Fehlerbehandlung,
siehe <a href="Algorithmus.xml#StaerkstesWachstum">Kapitel 4.5</a>.<br /><br />

Danach wird die Funktion bei Bedarf in eine ausrechend hohe 
Wachstumsklasse angehoben. Die Strategie ist aus <a href="Algorithmus.xml#AnhebungDerWachstumsklasse">Kapitel 4.6</a> bekannt.

<pre>            (* Scale up? *)
            scaleup=0;
            While[Ω[[1,1]]===x,
              (* x, if present in Ω, 
                will always be the first one *)
              scaleup++;
              f=f/.{log[x]→x,x→power[E,x]};
              Ω=
                MrvSetRules[Ω,{log[x]→x,
                    x→power[E,x]}];
              
              (* 
                in f and Ω, all occurences of ex got replaced the same way. 
                also, all inner occurences of log[x] got replaced the same way. 
                Only problem: The x in Ω may have been present in f as log[x], 
                transforming x→ex in Ω and log[x]→x in f. But since all members
                of Ω now have comparability class ex, this x term is of lower 
                class. But if none of the terms in Ω is left in f (all 
                occurences of x in f were log[x]), then there is no term of 
                ex order in f, and we have to search again for the next lower class.
              *)
              
              Ω=Select[Ω,!FreeQ[f,#[[1]]]&amp;];
              (* eliminate non-occuring terms *)
              
              If[Ω==={},Ω=MrvSet[f,x]];
              (* If all eliminated, scan for next lower class *)
              
              
              dprint["Scale Up: f=",$OutputProcessor[f],"n",
                "Ω=",$OutputProcessor[Ω]];
            ];</pre>

Damit ist die Wachstumsklasse bis über polynomielles Wachstum angehoben, der
Algorithmus kann fortgesetzt werden.

<pre>
            (* All Mrv's must be of e_ form. Assert if not. *)
            If[!Apply[And,Map[MatchQ[#[[1]],power[E,_]]&amp;,Ω]],
              
              Message[MrvLimit::"Assert",
                "Ω non-exponential",Ω];
              Throw[Null];
            ];
</pre>

Es folgt ein schlichter Test, ob Ω die geforderte Struktur hat und nur exponentielle
Terme enthält. Es gibt keinen Grund, weshalb dieser Fehler auftreten sollte.

<pre>     
            (* ω is the first one *)
            w=Ω[[1,1]];
</pre>

Der Algorithmus fordert eigentlich die Ordnung von Ω nach der
Komplexität. Tatsächlich genügt aber auch die Ordnung nach LeafCount,
die bereits vorliegt.

<pre>            
            dprint["ω=",$OutputProcessor[w]];
            
            (* We need ω→0. If ω→∞, 
               substitute. *)
            (* We know, ω is of ef_ form *)
            
            If[Ω[[1,3]]===∞,
              (* 
                w→∞. 
                Turn around sign inside e_ to get w→0 
              *)
              w[[2]]=-w[[2]];
              
              dprint["Replacing ω→1/ω: 
                     ω=",$OutputProcessor[w]];
            ];
</pre>

Falls ω → ∞ gilt, wird das Wachstumsverhalten 
abgeändert, wie im <a href="Algorithmus.xml#WahlDesRepraesentanten">Kapitel 4.7</a> dargelegt.<br /><br />

Es folgt die Ersetzungsschleife, die die Terme der stärksten Wachstumsklasse
in die Darstellung mit ω transferiert. Siehe <a href="Algorithmus.xml#UmschreibenDerFunktion">Kapitel 4.8</a> für Details.

<pre>            (* replace to ω in f step by step *)
            Do[
              g=Ω[[i,1]]; 
              (* Substitute all occurences of this subexpr *)
              (* see chapter 4.8 or Lemma 3.18 [Gru96] for details *)
              
              dprint["Prepare to rewrite g=",$OutputProcessor[g],
                " to A*ωc"];
              
              c=FromInternal[g[[2]]/w[[2]],x];
              (* We'll take some auto-optimizations for this *)
              c=ToInternal[c,x];
              If[!FreeQ[c,ToInternal],
                c=Null;
              ];
              (* Now fine again *)</pre>

Für den Term c lassen wir hier kurz Mathematica seine automatischen
Vereinfachungen durchführen. Der Verlust der Struktur von MrvLimitPreProcess
ist kein Problem, da c als nächstes an MrvLimitInf übergeben wird und
dann sowieso ein neuer Aufruf von MrvLimitPreProcess erfolgt.

<pre>              c=MrvLimitInf[c,x];
              If[c===Null || c===0 || !FreeQ[c,DirectedInfinity],
                
                Message[MrvLimit::"LimitFail","MrvLimit rewrite",
                  g[[2]]/w[[2]]];
                Throw[Null]
              ];</pre>

Wie im <a href="Algorithmus.xml#AlternativerAnsatz">Kapitel 4.12</a> dargelegt, kann dieser Grenzwertaufruf auch
eingespart werden.<br /><br />


Nun lässt sich der Term A bestimmen und die Ersetzung durchführen:

<pre>              A=power[E,g[[2]]-c w[[2]]]//.SimpleSimplifications;
              
              dprint["c=",$OutputProcessor[c]," A=",$OutputProcessor[A]];
              
              f=f/.{g→A*power[ω,c]}; (* and replace. *)
              
              dprint[
                "Rewriting ",$OutputProcessor[g],
                "→",$OutputProcessor[A*power[ω,c]],
                "n","f=",$OutputProcessor[f]
              ];
 
              (* do for *)
              ,{i,Length[Ω],1,-1}
            ]; (* end do *)
            
            (* Done rewriting *)</pre>

Damit ist der Umschreibeprozess fertig, es kommt die Potenzreihenentwicklung,
vgl. <a href="Algorithmus.xml#Potenzreihe">Kapitel 4.9</a>:

<pre>            dprint["Calculating power series: f=",$OutputProcessor[f]];
            
            (* Calculate power series head *)
            ser=$MrvSeriesHead[f,x,ω,w[[2]],dprint];
            
            If[ser===Null,Throw[Null]];
            {f,ex}=ser;</pre>

Das Ergebnis der Potenzreihenanalyse, der Vorfaktor des führenden Terms, 
ersetzt bereits hier die alte Funktion f. Der Rest wird durch ω und ex ausreichend
repräsentiert.<br /><br />

Schließlich kann die Anhebung der Wachstumsklasse wieder rückgängig gemacht werden,
wie in <a href="Algorithmus.xml#Ergebnisanalyse">Kapitel 4.10</a> geschildert.

<pre>            While[scaleup&gt;0,
              scaleup--;
              f=f/.{power[E,x]→x,x→log[x]};
              w=w/.{power[E,x]→x,x→log[x]};
              
              dprint["Scale Down: f=",$OutputProcessor[f],
                " , ω=",$OutputProcessor[w]];
            ];</pre>

Die Wachstumsklasse ω ist eliminiert, es bleibt, den führenden Term der Potenzreihe
weiter zu analysieren.

<pre>            dprint["Dominant term: ",$OutputProcessor[f*power[ω,ex]],
              " , ω=",$OutputProcessor[w]];
            
            If[$MrvTestZeroInterval[f,x→∞],
              (* This explicitly means that f has been identical zero *) (* 
                for x→∞ when entering this loop. 
                      This is fatal, *)
              (* since this should have been detected before entering *)
              (* the loop, or while in the previous loop run. *)
              (* Either the function was identical 0 from the beginning, *)
              (* or one of the series heads returned has been identical 0 *)
              (* without detecting it. Either way, we failed miserably. *)
              
              Message[MrvLimit::"ZeroFunction"];
              Throw[Null];
            ];</pre>

Das wäre ein fataler Fehler: Sollte sich jetzt herausstellen, dass der führende
Term 0 ist, so war die Funktion schon vor dem Aufruf von MrvSeriesHead
in der Umgebung um ∞ die Nullfunktion, denn MrvSeriesHead hätte andernfalls
weitere führende Terme ermitteln müssen.<br /><br />

Die Funktion war entweder von Anfang an die Nullfunktion in der Umgebung um ∞,
oder in einem vorherigen Schleifendurchlauf wurde in der Bestimmung des
führenden Terms der Potenzreihe ein Fehler gemacht. Dieses Problem derart zurück
zu rollen, dass die Ursache gefunden werden kann, ist fast unmöglich. In jedem Fall hat ein
Nulltest versagt.

<pre>            
            AppendTo[asympt,pwr[w,ex]];
            
            (* End of this limit round. Continue on lower Mrv level? *)
          ]; (* While !FreeQ[f,x] *)
</pre>

Das ist das Ende der Schleife, in der jeweils eine Wachstumsklasse abgebaut und 
an asympt angehängt wird. An dieser Stelle ist die Analyse beendet und der 
Grenzwert damit bekannt.          
          
<pre>          
          (* Result is f*Apply[Times,asympt]/.pwr→Power , 
            while asympt is a dominance ordered list of pwr[w,ex], 
            w tends to 0 for x→0 
          *)
          
          
          dprint["Dominant asymptotic: ",$OutputProcessor[f],
            Apply[Sequence,$OutputProcessor[asympt/.pwr→power]]];
          
          
          (* Add this result to cache *)
          MrvLimitCache[ff]=LeadTerm[f,asympt];
          
          Throw[MrvLimitOutput[LeadTerm[f,asympt],Output→output]];
</pre>

Das Ergebnis wird noch im Cache gespeichert, in die korrekte Ausgabeform gebracht
und per Throw abgeliefert.


<pre>
          (* end Catch *)
          ] // ( 
            (* Begin pure function *)
            (* Exit code *)
</pre>
Dies ist das Ende des mittels Catch[] gekapselten Hauptalgorithmus,
und alle Ergebnisse des Algorithmus durchlaufen diesen Code. 
Mit dem letzten '(' beginnt eine namenlose Funktion (siehe 
Mathematica-Hilfestellung unter Function), die die Ausgabe von
Catch[] verarbeitet, d.h. das mit Throw[] gemeldete Ergebnis.
Dieses Ergebnis ist jetzt in der Variablen # gespeichert.<br /><br />

Es bleibt nur noch, den Funktionsaufruf als beendet zu markieren,
eine passende Debug-Meldung über das Ergebnis abzusetzen und den
alten Inhalt von MrvLimitCurrentContext wieder herzustellen.<br /><br />

<pre>
            (* Clear 'in progress' state *)
            If[MrvLimitRunningTasks[ff],MrvLimitRunningTasks[ff]=.];
            
            
            dprint["Leave Level ",NestLevel,
              " Call ",$OutputProcessor[
                HoldForm[MrvLimitInf[ff,x,Options]==#]]];
            
            MrvLimitCurrentContext=LastContext;
</pre>

Schließlich wird das Ergebnis in # durchgereicht und als Funktionsergebnis
zurückgegeben.

<pre>            
            # (* Pass through result *)
          )&amp; (* end pure function *)
]; (* end MrvLimitInf *)

(* force cache clear on code change *)
MrvLimitClearCache[];
</pre>

Die letzte Zeile soll nur sicherstellen, dass bei allen Änderungen am Code
auch der Cache nochmals geleert wird. Damit ist der Algorithmus vollständig,
abgesehen vom eigentlichen Interface-Code.



<a name="MrvLimit" /><h4>5.2.9 MrvLimit</h4>

Der Interface-Code von MrvLimit hat keine komplizierten Aufgaben mehr zu bewältigen.
Er erledigt die Transformation zu einer Aufgabe für x → ∞
und übersetzt die Funktion in die interne Darstellung.

<pre>
(* MrvLimit interface code *)

Clear[MrvLimit];
Options[MrvLimit]={Output→Limit,Debug→Automatic,
      Direction→Automatic,ClearCache→False};
MrvLimit[ff_,x_→lim_,Options___]:=
    Module[{Res,ResA,ResB,f,z,direction,lim1,ChildOptions,clearcache},
      ReadOptions[
        MrvLimit,{Direction→direction,ClearCache→clearcache},
        Options];
      
      (* Remove Direction→ and ClearCache→ options *)
      ChildOptions=DropOptions[{Direction,ClearCache},Options];
</pre>

Die Optionen Direction und ClearCache werden vom Interface-Code behandelt
und werden daher nicht an MrvLimitInf weiter gereicht.

<pre>      
      If[clearcache,
        MrvLimitClearCache[];
      ];
      
      (* Delete lost contexts *)
      MrvLimitCurrentContext=Null;
      Clear[MrvLimitRunningTasks];
</pre>

Rekursive Aufrufe führen niemals durch MrvLimit, immer durch
MrvLimitInf. Deswegen kann hier sicher der Kontext zurück gesetzt werden,
falls eine vorherige Grenzwertberechnung unerwartet beendet wurde.

<pre>      f=ToInternal[ff,x];
      If[!FreeQ[f,ToInternal],
        Return[HoldForm[MrvLimit[ff,x→lim,Options]]];
      ];</pre>

Übersetzung des Funktionsterms in interne Darstellung.

<pre>      lim1=lim;
      If[direction===Automatic,
        (* Set auto direction *)
        Which[
            Head[lim]===Global`SuperPlus,(direction=-1;lim1=lim[[1]]),
            Head[lim]===Global`SuperMinus,(direction=1;lim1=lim[[1]]),
            (* 
              hell knows why I have to explicitly request global context. 
                  Wont work otherwise.*)
            lim===∞,direction=1,
            lim===-∞,direction=-1,
            True,direction=0
        ];
      ];</pre>

Falls die Option Direction nicht angegeben wurde, hat die Variable direction den Wert
Automatic. In diesem Modus wird bei Grenzwerten im Unendlichen
automatisch die korrekte Richtung gewählt, während bei endlichen Grenzwerten
der beidseitige Modus aktiviert wird. Außerdem wird die Schreibweise
mit SuperPlus und SuperMinus (hochgestelltes Plus/Minus) ausgewertet, siehe 
<a href="Paket.xml#EinfacheGrenzwertberechnungen">Kapitel 5.1.2</a>.

<pre>      If[direction≤0,
        (* limit from above *)
        If[Abs[lim1]==∞,
          ResA=MrvLimitInf[f/.x→-x,x,ChildOptions];
          ResA=ResA/.{x→-x};
          (* else *),
          ResA=MrvLimitInf[
              f/.{x→power[x,-1]+lim1}//.SimpleSimplifications
              ,x,ChildOptions];
          ResA=ResA/.{x→power[x-lim1,-1]}//.SimpleSimplifications;
        ];
        
        If[ ResA===Null,Return[HoldForm[MrvLimit[ff,x→lim,Options]]]];
      ];</pre>

Grenzwerte von oben: Je nachdem, ob endlich oder unendlich, wird die passende Transformation
angewendet, der Grenzwert ermittelt und die Transformation (für Output → LeadTerm) wieder
rückgängig gemacht.

<pre>      If[direction≥0,
        (* limit from below *)
        If[Abs[lim1]==∞,
          ResB=MrvLimitInf[f,x,ChildOptions];
          (* else *),
          ResB=MrvLimitInf[
              f/.{x→-power[x,-1]+lim1}//.SimpleSimplifications
              ,x,ChildOptions];
          ResB=ResB/.{x→power[lim1-x,-1]}//.SimpleSimplifications;
        ];
        
        If[ ResB===Null,Return[HoldForm[MrvLimit[ff,x→lim,Options]]]];
      ];</pre>

Grenzwerte von unten werden analog behandelt, nur wird das Ergebnis diesmal in ResB gespeichert. 
Beidseitige Grenzwerte durchlaufen beide Blöcke.

<pre>      (* Final result processing depending on direction *)
      
      If[direction==0,
        (* two sided limit, check continuity *)
        If[ResA===ResB ,
            Res=ResA;
            , (* else *)
            Res={ResB,ResA};
        ];
      ];</pre>

Im Fall von beidseitigen Grenzwerten wird nun auf Stetigkeit geprüft.
Falls die Funktion stetig am Grenzübergang ist, wird nur ein Ergebnis
zurück geliefert, andernfalls werden beide Ergebnisse zurück geliefert.

<pre>      If[direction&lt;0,
        Res=ResA;
      ];
      If[direction&gt;0,
        Res=ResB;
      ];
      
      If[Head[Res]=!=LeadTerm,Res=FromInternal[Res,x]];
</pre>

Im Fall von einseitigen Grenzwerten wird natürlich nur dieses eine Ergebnis zurück
geliefert.<br /><br />

Mit Ausnahme der Option Limit → LeadTerm wird das Ergebnis
noch in die offizielle Mathematica-Schreibweise zurück übersetzt.

<pre>      Res
]; (* end MrvLimit *)</pre>

Ende der Funktion MrvLimit...

<pre>End[];
EndPackage[];</pre>

... und Ende des Pakets MrvLimit.



<br /><a href="Paket.xml">Zurück</a> - <a href="Inhalt.xml">Inhalt</a> - <a href="index.html">Übersicht</a> - <a href="Praxis.xml">Vorwärts</a>


<br /><a href="Algorithmus.xml">Zurück</a> - <a href="Inhalt.xml">Inhalt</a> - <a href="index.html">Übersicht</a> - <a href="Praxis.xml">Vorwärts</a>
</body></html>