%htmlDTD; ]> Automatische Berechnung von Grenzwerten und Implementierung in Mathematica Zurück - Inhalt - Übersicht - Vorwärts

5 Details der Implementierung

Zurück - Inhalt - Übersicht - Vorwärts

5.2 Quelltextdokumentation

Kommen wir zum primären Ergebnis der Diplomarbeit, dem Mathematica-Paket MrvLimit. Der folgende Quelltext steht selbstverständlich zum
Download bereit.

Die hier wiedergegebene Version 1.0 des MrvLimit-Pakets enthält neben den normalen Kommentaren noch zusätzliche weiterführende Hinweise.

5.2.1 Paketanfang

(* MrvLimit v1.0 (c) 2005 by Udo Richter           *)
(* mail:udo_richter(at)gmx.de                      *)
(* web:http://urichter.cjb.net/MrvLimit            *)
(* Released under the GNU General Public License.  *)
(* "$Id: MrvLimit.m 97 2005-05-11 00:57:18Z udo $" *)



BeginPackage["MrvLimit`"];
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.
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];
Es folgen die Hilfetexte für die exportierten Funktionen. Die Hilfetexte können mit ?MrvLimit usw. abgefragt werden.
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->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->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 
{
pwr[ω 1,e 1],pwr[ω 2,e 2],..}
.
ω i

 is an asymptotic with 

ω i
0,ω i>0
 for 
x

. 
e i

 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->Power.";
Es folgen die Texte der diversen Fehlermeldungen, die mittels Message ausgegeben werden.
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`"];
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.
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 *)
Die Begrüßungsnachricht wird beim Laden des Pakets ausgegeben, es sei denn, die Variable MrvLimitLoadSilent wurde vorab gesetzt.

5.2.2 Interne Darstellung

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.

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.

(* 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]]
);
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.

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.
(* 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
    };
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.
(* 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]
};
Da in interner Darstellung keine der normalen Vereinfachungen Gültigkeit hat, bleiben selbst Terme wie x 0 , x 1 , e log x , log e x , ( x k) - 1 und ( x - 1) k unverändert. Diese sehr einfachen Transformationen kann man in geeigneten Momenten durch manuelles Anwenden der SimpleSimplifications erreichen.

Als nächstes wird die visuelle Darstellung der internen Funktionen verbessert. Damit bei Fehlermeldungen und der Debugausgabe nicht power[E,x], sondern e x ausgegeben wird, muss ein geeigneter Aufruf für MakeBoxes definiert werden, der die Darstellung der internen Funktionsnamen erzeugt.

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.

(* 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];
Eine Besonderheit bleibt: Mathematica stellt bekanntlich Brüche intern als Multiplikation mit Kehrwerten dar. a bc d hat so die interne Darstellung Times[a,b,Power[c,-1],Power[d,-1]]. Damit dessen Darstellung nicht ebenfalls a b1 c 1 d ist, wird bereits bei der Times-Darstellung auf eventuell vorhandene Potenzen geachtet. Deswegen muss auch für 'power' eine Darstellung von Times definiert werden.

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.

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.
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];

5.2.3 Tools

Es folgen einige allgemeine nützliche Mathematica-Hilfsfunktionen.
(* 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];
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.

GetSymbolModuleName ist das Gegenstück zur vorherigen Funktion und liefert den Symbolnamen ohne den Kontext-Teil zurück.
(* 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];
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.

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:
(* 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 *)
             ];
         ];
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.

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.

Es folgen einige Hilfsfunktionen, um Funktionen mit optionalen Parametern der Form Name → Wert zu unterstützen:
(* 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]]}&,Options[Caller]];
      
      (* Add Destinations *)
      Map[
        (FullOptions[[Position[FullOptions,{#[[1]],_,_}][[1,1]],2]]=#[[2]])&
        ,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]]
            ];
          ];
        )&,
        {OptionSeq}
      ];
      
      (* Assign results *)
      Map[(Evaluate[#[[2]]]=#[[3]])&,
        Select[FullOptions,!MatchQ[#,{_,Null,_}]&]];
];
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.

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!

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.
(* 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]]→_])&];
        (*do on*),
        {i,1,Length[DropOpts]}
      ];
      Apply[Sequence,opt]
];
Diese Hilfsfunktion dient dazu, bestimmte Optionen aus einer Sequenz herauszufiltern, um dann beispielsweise die verbleibenden Optionen an eine andere Funktion weiterzugeben.

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.
(* Debug Print function *)
(* IndentPrint allows to indent print output *)

IndentPrint[Spacing_,Text__]:=
    Print[DisplayForm[AdjustmentBox[ToBoxes[SequenceForm[Text]],BoxMargins->
                 {{Spacing,0}, {0, 0}}]]];

(* Interface to set own debug printing routines *)

$DebugPrint=IndentPrint;

(* Interface to modify debug and message output *)

$OutputProcessor=Identity;
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.

Für $OutputProcessor ist kein besonderer Standard-Handler vorgesehen.
(* 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. *)
Dies sind die Standard-Handler für Nulltests, siehe
Kapitel 5.1.5.

5.2.4 MrvSet

Als nächstes folgt die Implementierung der Funktion MrvSet, vgl.
Kapitel 4.5.

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.

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.

Zunächst kommen einige Hilfsfunktionen.
(* 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];
];
Die Hilfsfunktion MrvCompareMrvSet vergleicht die Wachstumsklasse zweier Terme durch rekursive Grenzwertaufrufe. Die Funktion ist dabei nur für Terme geeignet, die die Form e f (x) haben oder direkt x sind.

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.

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.
(*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]]<#2[[2]])&],
        Null,Null (* error indicator *)
      ]
];
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.

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.
(* 
  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]&,S],
      (#1[[2]]<#2[[2]])&
    ];
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.

Es folgt die Implementierung von MrvSet selbst, wieder indem MrvSet separat für verschiedene Funktionsaufrufmuster definiert wird.
(* 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_] *)
Die Implementierung der Grundfunktionen erfolgt recht geradlinig. Der Test auf ± ist nicht sehr elegant, da er durch die Tatsache erschwert wird, dass + Mathematica-intern als DirectedInfinity[1] dargestellt wird und - 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.
(* 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];
(* Catch all remaining *)
MrvSet[f_,x_]:=(
      Message[MrvLimit::"MrvSetFailed",f];
      Null
);
Die restlichen Definitionen für MrvSet sind wieder unspektakulär.
(* 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]];
];
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...

5.2.5 MrvSeriesHead

Es folgt die Standard-Implementierung von $MrvSeriesHead, die auf das Series-Kommando von Mathematica zurückgreift.
(* 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. *)
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.

SeriesHead ermittelt in all diesen Fällen den führenden Term der Potenzreihe. Zurückgegeben wird eine Menge {c,e}, die den Term c x e repräsentiert, oder im Fehlerfall ein relativ unveränderter Term, der noch das Symbol SeriesHead enthält.
(* 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]>0;

SeriesHead[f_,x_]:={f,0}/;FreeQ[f,x];
SeriesHead[c_.*x_e_.,x_]:={c,e}/;FreeQ[c,x];&&FreeQ[e,x];
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.

Die nächsten zwei Definitionen leiten Konstanten und einfache polynomielle Terme durch SeriesHead durch.
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]]<sg[[2]],Return[sf]];
      If[sf[[2]]>sg[[2]],Return[sg]];
      If[sf[[2]]==sg[[2]],Return[{sf[[1]]+sg[[1]],sf[[2]]}]];
      Return[sf+sg];
];
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.

Es folgt der eigentliche Code von MrvSeriesHead.

Der allgemeine Aufruf von MrvSeriesHead lautet MrvSeriesHead[f, x, ω, logω, dprint], und dabei ist:

MrvSeriesHead liefert wie SeriesHead eine Liste {c,e} zurück, die den Term c ω e repräsentiert oder im Fehlerfall Null. Die Komponenten c,e sind im Gegensatz zu SeriesHead aber in interner Darstellung.
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];
Durch das direkte Definieren von ln ω , ln 1/ω , e log ω und e 1 /logω 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.

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.
      (* 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 *)
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...

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.

      
      (* 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 *)
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.
      {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;
Der restliche Code führt noch ein paar Kontrollen durch, konvertiert das Ergebnis in interne Darstellung und liefert das Ergebnis schließlich zurück.

5.2.6 MrvLimit Tools

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:
(* Helper that interprets leading term results *)

MrvLimitLeadTermDominant[asympt_List]:=Module[{i},
      i=1;
      While[i≤Length[asympt] && asympt[[i,2]]==0,i++];
      If[i>Length[asympt],Return[Null]];
      Return[i];
];
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.
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>0,Return[0]];(* tends to 0 in all cases *)
      If[ex<0 ,
         (* limit result is +/- ∞ *)
        s=Sign[const];
        If[s===0,
          Message[MrvLimit::"ZeroFunction"];
          Return[Null];
        ];
        Return[s*∞]
      ]; (* end if ex < 0 *)
      Message[MrvLimit::"UnknownSign",ex];
]; (* end MrvLimitOutput *)
Diese Fassung von MrvLimitOutput (man beachte die festgeschriebene Option Output → Limit) berechnet den konkreten Grenzwert aus dem LeadTerm-Objekt.

Die folgenden zwei Varianten interpretieren die beiden anderen Ausgabevarianten bzw. lassen sie unverändert passieren.

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];
];
Der folgende Code dient zur Leerung des MrvLimit-Caches. Auf die genaue Funktionsweise des Caches wird im Hauptalgorithmus noch eingegangen.
(* Mrv limit remember cache *)
MrvLimitClearCache[]:=Module[{},
      Clear[MrvLimitCache];
      MrvLimitCacheHits=0;
      MrvLimitCacheMisses=0;
];
MrvLimitClearCache[];
MrvLimitGetCacheStats[]:={CacheHits->MrvLimitCacheHits,
    CacheMisses->MrvLimitCacheMisses}

5.2.7 MrvLimitPreProcess

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.

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.

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.

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.

(* 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];
Die eigentliche Funktion MrvLimitPreProcess ist wieder als Funktion für bestimmte Muster in den Parametern definiert:
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]];
Allgemeine Terme der Form f g werden in die Form e log (f)g gebracht, es sei denn, f ist gleich e, oder g ist konstant.
MrvLimitPreProcess[log[f_],x_]:=log[MrvLimitPreProcess[f,x]];
MrvLimitPreProcess[sin[f_],x_]:=
    If[TestLimitArgQ[f,x,FreeQ[#,DirectedInfinity]&],
      sin[MrvLimitPreProcess[f,x]],
      Message[MrvLimit::"UnsupportedArgument",Sin[x],x→∞,
        Sin[f]];
      HoldForm[MrvLimitPreProcess[sin[f],x]]
    ];
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.

Die meisten weiteren Funktionen werden ähnlich behandelt, nur mit wechselnden Bedingungen:
MrvLimitPreProcess[cos[f_],x_]:=
    If[TestLimitArgQ[f,x,FreeQ[#,DirectedInfinity]&],
      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]&],
      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)&],
      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)&],
      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)&],
      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]&],
      gamma[MrvLimitPreProcess[f,x]]
      (*else*),
      If[TestLimitArgQ[f,x,(#===∞)&],
        power[E,loggamma[MrvLimitPreProcess[f,x]]]
        (*else*),
        Message[MrvLimit::"UnsupportedArgument",Gamma[x],
          x→MrvLimitInf[f,x],Gamma[f]];
        HoldForm[MrvLimitPreProcess[gamma[f],x]]
      ]
    ];
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 e LogGamma [f] durchgeführt, um die essenzielle Singularität der Gamma-Funktion zu kompensieren.
MrvLimitPreProcess[loggamma[f_],x_]:=
    If[TestLimitArgQ[f,x,(0≤#≤∞)&],
      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<0,
        Message[MrvLimit::"UnsupportedArgument",PolyGamma[k,x],k==n,
          PolyGamma[n,f]];
        Return[HoldForm[MrvLimitPreProcess[polygamma[n,f],x]]];
      ];
      If[TestLimitArgQ[f,x,(-∞<#≤∞)&],
        Return[polygamma[n,f]];
      ];
      Message[MrvLimit::"UnsupportedArgument",PolyGamma[n,x],
        x→MrvLimitInf[f,x],PolyGamma[n,f]];
      Return[HoldForm[MrvLimitPreProcess[polygamma[n,f],x]]];
    ];
Bei der PolyGamma-Funktion wird zusätzlich darauf getestet, ob der n-Parameter ganzzahlig und nicht negativ ist.
(* catch all remaining *)
MrvLimitPreProcess[f_,x_]:=(
      Message[MrvLimit::"UnsupportedFunction",f];
      HoldForm[MrvLimitPreProcess[f,x]]
);
Die abschließende 'catch all'-Funktion fängt wie üblich alle verbleibenden ungültigen Aufrufe kontrolliert ab.

5.2.8 MrvLimitInf

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.
(* 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;
      ];
Am Anfang erfolgt einfache Optionsverarbeitung und Prüfung auf Gültigkeit.
      (* Some quick exits for really simple terms *)
      If[FreeQ[ff,x],
        If[output===LeadTerm,Return[LeadTerm[ff,{}]]];
        Return[ff];
      ];
Konstante Funktionen werden hier sehr frühzeitig abgebrochen.
      (* 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];
Ü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.

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.
      Catch[
          (* 
            Catch/Throw error handling from here. 
                Throw[] uses clean exit code. *)
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.
          (* 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]];
          ];
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.
          (* 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];
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.

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.
          
          (* 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]>0*),
            MrvLimitCacheMisses++;
          ]; (* end if Length[i]>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;
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.

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.
          
          (*************************************)
          (* 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];
          ];
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.
          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.
            *)
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.
          While[!FreeQ[f,x],
            (* While f depends on x: Do Mrv limit calculation *)
Damit beginnt die Hauptschleife, die jeweils die stärkste Wachstumsklasse bestimmt und auflöst. Dies wird solange fortgesetzt, bis f konstant ist.
            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];
            ];
Als Vorbereitung wird die Funktion in die für MrvLimit erforderliche Struktur transformiert, siehe auch
Kapitel 4.3. Dies muss bei jedem Schleifendurchlauf erneut geschehen, da die Funktion MrvSeriesHead die Struktur eventuell verändert hat.
            
            (* 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[Ω]];
Als nächstes folgt die Bestimmung der stärksten Wachstumsklasse inklusive elementarer Fehlerbehandlung, siehe Kapitel 4.5.

Danach wird die Funktion bei Bedarf in eine ausrechend hohe Wachstumsklasse angehoben. Die Strategie ist aus Kapitel 4.6 bekannt.
            (* 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]]]&];
              (* eliminate non-occuring terms *)
              
              If[Ω==={},Ω=MrvSet[f,x]];
              (* If all eliminated, scan for next lower class *)
              
              
              dprint["Scale Up: f=",$OutputProcessor[f],"n",
                "Ω=",$OutputProcessor[Ω]];
            ];
Damit ist die Wachstumsklasse bis über polynomielles Wachstum angehoben, der Algorithmus kann fortgesetzt werden.
            (* All Mrv's must be of e_ form. Assert if not. *)
            If[!Apply[And,Map[MatchQ[#[[1]],power[E,_]]&,Ω]],
              
              Message[MrvLimit::"Assert",
                "Ω non-exponential",Ω];
              Throw[Null];
            ];
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.
     
            (* ω is the first one *)
            w=Ω[[1,1]];
Der Algorithmus fordert eigentlich die Ordnung von Ω nach der Komplexität. Tatsächlich genügt aber auch die Ordnung nach LeafCount, die bereits vorliegt.
            
            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]];
            ];
Falls ω → ∞ gilt, wird das Wachstumsverhalten abgeändert, wie im Kapitel 4.7 dargelegt.

Es folgt die Ersetzungsschleife, die die Terme der stärksten Wachstumsklasse in die Darstellung mit ω transferiert. Siehe Kapitel 4.8 für Details.
            (* 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 *)
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.
              c=MrvLimitInf[c,x];
              If[c===Null || c===0 || !FreeQ[c,DirectedInfinity],
                
                Message[MrvLimit::"LimitFail","MrvLimit rewrite",
                  g[[2]]/w[[2]]];
                Throw[Null]
              ];
Wie im Kapitel 4.12 dargelegt, kann dieser Grenzwertaufruf auch eingespart werden.

Nun lässt sich der Term A bestimmen und die Ersetzung durchführen:
              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 *)
Damit ist der Umschreibeprozess fertig, es kommt die Potenzreihenentwicklung, vgl. Kapitel 4.9:
            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;
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.

Schließlich kann die Anhebung der Wachstumsklasse wieder rückgängig gemacht werden, wie in Kapitel 4.10 geschildert.
            While[scaleup>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]];
            ];
Die Wachstumsklasse ω ist eliminiert, es bleibt, den führenden Term der Potenzreihe weiter zu analysieren.
            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];
            ];
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.

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.
            
            AppendTo[asympt,pwr[w,ex]];
            
            (* End of this limit round. Continue on lower Mrv level? *)
          ]; (* While !FreeQ[f,x] *)
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.
          
          (* 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]];
Das Ergebnis wird noch im Cache gespeichert, in die korrekte Ausgabeform gebracht und per Throw abgeliefert.
          (* end Catch *)
          ] // ( 
            (* Begin pure function *)
            (* Exit code *)
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.

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.

            (* Clear 'in progress' state *)
            If[MrvLimitRunningTasks[ff],MrvLimitRunningTasks[ff]=.];
            
            
            dprint["Leave Level ",NestLevel,
              " Call ",$OutputProcessor[
                HoldForm[MrvLimitInf[ff,x,Options]==#]]];
            
            MrvLimitCurrentContext=LastContext;
Schließlich wird das Ergebnis in # durchgereicht und als Funktionsergebnis zurückgegeben.
            
            # (* Pass through result *)
          )& (* end pure function *)
]; (* end MrvLimitInf *)

(* force cache clear on code change *)
MrvLimitClearCache[];
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.

5.2.9 MrvLimit

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.
(* 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];
Die Optionen Direction und ClearCache werden vom Interface-Code behandelt und werden daher nicht an MrvLimitInf weiter gereicht.
      
      If[clearcache,
        MrvLimitClearCache[];
      ];
      
      (* Delete lost contexts *)
      MrvLimitCurrentContext=Null;
      Clear[MrvLimitRunningTasks];
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.
      f=ToInternal[ff,x];
      If[!FreeQ[f,ToInternal],
        Return[HoldForm[MrvLimit[ff,x→lim,Options]]];
      ];
Übersetzung des Funktionsterms in interne Darstellung.
      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
        ];
      ];
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
Kapitel 5.1.2.
      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]]]];
      ];
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.
      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]]]];
      ];
Grenzwerte von unten werden analog behandelt, nur wird das Ergebnis diesmal in ResB gespeichert. Beidseitige Grenzwerte durchlaufen beide Blöcke.
      (* Final result processing depending on direction *)
      
      If[direction==0,
        (* two sided limit, check continuity *)
        If[ResA===ResB ,
            Res=ResA;
            , (* else *)
            Res={ResB,ResA};
        ];
      ];
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.
      If[direction<0,
        Res=ResA;
      ];
      If[direction>0,
        Res=ResB;
      ];
      
      If[Head[Res]=!=LeadTerm,Res=FromInternal[Res,x]];
Im Fall von einseitigen Grenzwerten wird natürlich nur dieses eine Ergebnis zurück geliefert.

Mit Ausnahme der Option Limit → LeadTerm wird das Ergebnis noch in die offizielle Mathematica-Schreibweise zurück übersetzt.
      Res
]; (* end MrvLimit *)
Ende der Funktion MrvLimit...
End[];
EndPackage[];
... und Ende des Pakets MrvLimit.
Zurück - Inhalt - Übersicht - Vorwärts
Zurück - Inhalt - Übersicht - Vorwärts