|
Antwort |
Registriert seit: 5. Apr 2004
Auch euch will ich meinen Source nicht vorbehalten:
Delphi-Quellcode:
function Prim(Zahl: Cardinal): Boolean;
var Teiler: PCardinal; Wurzel: Cardinal; begin Result := True; // Result = True if not odd(Zahl) OR (Zahl <= 5) then // Ist die Zahl nich ungerade oder kleiner als 5, dann begin if (Zahl <> 2) AND (Zahl <> 3) AND (Zahl <> 5) then // Ist die Zahl nicht 2 und nicht 3 und nicht 5, dann Result := False; // Result = False Exit; // Exit end; Teiler := @PrimS[0]; // Teiler = @PrimS[0] Wurzel := Trunc(sqrt(Zahl)); // Wurzel aus der Zahl while Teiler^ <= Wurzel do // Solange Teiler^ <= Wurzel ist, mache begin if Zahl mod Teiler^ = 0 then // Ist Zahl / Teiler^ = Rest 0, dann begin Result := False; // Result = False Break; // Break end; Inc(Teiler); // Teiler erhöhen um 1 end; end;
Delphi-Quellcode:
Das Programm überprüft 10.000.000 Zahlen in erstaunlichen 6 Sekunden.
procedure TMainForm.StartButClick(Sender: TObject);
var Start, Ende: Real; Z: PCardinal; X, LPrim: Cardinal; PrimF: TStringList; begin try Von := StrToInt(VonEdit.Text); // Start Bis := StrToInt(BisEdit.Text); // Endwert if Bis > 10 then SetLength(PrimS, Trunc(0.4*Bis-(Bis/4))) // Größe des Array: 0.4*Bis-(Bis/4) else SetLength(PrimS, 4); LPrim := 0; // Letze Prim = 0 Z := @PrimS[0]; // Gefundene Prims = 0 if (Von >= 0) AND (Bis >= 0) AND (Von < Bis) then // Von >= 0; Bis >= 0; Von < Bis; begin Start := GetTickCount; // Start-Zeit for X := Von to Bis do // Schleife: Startwert -> Endwert begin if Prim(X) then // Funktion ausführen, wenn Prim dann begin Z^ := X; // Prim in Array schreiben Inc(Z); // Z erhöhen um 1 LPrim := X; // Letze Prim = X end; if X mod 20000 = 0 then // Wenn X : 20.000 = Rest 0, dann begin Application.ProcessMessages; // Anwendung aktualisieren PrimLab.Caption := 'Aktuelle Primzahl: ' + IntToStr(LPrim); // Akt. Primzahl anzeigen end; end; Ende := GetTickCount; // End-Zeit DauerLab.Caption := 'Diese Überprüfung hat ' + FloatToStr((Ende - Start) / 1000) + ' Sekunden gedauert.'; // Dauer anzeigen PrimLab.Caption := 'Speichern...'; // "Speichern..." anzeigen Z := @PrimS[0]; // Z auf 0 stellen PrimF := TStringList.Create; // Stringlist erzeugen for X := 0 to Length(PrimS)-1 do // Von 0 bis Größe des Array begin if Z^ = 0 then // Wenn Z^ = 0, dann Break; // Break PrimF.Add(IntToStr(Z^)); // Prim in Stringlist schreiben Inc(Z); // Z erhöhen um 1 end; PrimF.SaveToFile('Prim.txt'); // Stringlist speichern PrimF.Free; // Stringlist freigeben PrimLab.Caption := 'Aktuelle Primzahl: ' + IntToStr(LPrim); // Akt. Primzahl anzeigen end else ShowMessage('Ungültige Eingabe(n)!'); // Bei falschen Eingaben, Nachricht anzeigen except ShowMessage('Es ist ein Fehler aufgetreten!'); // Wenn Fehler auftritt, Nachricht anzeigen end; end; Und das Speichern geht so schnell, dass man "Speichern..." gar nicht sieht. |
|
#71
@Phantom1:
so es is Wochenende und ich habe mir mal deinen Algo genauer angeschaut. Vorweg: unsere beiden Siebe unterscheiden sich an wesentlichen Punkten, sind also nicht identisch, wenn auch die mathematische Grundlage identisch scheint. Bevor wir aber weiter versuchen deinen Algo zu optimieren, müssen wir ihr erstmal korrekt lauffähig bekommen. D.h. als erstes habe ich überprüft ob dein Algo korrekt arbeitet. Leider ist dies nicht der Fall. Mein Testcode dazu ist:
Delphi-Quellcode:
Wie du siehtst habe ich das ganze Dateihandling rausgenommen, da es irrelevant ist. Dafür habe ich aber die Anzahl der Primzahlen integriert und später dann mein eigenes Sieb parallel arbeitend integriert, um die Resultate direkt vergleichen zu können.
function SavePrimes(MaxPrime: Cardinal): Cardinal;
const CACHE = 64*1024; STEMPEL: array[0..7] of Byte = (1, 7, 11, 13, 17, 19, 23, 29); MODS: array[0..29] of Byte = (0, 1, 0, 0, 0, 0, 0, 2, 0, 0, 0, 4, 0, 8, 0, 0, 0, 16, 0, 32, 0, 0, 0, 64, 0, 0, 0, 0, 0, 128); var Primes, PrimesLUT: array of Byte; Count,FailsIndex,FailsNonPrime,P,Q,Index, i, j, k, PrimeLen, PrimeBits, Num, Num2, m, mbit, s: Cardinal; QisPrime: Boolean; f: TextFile; begin Index := 1; FailsIndex := 0; FailsNonPrime := 0; Count := 0; SetLength(PrimesLUT, Trunc(Sqrt(MaxPrime)/30)); // max 2184 Byte für 2^32 ;-) PrimesLUT[0]:=$01; PrimeLen:=Length(PrimesLUT); PrimeBits:=PrimeLen*30; for i:=0 to Trunc(Sqrt(PrimeBits)/30) do for j:=0 to 7 do if PrimesLUT[i] and (1 shl j)=0 then begin s:=STEMPEL[j]; Num:=i*30+s; Num2:=Num*Num; mbit:=Num2 mod 30; m:=(Num2-mbit) div 30; while m<PrimeLen do begin PrimesLUT[m]:=PrimesLUT[m] or MODS[mbit]; Inc(m, i); Inc(mbit, s); if mbit>29 then begin Dec(mbit, 30); Inc(m); end; end; end; SetLength(Primes, CACHE); PrimeLen:=Length(Primes); PrimeBits:=PrimeLen*30; for k:=0 to MaxPrime div PrimeBits do begin FillChar(Primes[0], PrimeLen, 0); for i:=0 to Trunc(Sqrt((k+1)*PrimeBits)/30) do for j:=0 to 7 do if PrimesLUT[i] and (1 shl j)=0 then begin s:=STEMPEL[j]; Num:=i*30+s; if k=0 then Num2:=Num*Num else Num2:=Trunc(k*PrimeBits/Num)*Num+Num; mbit:=Num2 mod 30; m:=(Num2-mbit) div 30-k*PrimeLen; while m<PrimeLen do begin primes[m]:=Primes[m] or MODS[mbit]; Inc(m, i); Inc(mbit, s); if mbit>29 then begin Dec(mbit, 30); Inc(m); end; end; end; for I := 0 to PrimeLen-1 do for J := 0 to 7 do begin Q := k * PrimeBits + I * 30 + Stempel[J]; if Q > MaxPrime then Break; if not ((I = 0) and (J = 0) and (K = 0)) and (Primes[I] and (1 shl J) = 0) then begin P := Prime.Primes[Index]; if Q <> P then begin QisPrime := Prime.IsPrime(Q); Inc(FailsIndex); if not QisPrime then Inc(FailsNonPrime); WriteLn('Fails: ', FailsIndex:5, '/', FailsNonPrime:5, ', Index: ', Index:12, ', Q: ', Q:12, ', ',QisPrime:6, ', P: ', P:12, ', ', Prime.IsPrime(P):6); while (Q > P) and (Index < Prime.Primes.MaxIndex) do begin Inc(Index); P := Prime.Primes[Index]; if P <> Q then begin Inc(FailsIndex); WriteLn('Fails: ', FailsIndex:5, '/', FailsNonPrime:5, ', Index: ', Index:12, ', Q: ', Q:12, ', ',QisPrime:6, ', P: ', P:12, ', ', Prime.IsPrime(P):6); end; end; WriteLn; if QIsPrime then Inc(Index); end else Inc(Index); Inc(Count); end; end; end; Result := count; end; Ein Ausschnitt des obigen Algo. in meiner Console sieht dann so aus:
Code:
Aufruf: mit SavePrimes($FFFFFFFB) -> $FFFFFFFB = 4294967291 ist höchste Primzahl < 2^32 mit Primzahlindex 203280221.
Fails: 1/ 0, Index: 1, Q: 7, TRUE, P: 2, TRUE
Fails: 2/ 0, Index: 2, Q: 7, TRUE, P: 3, TRUE Fails: 3/ 0, Index: 3, Q: 7, TRUE, P: 5, TRUE Fails: 4/ 1, Index: 203233127, Q: 4293918781, FALSE, P: 4293918787, TRUE Fails: 5/ 2, Index: 203233128, Q: 4293918791, FALSE, P: 4293918793, TRUE Fails: 6/ 3, Index: 203233131, Q: 4293918877, FALSE, P: 4293918883, TRUE Fails: 7/ 4, Index: 203233132, Q: 4293918887, FALSE, P: 4293918907, TRUE Fails: 8/ 5, Index: 203233133, Q: 4293918913, FALSE, P: 4293918953, TRUE Fails: 9/ 6, Index: 203233133, Q: 4293918947, FALSE, P: 4293918953, TRUE .... Fails: 38283/38280, Index: 203280214, Q: 4294967101, FALSE, P: 4294967111, TRUE Fails: 38284/38281, Index: 203280214, Q: 4294967107, FALSE, P: 4294967111, TRUE Fails: 38285/38282, Index: 203280219, Q: 4294967213, FALSE, P: 4294967231, TRUE Fails: 38286/38283, Index: 203280220, Q: 4294967273, FALSE, P: 4294967279, TRUE .... Fails: 38287/38284, Index: 203280222, Q: 15, FALSE, P: 0, FALSE Fails: 38288/38285, Index: 203280222, Q: 21, FALSE, P: 0, FALSE Fails: 38289/38285, Index: 203280222, Q: 37, TRUE, P: 0, FALSE Fails: 38290/38285, Index: 203280223, Q: 61, TRUE, P: 0, FALSE Fails: 38291/38286, Index: 203280224, Q: 75, FALSE, P: 0, FALSE Fails: 38292/38287, Index: 203280224, Q: 81, FALSE, P: 0, FALSE .... Fails: 113112/105327, Index: 203288004, Q: 917403, FALSE, P: 0, FALSE Fails: 113113/105328, Index: 203288004, Q: 917425, FALSE, P: 0, FALSE Fails: 113114/105329, Index: 203288004, Q: 917433, FALSE, P: 0, FALSE Fails: 113115/105330, Index: 203288004, Q: 917463, FALSE, P: 0, FALSE Fails: 113116/105331, Index: 203288004, Q: 917467, FALSE, P: 0, FALSE Fails: 113117/105332, Index: 203288004, Q: 917497, FALSE, P: 0, FALSE Fails: gibt ab wieviele Fehlresultat der Algo macht -> Anzahl Indexfehler/Anzahl fehlerhafter Primzahlen (sind also zusammengesetzte Zahlen die dein Algo als Primzahlen ausgibt). Index: der Index der Primzahl in der Primzahltabelle. Q: die Zahl die dein Algo als Primzahl ausgibt, danach FALSE/TRUE je nachdem ob Q tatsächlich eine Primzahl ist. P: die Primzahl die mein Algo. zum Index berechnet, FALSE/TRUE jenachdem ob P eine Primzahl ist Die ersten 3 Zeilen können wir ignorieren, da dein Algo keine direkte Berechnung zu den ersten 3 Primzahlen bietet. Ab Primzahlindex 203233127 erzeugt dein Algo nun Zahlen die keine Primzahlen sind, er arbeitet ab da falsch. Das geht bis Primzahlindex 203280220 was exakt der Moment ist wo der Algo im Grunde terminieren müsste. Bis zu diesem Bereich hat dein Algo also 38287 Zahlen als Primzahlen erzeugt die aber zusammengesetzte Zahlen sind. Der letzte Block ab dem P= 0 ist, hätte dein Algo bei der Zeile
Delphi-Quellcode:
schon längst terminieren müssen. Er rechnet aber weiter da wie man sieht Q nun < $FFFFFFFB ist, sprich ein Integer Überlauf dazu führt das der Algo noch mehr falsche Zahlen berechnet. Da er aber nicht terminiert beendet sich der Algo erst mit der äußersten Schleife k und erzeugt somit 113117 falsche Antworten.
for I := 0 to PrimeLen-1 do
for J := 0 to 7 do begin Q := k * PrimeBits + I * 30 + Stempel[J]; if Q > MaxPrime then Break; // <--- Nun zur Performance: ich habe mit nachfolgendem Source getestet. Auch hier wieder die Dateioperationen und Stringkonvertierungen entfernt, dafür aber unterschieden ob man nur die Anzahl der Primzahlen oder auch die Primzahlen wertmäßig exakt berechnen möchte. Diese Unterscheidung ist wichtig beim direkten Vergleich der unterschiedlichen Verfahren.
Delphi-Quellcode:
Laufzeiten:
function SavePrimes1(MaxPrime: Cardinal; CalcPrimes: Boolean): Cardinal;
const CACHE = 64*1024; STEMPEL: array[0..7] of Byte = (1, 7, 11, 13, 17, 19, 23, 29); MODS: array[0..29] of Byte = (0, 1, 0, 0, 0, 0, 0, 2, 0, 0, 0, 4, 0, 8, 0, 0, 0, 16, 0, 32, 0, 0, 0, 64, 0, 0, 0, 0, 0, 128); var Primes, PrimesLUT: array of Byte; Count, i, j, k, PrimeLen, PrimeBits, Num, Num2, m, mbit, s: Cardinal; begin SetLength(PrimesLUT, Trunc(Sqrt(MaxPrime)/30)); // max 2184 Byte für 2^32 ;-) PrimesLUT[0]:=$01; PrimeLen:=Length(PrimesLUT); PrimeBits:=PrimeLen*30; for i:=0 to Trunc(Sqrt(PrimeBits)/30) do for j:=0 to 7 do if PrimesLUT[i] and (1 shl j)=0 then begin s:=STEMPEL[j]; Num:=i*30+s; Num2:=Num*Num; mbit:=Num2 mod 30; m:=(Num2-mbit) div 30; while m<PrimeLen do begin PrimesLUT[m]:=PrimesLUT[m] or MODS[mbit]; Inc(m, i); Inc(mbit, s); if mbit>29 then begin Dec(mbit, 30); Inc(m); end; end; end; SetLength(Primes, CACHE); PrimeLen:=Length(Primes); PrimeBits:=PrimeLen*30; for k:=0 to MaxPrime div PrimeBits do begin FillChar(Primes[0], PrimeLen, 0); for i:=0 to Trunc(Sqrt((k+1)*PrimeBits)/30) do for j:=0 to 7 do if PrimesLUT[i] and (1 shl j)=0 then begin s:=STEMPEL[j]; Num:=i*30+s; if k=0 then Num2:=Num*Num else Num2:=Trunc(k*PrimeBits/Num)*Num+Num; mbit:=Num2 mod 30; m:=(Num2-mbit) div 30-k*PrimeLen; while m<PrimeLen do begin primes[m]:=Primes[m] or MODS[mbit]; Inc(m, i); Inc(mbit, s); if mbit>29 then begin Dec(mbit, 30); Inc(m); end; end; end; if CalcPrimes then for I := 0 to PrimeLen-1 do for J := 0 to 7 do begin if (k * PrimeBits + I * 30 + Stempel[J]) > MaxPrime then Break; if not ((I = 0) and (J = 0) and (K = 0)) and (Primes[I] and (1 shl J) = 0) then Inc(Count); end; end; Result := count; end; procedure TestPrimes; var I,P: Cardinal; begin StartTimer; Primes.IndexOf($7FFFFFFF); Writeln(Secs:10:2, ' sec'); StartTimer; SavePrimes1($7FFFFFFF, False); Writeln(Secs:10:2, ' sec'); I := 1; StartTimer; repeat P := Primes[I]; Inc(I); until P >= $7FFFFFFF; Writeln(Secs:10:2, ' sec'); StartTimer; SavePrimes1($7FFFFFFF, True); Writeln(Secs:10:2, ' sec'); end;
Code:
auf einem P4 1.5GHz 512Mb und Delphi 5.
Primes.IndexOf($7FFFFFFF) = 12.74 sec
SavePrimes($7FFFFFFF, False) = 51.38 sec loop Primes[i] until >= $7FFFFFFF = 30.64 sec SavePrimes($7FFFFFFF, True) = 67.98 sec Die gravierenden Performanceunterschiede zum AMD sind für mich nur durch die Anwendung der Opcodes BTS/BTC in meinen Bitarray[] Funktionen erklärbar. AMD Prozessoren sind mit diesen Operationen wesentlich langsammer als Pentium CPU's. Für AMD's müsste man diese Opcodes durch indizierte Array[] Zugriff + AND/OR Bitmasken per SHL/SHR ersetzen. Alledings, als erstes muß man den Algorithmus sauber lauffähig bekommen, denn Speed nutzt nur dann etwas wenn die berechneten Resultate auch wirklich mathematisch korrekt sind. Gruß Hagen |
Zitat |
Delphi 2007 Enterprise |
#72
Falls es hier jemanden interessiert: Ich habe mal PrimeGen, genauergesagt nur den FastSieve.c Code, in Delphi implementiert: Alle Primzahlen bis 500.000.000 (genau : 500.660.190) findet der in 840ms. Pentium M 1.5GHz. Kein Assembler, nur Delphi, aber mit Pointern statt Array-Zugriffen (Sonst 1200ms).
Naja, um genau zu sein, erstellt er 'nur' das Sieb. Ich habe mir nicht die Mühe gemacht, den Code so humzufrickeln, das er alle Primzahlen bis genau 500 Mio findet. PrimeGen berechnet immer einen Block von 960960 Zahlen. Ich habe den Code so original wie möglich belassen. Der Code ist aber (eben) C-Code, bei dem handoptimiert (d.h Register-Variablen etc.) wurde, das musste natürlich weg. Egal. Der Code benutzt die ersten 3509 Primzahlen als Konstante, was vielleicht geschummelt ist, aber ich denke, wenn man das berechnen würde, dürfte das die Performance nicht runterziehen. Offensichtlich muss man keinen hochoptimierenden Assemblercode mehr schreiben, um schnellen Code zu erzeugen. Code kann jeder selbst basteln (nach 'sieve of atkins' und/oder PrimeGen googeln und den Source suchen'). Hier ist mein Testprogramm: |
Zitat |
|
#73
Wow, das ist echt schnell. Der Source sieht zudem auch ziemlich einfach aus.
Muß mir doch mal den Original Code von PrimeGen anschauen. Gruß Hagen |
Zitat |
Delphi 10.4 Sydney |
#74
@negaH: ich muss meinen Code wohl doch nochmal genauer untersuchen... Ich hatte immer nur die Primzahlen bis 50 mio überprüft, das noch fehler kommen hätt ich nicht gedacht.
@alzaimar: Der Code ist wirklich echt fix, auf meinem CPU braucht der Code ganze 541ms (geringfügig schneller noch als negaH's code). Hab da gleich mal ein paar fragen: was für eine Bedeutung hat die Konstate "B32 = 1001"? müsste das nicht 1024 sein? Wozu dient das Konstanten-Array "two" ? es wird in deinem Code nicht verwendet. Müsste dein Code nicht jetzt schon mehr als 500 mio zahlen berechnen können? So wie ich das sehe 32749^2 ergibt das etwa 1 mrd. Er müsste also jetzt schon alle Primzahl bis 1 mrd finden können. mfg Phantom1 |
Zitat |
Delphi 2007 Enterprise |
#75
Hi Phantom1,
Erstmal! Wichtig! Es ist nicht mein Code, ehrlich. Ihr solltet euch den Primegen-Code ziehen und selbst durchlesen. Ich meine, ich hätte ihn ja gern entwickelt, hab ich aber nicht. Die 1001 ist die obere Grenze für L1-Cache. Steht in einem der Quelltexte drin. Da steht auch, das man diesen Wert hochsetzen kann, je nachdem, welchen Prozessor man hat. Da ich keine Ahnung von dem Code habe, lass ich die Finger davon. Ihr seit die Primzahl-Spezialisten, ich denke, ihr kriegt das hin. Mir ging es nur darum, "aus Spass" mal eine existierende Implementierung in Delphi zu coden, um zu sehen, wie weit man mit Delphi kommt. Sehr weit, wie es scheint. Keiner braucht mehr C, um schnelle Programme zu schreiben. Zu dem Array 'Two'... stimmt ja: Two[i] <==> 1 shl i. Ich meine, das shiften ist schneller, probiers aus. Und ja, der code schafft alle Primzahlen bis 2^31, denke ich. Wieso auch nicht? Ich hatte mir auch den ecprime-Code angeschaut, aber der ist hässlich. Besteht nur aus switches und optimiert so, das einem schlecht wird. Man kann es auch übertreiben... Aber, vielleicht kann man ja auch dem doch noch was rausholen. Auf jeden Fall ist das hier ein sehr unterhaltsamer und informativer Thread. Was ich hier an Optimierung etc. gelernt habe, ist echt nicht schlecht. |
Zitat |
Delphi 2007 Enterprise |
#76
Hehe, da war doch glatt ein Fehler drin. Hier ist der funktionierende Code.
|
Zitat |
Delphi 4 Standard |
#77
Hi Leutz
ich hab mir diesen Thread hier eben mal angescheut und versucht das nachzuvollziehen aber dank meinem uralten Delphi 4 läuft bei mir kein stück von eurem code könnte jemand so nett sein und mir was ganz einfaches schreiben was in endlosschleife läuft die ich abbrechen kann (mit tastenkombination???) und mir die Primzahlen in einer TXT datei ausgibt (einfach untereinander) und wenn ich das programm beende und wieder neustarte bei der zu letzt gefundenen zahl anfängt muss nicht der schnellste allgorithmus sein hauptsache das ist schneller als das in QBasic geschriebene dings was ich hier hab Danke schonmal! EDIT: ich hab mir mal selber was gebastelt! http://home.arcor.de/mystic-x-website/Prime.rar schaut euch das bitte mal an wie kann ich es jetzt noch hinbekommen was er aus der prime.txt die letzte eingetragene zahl ausliest und mir die ausgibt? |
Zitat |
Delphi 2007 Enterprise |
#78
Zitat von Mystic-X:
Hi Leutz
ich hab mir diesen Thread hier eben mal angescheut und versucht das nachzuvollziehen aber dank meinem uralten Delphi 4 läuft bei mir kein stück von eurem code Übrigens,
Zitat von Mystic-X:
...
könnte jemand so nett sein und mir was ganz einfaches schreiben was in endlosschleife läuft die ich abbrechen kann (mit tastenkombination???) und mir die Primzahlen in einer TXT datei ausgibt (einfach untereinander) und wenn ich das programm beende und wieder neustarte bei der zu letzt gefundenen zahl anfängt... |
Zitat |
Delphi 2005 Personal |
#79
Hallo alzaimar!
Ich habe mir gerade mal deinen PrimeGen heruntergeladen und wollte ihn laufenlassen, bekomme aber wegen "uses csPerformance" eine Fehlermeldung, und wenn ichs auskommentiere geht gar nichts. Kann man die unit hier irgendwo herunterladen? Weil ich vor einiger Zeit selbst ein Atkin-Sieb nach wiki-Anleitung erstellt hatte, habe ich eigentlich erwartet, in deinem Code bekanntes wiederzufinden, aber, ährrm, nada. Könntest du vielleicht mal erklären, was genau du machst? Leider verstehe ich (wie vielleicht auch andere) zu wenig von Pointer-Operationen um zu begreifen, was dein Programm tut. Wenn du auch noch deine array-Version anhängen könntest, wäre das sehr schön. Gruß Ullli P.S.: Hoffentlich klingt das jetzt nicht zu sehr nach Auftragserteilung !? |
Zitat |
Ansicht |
Linear-Darstellung |
Zur Hybrid-Darstellung wechseln |
Zur Baum-Darstellung wechseln |
ForumregelnEs ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.
BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus. Trackbacks are an
Pingbacks are an
Refbacks are aus
|
|
Nützliche Links |
Heutige Beiträge |
Sitemap |
Suchen |
Code-Library |
Wer ist online |
Alle Foren als gelesen markieren |
Gehe zu... |
LinkBack |
LinkBack URL |
About LinkBacks |