|
-- gh (-G-enerate -H-tml) v1.91 -- Copyright (c) 2002, 2005 SoffyT & MNe -- [ Abstrakt | Über Fakultäten | Das Programm 'nf' | Download | Kontakt ] 'nf' ist ein Programm Mathematische -- meist kombinatorische -- Formel sind manchmal mit einem Ausrufezeichen ('!') versehen. Dies ist aber kein Ausdruck der Überraschung, sondern ein mathematischer Operator, der Fakultätszeichen genannt wird. Ein Fakultätszeichen folgt einer Formel oder einer natürlichen Zahl, und bedeutet, daß die Zahl mit allen natürlichen Zahlen, die kleiner als diese sind, multipliziert werden. Zum Beispiel 4! -- gesprochen als '4 Fakultät' -- ist das Produkt von 4x3x2x1. Warum sind nun Fakultäten in der Kombinatorik und Wahrscheinlichkeitsrechnungen so wichtig? Die einfache Antwort lautet, daß n! die Anzahl der unterschiedlichen Möglichkeiten angibt, n Dinge in einer Reihe anzuordnen. Stellen wir uns hierfür folgendes Beispiel vor: wir haben 4 Personen und 4 Stühle, und fragen uns: wieviele Unterschiedliche Möglichkeiten gibt es, die Stühle zu besetzen? Logischerweise gibt es 4 Möglichkeiten, den ersten Stuhl zu besetzen. Nachdem nun Person und Stuhl 'aufgebraucht' sind, haben wir weitere 3 Möglichkeiten, den zweiten Stuhl zu besetzen (und somit 4x3 Möglichkeiten, die ersten beiden Stühle zu besetzen). Danach haben wir 2 weitere Möglichkeiten, den dritten Stuhl zu besetzen (und somit 4x3x2 Möglichkeiten, die ersten drei Stühle zu besetzen). Schließlich bleibt uns, für den letzten Stuhl, nur mehr eine Möglichkeit der Besetzung. Das Ergebnis lautet daher: wir haben 4! = 4x3x2x1 -- also 24 -- unterschiedliche Möglichkeiten, 4 Personen auf 4 Stühle zu setzen. Mit der selben Überlegung kann man z.B. auch ermitteln, wie viele Möglichkeiten es gibt, 40 Spielkarten (wie sie -- in der Ausführung als Tarockkarten -- bei uns in Österreich z.B. für das sehr beliebte Tarock XXer-Rufen Verwendung finden) anzuordnen: wir haben genau 40! unterschiedliche Möglichkeiten, dies durchzuführen - eine bereits recht 'ansehliche' Zahl mit 45 Stellen (siehe Tabelle). Interessant ist weiters, daß der Wert einer Fakultät auch näherungsweise bestimmt werden kann; dazu verwendet man die nach dem Mathematiker James Stirling (18. Jhdt) benannte Formel: Die Stirling'sche Formel ist insoferne recht eigenartig, da sie die zwei wohl bekanntesten transzendenten Zahlen pi (das Verhältnis zwischen dem Umfang und dem Durchmesser eines Kreises: 3,1415926..) und e (die Eulersche Zahl: 2,7182818..) verwendet. Der absolute Fehler (die Differenz zwischen dem wahren und dem angenähertem Wert einer Fakultät) wächst mit steigendem n; der relative Fehler (der absolute Fehler dividiert durch den wahren Wert) wird hingegen ständig kleiner. Wie wir erkennen, ist die Berechnung des exakten Wertes einer Fakultät nicht kompliziert, aber mühsam, und das Ergebnis wird schnell sehr groß. Die Berechnung von z.B. 1.000! erfordert 'nur', daß einfach das Ergebnis von 1000x999x998x .. x3x2x1 berechnet wird; aber versuchen Sie das einmal mit Papier und Bleistift; das Ergebnis hat lächerliche 2.568 Stellen :-). Vor der Entwicklung von Computern war daher der Wert von Fakultäten nur bis zirka 300! bekannt; ausgenommen einer Handvoll größerer Fakultäten, von denen sich jemand die Mühe gemacht hat, diese auszurechnen. Wie erwähnt haben Fakultäten haben im Besonderen die Eigenschaft, schnell sehr groß zu werden; eine Tatsache, welche durch die folgende Tabelle veranschaulicht wird:
Fakultäten haben somit nichts magisches oder mystisches oder gar unverständliches, nur war es eben vor 60 oder 70 Jahren keinem Menschen möglich, den Wert von, zum Beispiel, 100.000! (exakt!, nicht über die Stirling'sche Formel) zu bestimmen. Der Compilerlauf: ~~ ~~ brcc32 nf.verinfo.rc -fonf.verinfo.res ~~ dcc32 -B -CC -GD -H -W -$A+ -$B- -$C- -$D- -$L- -$Y- -$H+ -$I- -$J- -$O+ -$Q- -$R- -$T- -$U+ -$W- -$X+ -$Z4 nf.dpr ~~ Borland Delphi Version 13.0 Copyright (c) 1983,99 Inprise Corporation O:\Marcus\mne_Testverzeichnis\core_utilities\CmdLine_Utilities.pas(182) O:\Marcus\mne_Testverzeichnis\core_utilities\Misc_Utilities.pas(1054) O:\Marcus\mne_Testverzeichnis\bogus_classes\Fakultaet.pas(289) nf.dpr(164) 1693 Zeilen, 0.27 Sekunden, 43864 Bytes Code, 2609 Bytes Daten. Der Quelltext der Projektdatei ('nf.dpr', 11.543 Byte): (* * . * Module name: nf.dpr; Module type: Delphi (console) project; Language: Object pascal; . * Original developed with: Delphi 5 Enterprise; Portability: most likely down to Delphi 2; . * . * Author: ~SoffyT (web:<http://www.nefzger.at/~SoffyT/>; mailto:<MissyQueen@nefzger.at>) . * File created: Fri., 01.11.2002; Last modified: Thu., 03.02.2005 . * . * Bug report: Pls. report all bugs, wishes, suggestions etc. to <BugReport@nefzger.at>. . * . * Purpose: . * ---------- . * Console wrapper for Fakultaet.pas . * . *) (* * . * To compile with Delphi: . * (To do ..) . * To compile with FPC: . * fpc -B -Sd -Op2 -Xs -XX -Fu"..\includes;..\core_utilities;..\bogus_classes" -FU"%TEMP%" nf.dpr -onf.fpc.exe . * . *) {$APPTYPE Console} program nFakultaet; // AKA 'nf' or 'nf.dpr' . {$INCLUDE '..\includes\Globals.inc'} uses Windows, SysUtils, {$IFDEF DelphiPascalCompiler} CmdLine_Utilities in '..\core_utilities\CmdLine_Utilities.pas', Misc_Utilities in '..\core_utilities\Misc_Utilities.pas', Fakultaet in '..\bogus_classes\Fakultaet.pas'; {$ENDIF} {$IFDEF FreePascalCompiler} CmdLine_Utilities , Misc_Utilities , Fakultaet ; {$ENDIF} // -- Programm- & Versions-Info ------------------------------- . {$R 'nf.verinfo.res'} // generiert mit VerGen.exe Copyright (c) 2002, 2004 ~MNe :-) . var Program_name : String; CmdLine_ok : Integer; // ------------------------------------------------------------ . const MAX_n = 499999; // Max. Fakultät: 1/2 Million . PROFILE_n = 15123; // Diese Fakultät für's Profiling . var i : Integer; t : Cardinal; f : TFakultaet; s : String; // ============================================================================ . begin try // Vorspann des Programms . Program_name := appGetApplicationFileName(); CmdLine_ok := GetFirstInvalidSwitch( CheckCmdLineSwitches( // Kommandozeile auswerten #1, "Lang-Form" .. . ['version','history', 'quiet', 'profile'], ['-'], [False,False, False, False], True), CheckCmdLineSwitches( // Kommandozeile auswerten #2, "Kurz-Form" .. . ['v','h', 'q', 'p'], ['-','/'], [False,False, False, False] {$IFDEF FreePascalCompiler},False{$ENDIF})); WriteLn; WriteLn(Program_name,' -- ',resGetXVersionInfo(),', ',resGetXReleaseInfo(),', ', resGetXDateTimeInfo()); Writeln(StringOfChar(' ',Length(Program_name)),' -- ',resGetXCopyrightInfo()); WriteLn(StringOfChar(' ',Length(Program_name)),' -- ','(dbg) OS: ', sysGetWin32Version().w32ComposedName,'; CmdLineCnt: ',ParamCount(),'; CmdLineErg: ', CmdLine_ok); WriteLn; // Kommandozeile auswerten . if (CmdLine_ok = CMDLINE_VALID) and ((ParamCount = 1) and ((CmdLineSwitch('v',['-','/'],False{$IFDEF FreePascalCompiler},False{$ENDIF}) > 0) or (CmdLineSwitch('version',['-'],False,True) > 0))) then begin // Parameter '--version': keine weiteren Informationen anzeigen .. . // WriteLn('Exiting.'); {$INCLUDE '.\all.motd.inc'} end else if (CmdLine_ok = CMDLINE_VALID) and ((ParamCount = 1) and ((CmdLineSwitch('h',['-','/'],False{$IFDEF FreePascalCompiler},False{$ENDIF}) > 0) or (CmdLineSwitch('history',['-'],False,True) > 0))) then begin // Parameter '-History': als Resource gelinkte History anzeigen .. . WriteLn(resGetXHistoryInfo()); end else if (CmdLine_ok = CMDLINE_VALID) and ((ParamCount = 1) and ((CmdLineSwitch('p',['-','/'],False{$IFDEF FreePascalCompiler},False{$ENDIF}) > 0) or (CmdLineSwitch('profile',['-'],False,True) > 0))) then begin // Parameter '-p': Profiling der unterschiedlichen Methoden mit vorgegebenem N .. . WriteLn('Profiling ',TFakultaet.GetBestM(),' different methodes by calculating ', fmtFormatNumber((1.0*PROFILE_n),0),'!:'); WriteLn; for i := 1 to TFakultaet.GetBestM() do begin f := TFakultaet.Create(); Write('Calculating ',fmtFormatNumber((1.0*PROFILE_n),0),'! /w methode #',i,' ..'); t := GetTickCount(); f.M := i; f.N := PROFILE_n; t := (GetTickCount()-t); if (f.NumDigits > 0) then begin // Berechnung ist OK - Ergebnis ausgeben .. . WriteLn(' Done. Calculation needed ',fmtFormatNumber((t/1000),2),' second(s).'); s := Copy(f.Result,1,100); WriteLn(' Result snapshot: ',Copy(s,1,50)); WriteLn(' ',Copy(s,51,50),' ..'); end else begin // Berechnung ist fehlgeschlagen - Fehlernummer und -meldung ausgeben .. . WriteLn(' Done, but the following error occured:'); WriteLn(' Error ',f.NumDigits,': ',f.Result,'.'); end; FreeAndNil(f); end; end else if (CmdLine_ok = CMDLINE_VALID) and ((ParamCount = 1) and (StrToIntDef(ParamStr(1),(-1)) > 0) and (StrToIntDef(ParamStr(1),(MAX_n + 1)) <= MAX_n)) then begin // Priorität des Programmes herabsetzen, damit der Compi weiter benutzt werden kann .. . if not(SetPriorityClass(GetCurrentProcess(),NORMAL_PRIORITY_CLASS)) then WriteLn('SetPriorityClass() failed: ''',fmtFormatMessageFromSystem({$IFDEF FreePascalCompiler}MAX_CARDINAL,True{$ENDIF}), '''; will continue anyway.'); if not(SetThreadPriority(GetCurrentThread(),THREAD_PRIORITY_LOWEST)) then WriteLn('SetThreadPriority failed: ''',fmtFormatMessageFromSystem({$IFDEF FreePascalCompiler}MAX_CARDINAL,True{$ENDIF}), '''; will continue anyway.'); // Hier geht es los mit der Berechnung von N! .. . f := TFakultaet.Create(); Write('Wait ..'); t := GetTickCount(); // f.M := TFakultaet.GetBestM(); << wird im Konstruktor eh eingestellt .. . f.N := StrToInt(ParamStr(1)); t := (GetTickCount()-t); if (f.NumDigits > 0) then begin // Berechnung ist OK - Ergebnis ausgeben .. . WriteLn(' Done. ',fmtFormatNumber((1.0*f.N),0),'! has ',fmtFormatNumber( (1.0*f.NumDigits),0),' digits; calculation /w methode #',f.M,' needed ', fmtFormatNumber((t/1000),2),' second(s) and took (',fmtFormatNumber( (1.0*f.M_Iterations),0),'+',fmtFormatNumber((1.0*f.UC_Iterations),0), ') iteration(s).'); WriteLn; WriteLn(fmtFormatNumber((1.0*f.N),0),'! = ',f.Result); end else begin // Berechnung ist fehlgeschlagen - Fehlernummer und -meldung ausgeben .. . WriteLn(' Done, but the following error occured:'); WriteLn(' Error ',f.NumDigits,': ',f.Result,'.'); end; FreeAndNil(f); end else begin // Ungültigen Parameter / Schalter gefunden? -> Ausgabe desjenigen welchen .. . if ((CmdLine_ok <> CMDLINE_VALID) or (ParamCount() > 0)) then begin if (CmdLine_ok <> CMDLINE_VALID) then WriteLn('Sorry! Unknown switch: ','''',ParamStr(CmdLine_ok),'''','.') else WriteLn('Sorry! Unknown or unsupported parameter: ', '''',ParamStr(ParamCount()),'''','.'); WriteLn; end; // Angegebene Kommandozeile war wohl niX .. Usage-Meldung ausgeben .. . WriteLn('Usage: ',Program_name,' [N] {-q|--quiet} | {-v|--version} | {-h|' + '--history} | {-p|--profile}'); WriteLn; WriteLn(' e.g.: ',Program_name,' 666'); WriteLn; WriteLn(' N A number between 1 (min) and ', fmtFormatNumber((1.0*MAX_n),0),' (max).'); WriteLn(' -q, /q, --quiet Quiet: no additional output. (Unimplemented' + ' by now.)'); WriteLn(' -v, /v, --version Display version number only.'); WriteLn(' -h, /h, --history Display program/ version history.'); WriteLn(' -p, /p, --profile Profile the ',TFakultaet.GetBestM(),' diffe' + 'rent implementations.'); WriteLn; WriteLn('Hint(s): Parameters enclosed in ''[]'' are mandatory; parameters' + ' enclosed in ''{}'' are optional; parameters seperated by ''|'' exclu' + 'de each other; ''<..>'' within a parameter means that *you* have to s' + 'upply additional information.'); WriteLn; WriteLn('Beware: While calculating 999! (2.565 digits: ''40238726007..'')' + ' only takes about 00:00,20 [mm:ss,hh], calculating 99.999! (456.569 d' + 'igits: ''28242294079..'') takes about 59:25,50 [mm:ss,hh]! (System wa' + 's a P3 @ 550 MHz /w 512 MB, NT 4, SP6a).'); WriteLn; WriteLn('Bug report: Pls. report all bug(s) and/ or wishes, suggestions e' + 'tc. to <BugReport@nefzger.at>, including the module name (which is: ''', Program_name,''') and module version (which is: ''',resGetXVersionInfo(), '''). Thank you.'); end; Halt(0); // Normales Programmende, mit Exitcode 0 retour .. . except on e: Exception do begin WriteLn(' Fatal: Exception: ','''',e.Message,'''','; pls. contact the pro' + 'grammer: <BugReport@nefzger.at>.'); Halt(1); // Abnormales Programmende: irgendwo wurde (intern) eine Exception geworfen .. . end; end; {try} end. // {begin} . Der Quelltext der Klasse TFakultaet ('fakultaet.pas', 13.277 Byte): (* * . * Module name: Fakultaet.pas; Module type: Delphi unit; Language: Object pascal; . * Original developed with: Delphi 5 Enterprise; Portability: most likely down to Delphi 2; . * . * Author: ~SoffyT (web:<http://www.nefzger.at/~SoffyT/>; mailto:<MissyQueen@nefzger.at>) . * File created: Fri., 01.11.2002; Last modified: Fri., 17.01.2003 . * . * Bug report: Pls. report all bugs, wishes, suggestions etc. to <bugreport@nefzger.at>. . * . * Purpose: . * ---------- . * . * Implemented classes: . * ---------------------- . * TFakultaet { = class (TObject) } . * . *) unit Fakultaet; // ---------- . interface // ---------- . type PArrayOfByte = ^TArrayOfByte; TArrayOfByte = packed array of Byte; type TFakultaet = class { Private-Deklarationen } private FMethode: Integer; FN: Integer; FNErg: TArrayOfByte; FNErgLen: Integer; FMultiplyIterations: Int64; FUpdateCarryIterations: Int64; procedure Calculate_1 ({keine Parameter}); procedure Calculate_2 ({keine Parameter}); procedure Calculate_3 ({keine Parameter}); procedure SetN (const AN: Integer); function GetResult ({keine Parameter}): String; { Protected-Deklarationen } protected { Public-Deklarationen } public constructor Create ({keine Parameter}); destructor Destroy ({keine Parameter}); override; property M: Integer read FMethode write FMethode; property N: Integer read FN write SetN; property NumDigits: Integer read FNErgLen; property M_Iterations: Int64 read FMultiplyIterations; property UC_Iterations: Int64 read FUpdateCarryIterations; property Result: String read GetResult; class function GetBestM: Integer; { Published-Deklarationen } published end; // --------------- . implementation // --------------- . const ERR_NO_SUCH_METHODE = -1; ERR_BUFFER_OVERFLOW_1 = -2; ERR_BUFFER_OVERFLOW_2 = -3; const MAX = (8*(1024*1024)); // Max. Anzahl an Ziffern . NAN = Byte(-1); // 'Flag' für ~N~ot ~A~ ~N~umber . // ---------------------------------------------------------------------------- . // Berechnung von N! - Erste (AKA "originale") Implementierung . // ---------------------------------------------------------------------------- . procedure TFakultaet.Calculate_1 (); var i, c: Integer; begin // Grundinitialisierung . for i := High(FNErg) downto 0 do FNErg[i] := NAN; // Ergebnis für 1! initialisieren . FNErg[0] := 1; // Ergebnis für N! berechnen . if (FN > 1) then begin i := 2; while (i <= FN) do begin FNErgLen := 0; // Index . c := 0; // (Teil-) Ergebnis; akkumulativ; eine Art "Carry" . // Berechne: (Bisheriges Ergebnis) * i . while ((FNErgLen <= High(FNErg)) and (FNErg[FNErgLen] <> NAN)) do begin c := ((i * Integer(FNErg[FNErgLen])) + c); FNErg[FNErgLen] := Byte(c mod 10); c := (c div 10); Inc(FNErgLen); Inc(FMultiplyIterations); end; // Bei Buffer-Überlauf: Fehler setzen, raus hier . if (FNErgLen > High(FNErg)) then begin FNErgLen := ERR_BUFFER_OVERFLOW_1; Exit; end; // Aktualisiere: angelaufener Übertrag c . while ((FNErgLen <= High(FNErg)) and (c > 0)) do begin FNErg[FNErgLen] := Byte(c mod 10); c := (c div 10); Inc(FNErgLen); Inc(FUpdateCarryIterations); end; // Bei Buffer-Überlauf: Fehler setzen, raus hier . if (FNErgLen > High(FNErg)) then begin FNErgLen := ERR_BUFFER_OVERFLOW_2; Exit; end; Inc(i); end; end else FNErgLen := 1; end; // ---------------------------------------------------------------------------- . // Berechnung von N! - Zweite Implementierung: . // High(FNErg) wurde durch (MAX-1) ersetzt, da das Feld *IMMER* . // MAX Byte groß ist (indiziert von [0 .. (MAX-1)] . // ---------------------------------------------------------------------------- . procedure TFakultaet.Calculate_2 (); var i, c: Integer; begin // Grundinitialisierung . for i := (MAX - 1) downto 0 do FNErg[i] := NAN; // Ergebnis für 1! initialisieren . FNErg[0] := 1; // Ergebnis für N! berechnen . if (FN > 1) then begin i := 2; while (i <= FN) do begin FNErgLen := 0; // Index . c := 0; // (Teil-) Ergebnis; akkumulativ; eine Art "Carry" . // Berechne: (Bisheriges Ergebnis) * i . while ((FNErgLen <= (MAX-1)) and (FNErg[FNErgLen] <> NAN)) do begin c := ((i*Integer(FNErg[FNErgLen])) + c); FNErg[FNErgLen] := Byte(c mod 10); c := (c div 10); Inc(FNErgLen); Inc(FMultiplyIterations); end; // Bei Buffer-Überlauf: Fehler setzen, raus hier . if (FNErgLen > (MAX-1)) then begin FNErgLen := ERR_BUFFER_OVERFLOW_1; Exit; end; // Aktualisiere: angelaufener Übertrag c . while ((FNErgLen <= (MAX-1)) and (c > 0)) do begin FNErg[FNErgLen] := Byte(c mod 10); c := (c div 10); Inc(FNErgLen); Inc(FUpdateCarryIterations); end; // Bei Buffer-Überlauf: Fehler setzen, raus hier . if (FNErgLen > (MAX-1)) then begin FNErgLen := ERR_BUFFER_OVERFLOW_2; Exit; end; Inc(i); end; end else FNErgLen := 1; end; // ---------------------------------------------------------------------------- . // Berechnung von N! - Zweite Implementierung: . // High(FNErg) wurde durch (MAX-1) ersetzt, da das Feld *IMMER* . // MAX Byte groß ist (indiziert von [0 .. (MAX-1)] . // ---------------------------------------------------------------------------- . procedure TFakultaet.Calculate_3 (); var i, c: Integer; begin // Grundinitialisierung . for i := (MAX - 1) downto 0 do FNErg[i] := NAN; // Ergebnis für 1! initialisieren . FNErg[0] := 1; // Ergebnis für N! berechnen . if (FN > 1) then begin i := 2; while (i <= FN) do begin FNErgLen := 0; // Index . c := 0; // (Teil-) Ergebnis; akkumulativ; eine Art "Carry" . // Berechne: (Bisheriges Ergebnis) * i . while (FNErg[FNErgLen] <> NAN) do begin c := ((i * Integer(FNErg[FNErgLen])) + c); FNErg[FNErgLen] := Byte(c mod 10); c := (c div 10); Inc(FNErgLen); Inc(FMultiplyIterations); end; // Aktualisiere: angelaufener Übertrag c . while (c > 0) do begin FNErg[FNErgLen] := Byte(c mod 10); c := (c div 10); Inc(FNErgLen); Inc(FUpdateCarryIterations); end; Inc(i); end; end else FNErgLen := 1; end; // ---------------------------------------------------------------------------- . procedure TFakultaet.SetN (const AN: Integer); begin if ((AN > 0) and (AN <> FN)) then begin FN := AN; FMultiplyIterations := 0; FUpdateCarryIterations := 0; case FMethode of // N! (sprich: 'N-Fakultät') berechnen . 1: Calculate_1(); 2: Calculate_2(); 3: Calculate_3(); else FNErgLen := ERR_NO_SUCH_METHODE; end; end; end; // ---------------------------------------------------------------------------- . function TFakultaet.GetResult (): String; var i, j: Integer; begin if (FNErgLen < 0) then begin case FNErgLen of ERR_NO_SUCH_METHODE: Result := 'No such methode'; ERR_BUFFER_OVERFLOW_1: Result := 'Digit buffer overflow while in loop #1 (calculating result)'; ERR_BUFFER_OVERFLOW_2: Result := 'Digit buffer overflow while in loop #2 (updating result /w carry)'; else Result := ''; end; end else if (FN <= 0) then Result := '1' // negative Werte und '0' sind '1' . else begin i := 0; j := FNErgLen; SetLength(Result,j); while (i < FNErgLen) do begin // sonst: Ergebnis zu String zusammensetzen . Result[j] := Chr(Ord('0') + FNErg[i]); Inc(i); Dec(j); end; end; end; // ---------------------------------------------------------------------------- . constructor TFakultaet.Create (); begin inherited; // Initialisierung: . FMethode := TFakultaet.GetBestM(); // .) beste (= schnellste) Implementierung . FN := (-1); // .) keine Fakultät . SetLength(FNErg,MAX); // .) Speicherbuffer (für Berechnung) allokieren . FNErgLen := 0; // .) keine Ergebnislänge . FMultiplyIterations := 0; FUpdateCarryIterations := 0; end; // ---------------------------------------------------------------------------- . destructor TFakultaet.Destroy (); begin SetLength(FNErg,0); // Shutdown: . inherited; // .) Speicherbuffer freigeben . end; // ---------------------------------------------------------------------------- . class function TFakultaet.GetBestM: Integer; begin Result := 3; // Zugleich auch die "höchste" Methode: . // gültig sind 1 .. GetBestM . end; // keine Initialisierung. . end. Die ausführbare Datei -- auf Wunsch inklusive komplettem Quelltext -- steht in der Download-Sektion als ZIP-Archiv zur Verfügung. Folgende Dateien stehen zum Download zur Verfügung:
Zusätzliche Informationen:
Web- & Mail-Adresse(n):
|