Schnelle Integer-Multiplikation mit FFT

Hier könnt Ihr gute, von Euch geschriebene Codes posten. Sie müssen auf jeden Fall funktionieren und sollten möglichst effizient, elegant und beispielhaft oder einfach nur cool sein.
Benutzeravatar
Helle
Beiträge: 566
Registriert: 11.11.2004 16:13
Wohnort: Magdeburg

Schnelle Integer-Multiplikation mit FFT

Beitrag von Helle »

Hier mal wieder was für die Freunde großer Zahlen. So richtig habe ich zu dem Thema hier nichts gefunden; also: selbermachen. Der Code ist kein Kleinod der Programmierkunst, aber wer Interesse hat wird mit klarkommen.

Code: Alles auswählen

;- Schnelle Integer-Multiplikation mit FFT für große Zahlen
;- Beide Faktoren müssen gleich groß sein und die Länge muss eine Zweier-Potenz sein. Padding bei Bedarf mit führenden Nullen
;- Berechnungs-Schema läuft ab nach Schmetterlingsgraph
;- Keine Fremd-Bibliotheken!
;- Man beachte: Kein ASM-Anteil :-) !
;- Windows7/64, PureBasic 5.31 (x64)
;- "Helle" Klaus Helbing, 02.10.2015

;Als Quelle (Faktoren) dienen hier 2 Strings
Faktor1$ = "6582018229284824168619876730229402019930943462534319453394436096"  ;64-Bytes lang für Test
Faktor2$ = "5486721320215405789450123459785103469751240504554109782315460125"

For i = 1 To 10              ;für Test. Faktoren-Länge von 2MB ist noch o.K.
  Faktor1$ + Faktor1$
  Faktor2$ + Faktor2$  
Next

;Hier könnte Test auf Länge Faktor1$/Faktor2$ usw. hin

N.q = Len(Faktor1$) << 1
Exponent.q
Mantisse.q
PF1.q
PF2.q
EW.q
Pointer0.q
Pointer1.q
Pointer2.q
Pointer_Produkt.q = N - 1
Pointer_Produkt1.q
RevPos.q
FakPos.q
QuadWert.q
QuadWert1.q
Fehler.q                     ;nur Flag für Fehler-Test

Si.d
Co.d
Pi2.d = #PI * 2
Rad.d
Rad1.d
WR.d
WI.d
ZR.d
ZI.d
ND.d

WL.l

Ziffer.b

If N > 4194304               ;=2^22 als Produkt-Größe = 2^21 als Faktor sind noch o.K.
  If MessageRequester("Warnung!", "Die Faktoren sind zu groß! Das Ergebnis wird mit hoher Wahrscheinlichkeit falsch sein!" + #CRLF$ + "Trotzdem weiter?", #PB_MessageRequester_YesNo) = #PB_MessageRequester_No
    End
  EndIf
EndIf

;Exponent ermitteln; 2^Exponent=N
ND = N 
QuadWert = PeekQ(@ND) & $7FFFFFFFFFFFFFFF   ;Vorzeichen-Bit ausblenden (Bit 63)
Exponent = QuadWert >> 52                   ;Exponent = Bits 52 bis 62 (11 Bits)  
Mantisse = QuadWert & $FFFFFFFFFFFFF        ;Mantisse = Bits 0 bis 51 (52 Bits)
If Mantisse = $FFFFFFFFFFFFF                ;also fast 1
  Mantisse = 0                              ;kann zu Null aufgerundet werden; ist ja Aufaddierung
  QuadWert1 = 1                             ;Exponent dafür um eins erhöhen    
EndIf
If QuadWert = 0  Or Mantisse <> 0 
  Exponent = 0                              ;Null
 Else 
  Exponent = Exponent + QuadWert1 - 1023    ;1023=Bias
EndIf  

If Exponent < 3
  MessageRequester("Fehler!", "Möglicherweise Faktoren zu klein!")
  End 
EndIf

;Speicher-Reservierung; kann sicher noch verbessert werden. Vllt. mal alles zusammenfassen und dann zuteilen
BufferSin = AllocateMemory((N * 8) + 128)
If BufferSin
  BufferSinA = BufferSin + (64 - (BufferSin & $3F))             ;64-er Alignment, klotzen, nicht kleckern!
 Else 
  MessageRequester("Fehler!", "Nicht genügend Speicher für BufferSin!")   ;kann auch noch ein "End" hinter
EndIf

BufferCos = AllocateMemory((N * 8) + 128)
If BufferCos
  BufferCosA = BufferCos + (64 - (BufferCos & $3F))             ;64-er Alignmentr Alignment
 Else 
  MessageRequester("Fehler!", "Nicht genügend Speicher für BufferCos!")
EndIf

BufferFaktor1 = AllocateMemory((2 * N * 8) + 128)
If BufferFaktor1
  BufferFaktor1A = BufferFaktor1 + (64 - (BufferFaktor1 & $3F)) ;64-er Alignment
 Else 
  MessageRequester("Fehler!", "Nicht genügend Speicher für BufferFaktor1!")
EndIf

BufferFaktor2 = AllocateMemory((2 * N * 8) + 128)
If BufferFaktor2
  BufferFaktor2A = BufferFaktor2 + (64 - (BufferFaktor2 & $3F)) ;64-er Alignment
 Else 
  MessageRequester("Fehler!", "Nicht genügend Speicher für BufferFaktor2!")
EndIf

BufferProdukt = AllocateMemory((N * 8) + 128)
If BufferProdukt
  BufferProduktA = BufferProdukt + (64 - (BufferProdukt & $3F)) + 64 ;64-er Alignment, +64 um Unterlauf zu vermeiden
 Else 
  MessageRequester("Fehler!", "Nicht genügend Speicher für BufferProdukt!")
EndIf

BufferDez.q = AllocateMemory(16 + 128)
If BufferDez
  BufferDezA = BufferDez + (64 - (BufferDez & $3F))             ;64-er Alignment
 Else 
  MessageRequester("Fehler!", "Nicht genügend Speicher für BufferDez!")
EndIf

;========================================================================================
TA_Gesamt = ElapsedMilliseconds()

PF1 = @Faktor1$
PF2 = @Faktor2$

TA_Faktoren = ElapsedMilliseconds()
;Faktoren bearbeiten
;Zuerst die Ziffern gemäß reverse Verteilung neu setzen, dabei gleich in Long konvertieren
FakPos = 0
For i = (N / 2) - 1 To 0 Step -1 
  RevPos = FakPos
  RevPos = ((RevPos >> 1) & $55555555) | ((RevPos & $55555555) << 1)
  RevPos = ((RevPos >> 2) & $33333333) | ((RevPos & $33333333) << 2)
  RevPos = ((RevPos >> 4) & $0F0F0F0F) | ((RevPos & $0F0F0F0F) << 4)
  RevPos = ((RevPos >> 8) & $00FF00FF) | ((RevPos & $00FF00FF) << 8)
  RevPos = ( RevPos >> 16            ) | ( RevPos              << 16)
  RevPos = ( RevPos >> (32 - Exponent)) & $FFFFFFFF
  WL = PeekB(PF1 + i) - $30
  PokeL(BufferSinA + 2 * RevPos, WL)
  WL = PeekB(PF2 + i) - $30
  PokeL(BufferCosA + 2 * RevPos, WL)
  FakPos + 1
Next

j = 0
For i = 0 To 2 * N Step 4              ;Step 4 = Long
  WL = PeekL(BufferSinA + i)
  WR = WL
  PokeD(BufferFaktor1A + j, WR)
  PokeD(BufferFaktor1A + j + 16, WR)
  WL = PeekL(BufferCosA + i)
  WR = WL
  PokeD(BufferFaktor2A + j, WR)
  PokeD(BufferFaktor2A + j + 16, WR)
  j + 32
Next
TE_Faktoren = ElapsedMilliseconds() - TA_Faktoren

;Winkel erst hier, weil BufferSinA und BufferCosA oben "missbraucht" werden
TA_Winkel = ElapsedMilliseconds()
Rad1 = Pi2 / N                         ;Konstante 
;SinCos; da dies lahm ist und sowieso beide Werte benötigt werden, wird nur bis Pi/4 (45°) ermittelt und der Rest (bis 180°) nur umkopiert
For k = 0 To (N >> 3)                  ;hier nicht -1 !!!
  Rad = Rad1 * k
  Si = Sin(Rad)
  Co = Cos(Rad)
  PokeD(BufferSinA + k << 3, Si)       ;0-45°
  PokeD(BufferCosA + k << 3, Co)       ;0-45°
  PokeD(BufferCosA + ((N >> 2) - k) << 3, Si)    ;45-90°
  PokeD(BufferCosA + ((N >> 2) + k) << 3, -Si)   ;90-135°
  PokeD(BufferCosA + ((N >> 1) - k) << 3, -Co)   ;135-180°
  PokeD(BufferSinA + ((N >> 2) - k) << 3, Co)    ;45-90°
  PokeD(BufferSinA + ((N >> 2) + k) << 3, Co)    ;90-135°
  PokeD(BufferSinA + ((N >> 1) - k) << 3, Si)    ;135-180°
Next
TE_Winkel = ElapsedMilliseconds() - TA_Winkel

;Faktor1
TA_FFT = ElapsedMilliseconds()
Pointer0 = 2
Pointer1 = 0
Pointer2 = Pointer0
While Pointer2 < N
  While Pointer2 < N
    For k = 1 To Pointer0
      EW = (N / Pointer0 << 1) * Pointer1
      WR = (PeekD(BufferFaktor1A + 2*Pointer2 * 8) * PeekD(BufferCosA + EW * 8) - PeekD(BufferFaktor1A+8 + 2*Pointer2 * 8) * PeekD(BufferSinA + EW * 8))
      WI = (PeekD(BufferFaktor1A+8 + 2*Pointer2 * 8) * PeekD(BufferCosA + EW * 8) + PeekD(BufferFaktor1A + 2*Pointer2 * 8) * PeekD(BufferSinA + EW * 8))
      ZR = PeekD(BufferFaktor1A + 2*(Pointer2 - Pointer0) * 8)
      ZI = PeekD(BufferFaktor1A+8 + 2*(Pointer2 - Pointer0) * 8)
      PokeD(BufferFaktor1A + 2*(Pointer2 - Pointer0) * 8, ZR + WR)
      PokeD(BufferFaktor1A+8 + 2*(Pointer2 - Pointer0) * 8, ZI + WI)
      PokeD(BufferFaktor1A + 2*Pointer2 * 8, ZR - WR)
      PokeD(BufferFaktor1A+8 + 2*Pointer2 * 8, ZI - WI)
      Pointer1 + 1
      Pointer2 + 1
    Next
    Pointer1 = 0
    Pointer2 + Pointer0
  Wend 
  Pointer0 << 1
  Pointer1 = 0
  Pointer2 = Pointer0
Wend
Debug "--------------"
;Faktor2
Pointer0 = 2
Pointer1 = 0
Pointer2 = Pointer0
While Pointer2 < N
  While Pointer2 < N
    For k = 1 To Pointer0
      EW = (N / Pointer0 << 1) * Pointer1
      WR = (PeekD(BufferFaktor2A + 2*Pointer2 * 8) * PeekD(BufferCosA + EW * 8) - PeekD(BufferFaktor2A+8 + 2*Pointer2 * 8) * PeekD(BufferSinA + EW * 8))
      WI = (PeekD(BufferFaktor2A+8 + 2*Pointer2 * 8) * PeekD(BufferCosA + EW * 8) + PeekD(BufferFaktor2A + 2*Pointer2 * 8) * PeekD(BufferSinA + EW * 8))
      ZR = PeekD(BufferFaktor2A + 2*(Pointer2 - Pointer0) * 8)
      ZI = PeekD(BufferFaktor2A+8 + 2*(Pointer2 - Pointer0) * 8)
      PokeD(BufferFaktor2A + 2*(Pointer2 - Pointer0) * 8, ZR + WR)
      PokeD(BufferFaktor2A+8 + 2*(Pointer2 - Pointer0) * 8, ZI + WI)
      PokeD(BufferFaktor2A + 2*Pointer2 * 8, ZR - WR)
      PokeD(BufferFaktor2A+8 + 2*Pointer2 * 8, ZI - WI)
      Pointer1 + 1
      Pointer2 + 1
    Next
    Pointer1 = 0
    Pointer2 + Pointer0
  Wend 
  Pointer0 << 1
  Pointer1 = 0
  Pointer2 = Pointer0
Wend
TE_FFT = ElapsedMilliseconds() - TA_FFT 

;Und jetzt die beiden transformierten Vektoren miteinander multiplizieren
TA_Multi = ElapsedMilliseconds()
For k = 0 To N - 1
  WR = (PeekD(BufferFaktor1A + 2*k * 8) * PeekD(BufferFaktor2A + 2*k * 8)) - (PeekD(BufferFaktor1A+8 + 2*k * 8) * PeekD(BufferFaktor2A+8 + 2*k * 8))    ;hier kann nicht direkt in BufferFaktor1RA geschrieben werden
  PokeD(BufferFaktor1A+8 + 2*k * 8, (PeekD(BufferFaktor1A + 2*k * 8) * PeekD(BufferFaktor2A+8 + 2*k * 8)) + (PeekD(BufferFaktor1A+8 + 2*k * 8) * PeekD(BufferFaktor2A + 2*k * 8)))
  PokeD(BufferFaktor1A + 2*k * 8, WR)  ;weil vorher BufferFaktor1RA noch benötigt wird
Next
TE_Multi = ElapsedMilliseconds() - TA_Multi 

;Die Produkt-Werte wieder revers "verteilen"
TA_Rueck = ElapsedMilliseconds()
FakPos = 0
For i = 0 To N - 1 
  RevPos = FakPos
  RevPos = ((RevPos >> 1) & $55555555) | ((RevPos & $55555555) << 1)
  RevPos = ((RevPos >> 2) & $33333333) | ((RevPos & $33333333) << 2)
  RevPos = ((RevPos >> 4) & $0F0F0F0F) | ((RevPos & $0F0F0F0F) << 4)
  RevPos = ((RevPos >> 8) & $00FF00FF) | ((RevPos & $00FF00FF) << 8)
  RevPos = ( RevPos >> 16            ) | ( RevPos              << 16)
  RevPos = ( RevPos >> (32 - Exponent)) & $FFFFFFFF
  PokeD(BufferFaktor2A + 2*8 * RevPos, PeekD(BufferFaktor1A + 2*i * 8))
  PokeD(BufferFaktor2A+8 + 2*8 * RevPos, PeekD(BufferFaktor1A+8 + 2*i * 8))
  FakPos + 1
Next

;Anfangswerte verteilen für FFT
For i = 0 To N - 2 Step 2
  WR = PeekD(BufferFaktor2A + 2*i * 8) + PeekD(BufferFaktor2A + 2*(i + 1) * 8)
  WI = PeekD(BufferFaktor2A+8 + 2*i * 8) + PeekD(BufferFaktor2A+8 + 2*(i + 1) * 8)
  ZR = PeekD(BufferFaktor2A + 2*i * 8) - PeekD(BufferFaktor2A + 2*(i + 1) * 8)
  ZI = PeekD(BufferFaktor2A+8 + 2*i * 8) - PeekD(BufferFaktor2A+8 + 2*(i + 1) * 8)
  PokeD(BufferFaktor2A + 2*i * 8, WR)
  PokeD(BufferFaktor2A+8 + 2*i * 8, WI)
  PokeD(BufferFaktor2A + 2*(i + 1) * 8, ZR)
  PokeD(BufferFaktor2A+8 + 2*(i + 1) * 8, ZI)
Next

;nochmal FFT, jetzt aber invers (mit neg.Sinus)
Pointer0 = 2
Pointer1 = 0
Pointer2 = Pointer0
While Pointer2 < N
  While Pointer2 < N
    For k = 1 To Pointer0
      EW = (N / Pointer0 << 1) * Pointer1
      WR = (PeekD(BufferFaktor2A + 2*Pointer2 * 8) * PeekD(BufferCosA + EW * 8) + PeekD(BufferFaktor2A+8 + 2*Pointer2 * 8) * PeekD(BufferSinA + EW * 8))  ;neg.Sin, Operand geändert
      WI = (PeekD(BufferFaktor2A+8 + 2*Pointer2 * 8) * PeekD(BufferCosA + EW * 8) - PeekD(BufferFaktor2A + 2*Pointer2 * 8) * PeekD(BufferSinA + EW * 8))  ;neg.Sin, Operand geändert
      ZR = PeekD(BufferFaktor2A + 2*(Pointer2 - Pointer0) * 8)
      ZI = PeekD(BufferFaktor2A+8 + 2*(Pointer2 - Pointer0) * 8)
      PokeD(BufferFaktor2A + 2*(Pointer2 - Pointer0) * 8, ZR + WR)
      PokeD(BufferFaktor2A+8 + 2*(Pointer2 - Pointer0) * 8, ZI + WI)
      PokeD(BufferFaktor2A + 2*Pointer2 * 8, ZR - WR)
      PokeD(BufferFaktor2A+8 + 2*Pointer2 * 8, ZI - WI)
      Pointer1 + 1
      Pointer2 + 1
    Next
    Pointer1 = 0
    Pointer2 + Pointer0
  Wend 
  Pointer0 << 1
  Pointer1 = 0
  Pointer2 = Pointer0
Wend
TE_Rueck = ElapsedMilliseconds() - TA_Rueck

TA_Ergebnis = ElapsedMilliseconds()
;die Doubles von FFT in Integer konvertieren und Koeffizienten aufaddieren mit Zehner-Versatz
Restore Dezi                 ;sicher ist sicher!
Pointer0 = 0
For i = 0 To N - 1           ;vom Produkt alle Werte durch
  ;Konvertierung Double in Integer
  QuadWert = Round(PeekD(BufferFaktor2A + Pointer0), #PB_Round_Nearest)  ;Double zu Quad
  QuadWert >> Exponent       ;=div N
;    ;Fehler-Abschätzung      ;dann Zeile drüber auskommentieren 
;    !xor rdx,rdx             ;sorry, ASM hier der Einfachheit halber
;    !mov rcx,[v_N]
;    !mov rax,[v_QuadWert]
;    !div rcx
;    !mov [v_QuadWert],rax
;    !or rdx,rdx              ;Remainder?
;    !jz @f
;    !mov [v_Fehler],1        ;Ergebnis anrüchig, Faktoren zu groß 
;    !@@:
 ;Hex2Dez
  QuadWert1 = QuadWert
  Ziffer = 0
  Pointer1 = 0
  Pointer2 = 0
  While PeekQ(?Dezi + Pointer1) <> 0
    While (QuadWert - PeekQ(?Dezi + Pointer1)) >= 0
      Ziffer + 1
      QuadWert - PeekQ(?Dezi + Pointer1)
      QuadWert1 = QuadWert
    Wend
    PokeB(BufferDezA + Pointer2, Ziffer)
    Pointer1 + 8
    Pointer2 + 1
    Ziffer = 0
    QuadWert = QuadWert1
  Wend
  PokeB(BufferDezA + Pointer2, QuadWert1)   ;letzte Dezimal-Ziffer abspeichern

  Pointer_Produkt1 = Pointer_Produkt 
  For k = 0 To 15            ;eigentlich hier überdimensioniert
    Ziffer = PeekB(BufferProduktA + Pointer_Produkt1)
    Ziffer + PeekB(BufferDezA + Pointer2)
    PokeB(BufferProduktA + Pointer_Produkt1, Ziffer)
    If Ziffer > 9            ;Übertrag
      Ziffer - 10
      PokeB(BufferProduktA + Pointer_Produkt1, Ziffer)
      Ziffer = PeekB(BufferProduktA + Pointer_Produkt1 - 1) + 1
      PokeB(BufferProduktA + Pointer_Produkt1 - 1, Ziffer)
    EndIf
    Pointer_Produkt1 - 1
    Pointer2 - 1
  Next

  Pointer_Produkt - 1
  Pointer0 + 16              ;Zeiger in BufferFaktor2A
Next

;------------------------
;Ergebnis (Produkt) in ASCII-Ziffern abspeichern
For i = 0 To N Step 8
  PokeQ(BufferProduktA + i, PeekQ(BufferProduktA + i) | $3030303030303030)
Next
PokeB(BufferProduktA + i - 8, 0)  ;Zero-Byte zur Sicherheit für String-Auslesen

;Hier könnte man auch sinnvoller Weise vorher Speicher wieder freigeben
Produkt$ = LTrim(PeekS(BufferProduktA), "0")     ;evtl. führende Null(en) weg
SetClipboardText(Produkt$)        ;oder auskommentieren

TE_Ergebnis = ElapsedMilliseconds() - TA_Ergebnis

TE_Gesamt = ElapsedMilliseconds() - TA_Gesamt

Faktor$ = "Faktoren-Laenge: " + Str(Len(Faktor1$)) + " Bytes" + #CRLF$
Exponent$ = "Exponent 2^ für Faktoren-Laenge: " + Str(Exponent - 1) + #CRLF$
Winkel$ = "Zeit für Sinus/Cosinus-Berechnung: " + Str(TE_Winkel) + " ms" + #CRLF$
Faktoren$ = "Zeit für Faktoren-Aufbereitung: " + Str(TE_Faktoren) + " ms" + #CRLF$
FFT$ = "Zeit für Fast Fourier Transformation: " + Str(TE_FFT) + " ms" + #CRLF$
Multi$ = "Zeit für Multiplikation: " + Str(TE_Multi) + " ms" + #CRLF$
Rueck$ = "Zeit für Rueck-Transformation: " + Str(TE_Rueck) + " ms" + #CRLF$
Ergebnis$ = "Zeit für Ergebnis-Aufbereitung als String: " + Str(TE_Ergebnis) + " ms" + #CRLF$
Gesamt$ = "Zeit gesamt: " + Str(TE_Gesamt) + " ms" + #CRLF$
Fehler$ = "" + #LFCR$        ;oder nachfolgendes 
;  If Fehler                  ;wenn Fehler-Test
;    Fehler$ = "Ergebnis (möglicherweise) fehlerhaft!" + #LFCR$
;   Else
;    Fehler$ = "Ergebnis (mit hoher Wahrscheinlichkeit) o.K.!" + #LFCR$  
;  EndIf
Info$ = Faktor$ + Exponent$ + Winkel$ + Faktoren$ + FFT$ + Multi$ + Rueck$ + Ergebnis$ + Gesamt$ + Fehler$ + #LFCR$

If Len(Produkt$) > 65536 * 2      ;je nach Gusto und was Windows noch sicher anzeigt
  MessageRequester("Helles Multiplikation-FFT-Test", Info$)
 Else
  OpenWindow(0, 0, 0, 1000, 800, "Helles Multiplikation-FFT-Test", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
  StringGadget(0, 10, 10, 980, 780, Info$ + Produkt$, #PB_String_ReadOnly | #ES_MULTILINE | #ESB_DISABLE_LEFT | #ESB_DISABLE_RIGHT | #WS_VSCROLL)
  Repeat : Until WaitWindowEvent() = #PB_Event_CloseWindow
  CloseWindow(0)
EndIf
Z:
DataSection
  Dezi:
  Data.q 1000000000000000    ;begrenze ich mal auf 16 Stellen; ist damit schon überdimensioniert
  Data.q  100000000000000
  Data.q   10000000000000
  Data.q    1000000000000
  Data.q     100000000000
  Data.q      10000000000
  Data.q       1000000000
  Data.q        100000000
  Data.q         10000000
  Data.q          1000000
  Data.q           100000
  Data.q            10000
  Data.q             1000
  Data.q              100
  Data.q               10
  Data.q                0
EndDataSection
Na dann, viel Spaß :mrgreen: !
Helle

P.S.: Natürlich existiert auch eine ASM-Version, aber die ist speziell auf Haswell/Skylake zugeschnitten und teilweise noch in Arbeit.

Edit 3.10.2015: Kleine Optimierungen
Edit 29.12.2015: Speicher-Anforderung optimiert
Zuletzt geändert von Helle am 29.12.2015 12:35, insgesamt 4-mal geändert.
Benutzeravatar
STARGÅTE
Kommando SG1
Beiträge: 6999
Registriert: 01.11.2005 13:34
Wohnort: Glienicke
Kontaktdaten:

Re: Schnelle Integer-Multiplikation mit FFT

Beitrag von STARGÅTE »

Hallo Helle,

vielen danke. Wie du vllt noch weiß, habe ich ja mit ASM eine Lib geschrieben für beliebig große Zahlen, bei der du auch einige Optimierungen gemacht hast.
Gerade bei der Multiplikation verwende ich aktuell nur den Karatsuba-Algorithmus und Multi-Threads zur Beschleunigung.
Hier kommt mir eine FFT natürlich recht.

Da ich mich mit dem Thema jedoch nicht tiefer befasst haben stellen sich mir ein paar Fragen:
  • Für die Fourierkoeffizienten werden hier ja Doubles benutzt, deren Genauigkeit für Hin-und Rücktransformation anscheint ausreicht?
  • Bei ungleicher Länge oder nicht 2er Potenzlänge ist Null-Padding da "besser" als die Zerlegung in passende Teile?
PB 6.01 ― Win 10, 21H2 ― Ryzen 9 3900X, 32 GB ― NVIDIA GeForce RTX 3080 ― Vivaldi 6.0 ― www.unionbytes.de
Aktuelles Projekt: Lizard - Skriptsprache für symbolische Berechnungen und mehr
Benutzeravatar
Helle
Beiträge: 566
Registriert: 11.11.2004 16:13
Wohnort: Magdeburg

Re: Schnelle Integer-Multiplikation mit FFT

Beitrag von Helle »

Sorry, hat etwas gedauert, aber für die Fehler-Einschätzung wollte ich auch die ASM-Version mit heranziehen, und da schlug erstmal Polink mit Fehler bei größeren Faktoren zu. Ist behoben. Die Fehler-Auswertung (vllt. besser: Genauigkeit) ist oben im erweiterten Code mit eingebaut (kann so auch selbst getestet werden). Fazit: Bis Faktoren-Größe von 4MB ist alles o.K., darüber treten Fehler auf (auch verglichen mit den Ergebnissen anderer Programme; hat daaaas gedauert... :mrgreen: ). Mehr gibt die Double-Genauigkeit nicht her. Aber immerhin!
Ob weitere Zerlegungen was bringen müsste mal getestet werden. Vllt. mal Karatsuba drüberstülpen. Ein erstes Speed-Up wäre die Zuweisung der FFT-Transformation pro Faktor in separaten Thread.

Gruß
Helle
Benutzeravatar
NicTheQuick
Ein Admin
Beiträge: 8679
Registriert: 29.08.2004 20:20
Computerausstattung: Ryzen 7 5800X, 32 GB DDR4-3200
Ubuntu 22.04.3 LTS
GeForce RTX 3080 Ti
Wohnort: Saarbrücken
Kontaktdaten:

Re: Schnelle Integer-Multiplikation mit FFT

Beitrag von NicTheQuick »

Ich hatte vor ein paar Wochen mal einen Import für FFTW3 unter Linux gemacht. Könnte das noch zusätzlich helfen?

Code: Alles auswählen

; Die Funktionen für verschiedene Datentypen haben die folgenden Präfixe
;   Double      : fftw_
;   Float       : fftwf_
;   Long Double : fftwl_        (gibt es in Purebasic nicht)
;   Quad        : fftwq_        (gibt es in Purebasic nicht)

; Für Windows (mit Dank an edel: http://www.purebasic.fr/german/viewtopic.php?p=329451#p329451):
; - Für 32-Bit Anwendungen:
;     C:\Program Files (x86)\PureBasic\Compilers\polib.exe /DEF:libfftw3f-3.def /MACHINE:X86 /OUT:fftw3f.lib
;     C:\Program Files (x86)\PureBasic\Compilers\polib.exe /DEF:libfftw3-3.def /MACHINE:X86 /OUT:fftw3.lib
; - Für 64-Bit Anwendungen:
;     C:\Program Files (x86)\PureBasic\Compilers\polib.exe /DEF:libfftw3f-3.def /MACHINE:X64 /OUT:fftw3f.lib
;     C:\Program Files (x86)\PureBasic\Compilers\polib.exe /DEF:libfftw3-3.def /MACHINE:X64 /OUT:fftw3.lib

DeclareModule FFTW3
	EnableExplicit
	
	#FFTW_FORWARD = -1
	#FFTW_BACKWARD = 1
	
	#FFTW_NO_TIMELIMIT = -1.0
	
	; documented flags
	#FFTW_MEASURE = (0)
	#FFTW_DESTROY_INPUT   = (1 << 0)
	#FFTW_UNALIGNED       = (1 << 1)
	#FFTW_CONSERVE_MEMORY = (1 << 2)
	#FFTW_EXHAUSTIVE      = (1 << 3) ; NO_EXHAUSTIVE is Default
	#FFTW_PRESERVE_INPUT  = (1 << 4) ; cancels FFTW_DESTROY_INPUT
	#FFTW_PATIENT         = (1 << 5) ; IMPATIENT is Default
	#FFTW_ESTIMATE        = (1 << 6)
	#FFTW_WISDOM_ONLY     = (1 << 21)
	
	; undocumented beyond-guru flags
	#FFTW_ESTIMATE_PATIENT       = (1 << 7)
	#FFTW_BELIEVE_PCOST          = (1 << 8)
	#FFTW_NO_DFT_R2HC            = (1 << 9)
	#FFTW_NO_NONTHREADED         = (1 << 10)
	#FFTW_NO_BUFFERING           = (1 << 11)
	#FFTW_NO_INDIRECT_OP         = (1 << 12)
	#FFTW_ALLOW_LARGE_GENERIC    = (1 << 13) ; NO_LARGE_GENERIC is Default
	#FFTW_NO_RANK_SPLITS         = (1 << 14)
	#FFTW_NO_VRANK_SPLITS        = (1 << 15)
	#FFTW_NO_VRECURSE            = (1 << 16)
	#FFTW_NO_SIMD                = (1 << 17)
	#FFTW_NO_SLOW                = (1 << 18)
	#FFTW_NO_FIXED_RADIX_LARGE_N = (1 << 19)
	#FFTW_ALLOW_PRUNING          = (1 << 20)
	
	Macro size_t
		i
	EndMacro
	
	Macro fftwDefineApi(X, R, library)
		Structure X#_complexArray
			R.R[2]
		EndStructure
		Structure X#_complex
			StructureUnion
				R.R[2]
				c.X#_complexArray[0]
			EndStructureUnion
		EndStructure
		Structure X#_plan
		EndStructure
		Structure X#_iodim
			n.l
			is.l
			os.l
		EndStructure
		Structure X#_iodim64
			n.i
			is.i
			os.i
		EndStructure
		Structure X#_R
			R.R[0]
		EndStructure
		
		Declare X#_execute(*p.X#_plan) ;return void
		Declare.i X#_plan_dft(rank.l, *n.Long,
		              *in.X#_complex, *out.X#_complex, sign.l, flags.l) ;return *X#_plan
		Declare.i X#_plan_dft_1d(n.l, *in.X#_complex, *out.X#_complex, sign.l,
		                 flags.l) ;return *X#_plan
		Declare.i X#_plan_dft_2d(n0.l, n1.l,
		                 *in.X#_complex, *out.X#_complex, sign.l, flags.l) ;return *X#_plan
		Declare.i X#_plan_dft_3d(n0.l, n1.l, n2.l,
		                 *in.X#_complex, *out.X#_complex, sign.l, flags.l) ;return *X#_plan
		Declare.i X#_plan_many_dft(rank.l, *n.Long,
		                 howmany.l,
		                 *in.X#_complex, *inembed.Long,
		                 istride.l, idist.l,
		                 *out.X#_complex, *onembed.Long,
		                 ostride.l, odist.l,
		                 sign.l, flags.l) ;return *X#_plan
		Declare.i X#_plan_guru_dft(rank.l, *dims.X#_iodim,
		                   howmany_rank.l,
		                   *howmany_dims.X#_iodim,
		                   *ri.x#_R, *ii.x#_R, *ro.x#_R, *io.x#_R,
		                   flags.l) ;return *X#_plan
		Declare.i X#_plan_guru_split_sft(rank.l, *dims.X#_iodim,
		                         howmany_rank.l,
		                         *howmany_dims.X#_iodim,
		                         *ri.x#_R, *ii.x#_R, *ro.x#_R, *io.x#_R,
		                         flags.l) ;return *X#_plan
		Declare.i X#_plan_guru64_dft(rank.l,
		                     *dims.X#_iodim64,
		                     howmany_rank.l,
		                     *howmany_dims.X#_iodim64,
		                     *in.X#_complex, *out.X#_complex,
		                     sign.l, flags.l) ;return *X#_plan
		Declare.i X#_plan_guru64_split_dft(rank.l,
		                           *dims.X#_iodim64,
		                           howmany_rank.l,
		                           *howmany_dims.X#_iodim64,
		                           *ri.x#_R, *ii.x#_R, *ro.x#_R, *io.x#_R,
		                           flags.l) ;return void
		Declare X#_execute_dft(*p.X#_plan, *in.X#_complex, *out.X#_complex)
		Declare X#_execute_split_dft(*p.X#_plan, *ri.x#_R, *ii.x#_R,
		                     *ro.x#_R, *io.x#_R) ;return void
		
		;TODO: Hier fehlt noch was
		
		Declare X#_destroy_plan(*p.X#_plan) ;return void
		Declare X#_forget_wisdom() ;return void
		Declare X#_cleanup() ;return void
		
		Declare X#_set_timelimit(t.d) ;return void
		
		;TODO: Hier fehlt noch was
		
		Declare.i X#_malloc(n.size_t) ;return pointer
		Declare.i X#_alloc_real(n.size_t) ;return X#_R
		Declare.i X#_alloc_complex(n.size_t) ;return X#_complex
		
		Declare X#_free(*p)
		
		;TODO: Hier fehlt noch was
	EndMacro
	
	fftwDefineApi(fftw, d, fftw3)
	fftwDefineApi(fftwf, f, fftw3f)
	
	UndefineMacro fftwDefineApi
EndDeclareModule

Module FFTW3

	Macro DQ
		"
	EndMacro
	
	Macro fftwDefineApi(X, R, library)
	
		CompilerIf #PB_Compiler_OS = #PB_OS_Linux
			ImportC DQ#-l#library#DQ
		CompilerElse
			Import DQ#library#.lib#DQ
		CompilerEndIf  
			X#_execute(*p.X#_plan) ;return void
			X#_plan_dft.i(rank.l, *n.Long,
			              *in.X#_complex, *out.X#_complex, sign.l, flags.l) ;return *X#_plan
			X#_plan_dft_1d.i(n.l, *in.X#_complex, *out.X#_complex, sign.l,
			                 flags.l) ;return *X#_plan
			X#_plan_dft_2d.i(n0.l, n1.l,
			                 *in.X#_complex, *out.X#_complex, sign.l, flags.l) ;return *X#_plan
			X#_plan_dft_3d.i(n0.l, n1.l, n2.l,
			                 *in.X#_complex, *out.X#_complex, sign.l, flags.l) ;return *X#_plan
			X#_plan_many_dft.i(rank.l, *n.Long,
			                 howmany.l,
			                 *in.X#_complex, *inembed.Long,
			                 istride.l, idist.l,
			                 *out.X#_complex, *onembed.Long,
			                 ostride.l, odist.l,
			                 sign.l, flags.l) ;return *X#_plan
			X#_plan_guru_dft.i(rank.l, *dims.X#_iodim,
			                   howmany_rank.l,
			                   *howmany_dims.X#_iodim,
			                   *ri.x#_R, *ii.x#_R, *ro.x#_R, *io.x#_R,
			                   flags.l) ;return *X#_plan
			X#_plan_guru_split_sft.i(rank.l, *dims.X#_iodim,
			                         howmany_rank.l,
			                         *howmany_dims.X#_iodim,
			                         *ri.x#_R, *ii.x#_R, *ro.x#_R, *io.x#_R,
			                         flags.l) ;return *X#_plan
			X#_plan_guru64_dft.i(rank.l,
			                     *dims.X#_iodim64,
			                     howmany_rank.l,
			                     *howmany_dims.X#_iodim64,
			                     *in.X#_complex, *out.X#_complex,
			                     sign.l, flags.l) ;return *X#_plan
			X#_plan_guru64_split_dft.i(rank.l,
			                           *dims.X#_iodim64,
			                           howmany_rank.l,
			                           *howmany_dims.X#_iodim64,
			                           *ri.x#_R, *ii.x#_R, *ro.x#_R, *io.x#_R,
			                           flags.l) ;return void
			X#_execute_dft(*p.X#_plan, *in.X#_complex, *out.X#_complex)
			X#_execute_split_dft(*p.X#_plan, *ri.x#_R, *ii.x#_R,
			                     *ro.x#_R, *io.x#_R) ;return void
			
			;TODO: Hier fehlt noch was
			
			X#_destroy_plan(*p.X#_plan) ;return void
			X#_forget_wisdom() ;return void
			X#_cleanup() ;return void
			
			X#_set_timelimit(t.d) ;return void
			
			;TODO: Hier fehlt noch was
			
			X#_malloc.i(n.size_t) ;return pointer
			X#_alloc_real.i(n.size_t) ;return X#_R
			X#_alloc_complex.i(n.size_t) ;return X#_complex
			
			X#_free(*p)
			
			;TODO: Hier fehlt noch was
		EndImport
	
	EndMacro
	
	fftwDefineApi(fftw, d, fftw3)
	fftwDefineApi(fftwf, f, fftw3f)
	
	UndefineMacro fftwDefineApi
EndModule

EnableExplicit

CompilerIf #PB_Compiler_IsMainFile
	
	UseModule FFTW3
	
	Procedure.s StrD2(d.d)
		ProcedureReturn RSet(StrD(d, 5), 10, " ")
	EndProcedure
	Procedure.s StrC(*c.fftw_complex, divider.d = 1.0)
		Protected phase.d = ATan2(*c\d[0], *c\d[1])
		Protected length.d = Sqr(*c\d[0] * *c\d[0] + *c\d[1] * *c\d[1]) / divider
		ProcedureReturn StrD2(*c\d[0] / divider) + " + " + StrD2(*c\d[1] / divider) + "i (l=" + StrD2(length) + ", p=" + strd2(phase) + ")"
	EndProcedure

	OpenConsole()
	
	#n = 64
	
	Define.fftw_complex *in, *out
	Define.fftw_plan *plan
	
	*in = fftw_malloc(SizeOf(fftw_complex) * #n)
	*out = fftw_malloc(SizeOf(fftw_complex) * #n)
	
	Define time.i = ElapsedMilliseconds()
	*plan = fftw_plan_dft_1d(#n, *in, *out, #FFTW_FORWARD, #FFTW_MEASURE)
	time = ElapsedMilliseconds() - time
	PrintN("Plan: " + time + " ms")
	
	Define i.i, j.i
	For i = 0 To #n - 1
		*in\c[i]\d[0] = 0.0
		*in\c[i]\d[1] = 0.0
		For j = 1 To 1;#n / 2
			*in\c[i]\d[0] + Cos(1.1 * (j * i / #n + 0.0) * 2 * #PI)
			*in\c[i]\d[1] + 0;Cos((j * i / #n + 0.0) * 2 * #PI)
		Next
		PrintN(" in[" + RSet(Str(i), 5, " ") + "] = " + StrC(@*in\c[i]))
	Next
	
	time = ElapsedMilliseconds()
	fftw_execute(*plan)
	time = ElapsedMilliseconds() - time
	PrintN("Execute: " + time + " ms")

	For i = 0 To #n - 1
		PrintN("out[" + RSet(Str(i), 5, " ") + "] = " + StrC(@*out\c[i], #n))
	Next
	
	fftw_destroy_plan(*plan)
	fftw_free(*in)
	fftw_free(*out)
	
	PrintN("Press any key...")
	Input()
	
	CloseConsole()

CompilerEndIf
Da fehlen noch ein paar Imports, aber ich vermute, dass die selten verwandt werden. Ich hab auch zu wenig Zeit mich da in Helles Code einzuarbeiten um zu wissen, wo diese Funktionen von FFTW3 eingebaut werden müssten.
Bild
Benutzeravatar
Helle
Beiträge: 566
Registriert: 11.11.2004 16:13
Wohnort: Magdeburg

Re: Schnelle Integer-Multiplikation mit FFT

Beitrag von Helle »

Habe nochmal ein wenig mit FFT rumgewerkelt. Die ASM-Version ist 6-7-mal schneller (mit SSE bis AVX2) als obige PB-Version. Da ich aber keine Fremd-Bibliotheken verwenden will ist die ASM-Version natürlich auch nicht "genauer" als PB. Tests haben ergeben, das mit Doubles (64-Bit) sicher "nur" eine Faktoren-Länge von 2MB genau ist. Aber wir haben ja noch die FPU mit 80-Bit zur Verfügung! Tests damit haben ergeben, das selbst eine Faktoren-Länge von 128MB noch genau ist. 256MB konnte ich nicht testen wegen "out of memory"; und das mit 32GB RAM :mrgreen: ! FFT ist eben ein gewaltiger Speicherfresser.
Hier der Code für die 80-Bit-FPU-Version:

Code: Alles auswählen

;- Schnelle Integer-Multiplikation mit FFT für große Zahlen
;- Zur Erhöhung des Wertebereiches wird mit 80-Bit-FPU gerechnet
;- Getestet bis Faktoren-Länge 128M
;- Beide Faktoren müssen gleich groß sein und die Länge muss eine Zweier-Potenz sein. Padding bei Bedarf mit führenden Nullen oder Null-Memory
;- Berechnungs-Schema läuft ab nach Schmetterlingsgraph
;- Keine Fremd-Bibliotheken!
;- Windows7/64, PureBasic 5.41 LTS (x64)
;- "Helle" Klaus Helbing, 27.12.2015

;Als Quelle (Faktoren) dienen hier 2 Strings
Faktor1$ = "6582018229284824168619876730229402019930943462534319453394436096"  ;64-Bytes lang für Test
Faktor2$ = "5486721320215405789450123459785103469751240504554109782315460125"

For i = 1 To 10         ;für Test. Faktoren-Länge: 14=1M(2s); 15=2M(6s); 16=4M(14s); 17=8M(31s); 18=16M(68s); 19=32M(149s); 20=64M(320s); 21=128M(687s); 22=Out of Memory (32GB!)
  Faktor1$ + Faktor1$
  Faktor2$ + Faktor2$  
Next
;Hier könnte Test auf Länge Faktor1$/Faktor2$ usw. hin

LenF.q = Len(Faktor1$)
N.q = LenF << 1
Exponent.q
Mantisse.q
PF1.q
PF2.q
EW.q
Pointer0.q
Pointer1.q
Pointer2.q
Pointer_Produkt.q = N - 1
Pointer_Produkt1.q
RevPos.q
FakPos.q
QuadWert.q
QuadWert1.q
P0.q                         ;Hilfs-Pointer
P00.q
P1.q
P2.q
P3.q
P4.q
P5.q
P6.q
P7.q

ND.d

MXCSR.l                      ;für Rundungs.Kontrolle Double -> Quad-Integer
MXCSR_Sicher.l
WL.l

Ziffer.b

;2-Exponent von N ermitteln; 2^Exponent=N
ND = N 
QuadWert = PeekQ(@ND) & $7FFFFFFFFFFFFFFF   ;Vorzeichen-Bit ausblenden (Bit 63)
Exponent = QuadWert >> 52                   ;Exponent = Bits 52 bis 62 (11 Bits)  
Mantisse = QuadWert & $FFFFFFFFFFFFF        ;Mantisse = Bits 0 bis 51 (52 Bits)
If Mantisse = $FFFFFFFFFFFFF                ;also fast 1
  Mantisse = 0                              ;kann zu Null aufgerundet werden; ist ja Aufaddierung
  QuadWert1 = 1                             ;Exponent dafür um eins erhöhen    
EndIf
If QuadWert = 0  Or Mantisse <> 0 
  Exponent = 0                              ;Null
 Else 
  Exponent = Exponent + QuadWert1 - 1023    ;1023=Bias
EndIf  

If Exponent < 3
  MessageRequester("Fehler!", "Möglicherweise Faktoren zu klein!")
  End 
EndIf

;Speicher-Reservierung; 10=80Bit 
BufferSin = AllocateMemory((N * 10) + 128)                      ;128 muss
If BufferSin
  BufferSinA = BufferSin + (64 - (BufferSin & $3F))             ;64-er Alignment, klotzen, nicht kleckern!
 Else 
  MessageRequester("Fehler!", "Nicht genügend Speicher für BufferSin!")
  End
EndIf

BufferCos = AllocateMemory((N * 10) + 128)
If BufferCos
  BufferCosA = BufferCos + (64 - (BufferCos & $3F))             ;64-er Alignment
 Else 
  MessageRequester("Fehler!", "Nicht genügend Speicher für BufferCos!")
  End
EndIf

BufferFaktor1 = AllocateMemory((2 * N * 10) + 128)
If BufferFaktor1
  BufferFaktor1A = BufferFaktor1 + (64 - (BufferFaktor1 & $3F)) ;64-er Alignment
 Else 
  MessageRequester("Fehler!", "Nicht genügend Speicher für BufferFaktor1!")
  End
EndIf

BufferFaktor2 = AllocateMemory((2 * N * 10) + 128)
If BufferFaktor2
  BufferFaktor2A = BufferFaktor2 + (64 - (BufferFaktor2 & $3F)) ;64-er Alignment
 Else 
  MessageRequester("Fehler!", "Nicht genügend Speicher für BufferFaktor2!")
  End
EndIf

;========================================================================================
TA_Gesamt = ElapsedMilliseconds()

PF1 = @Faktor1$
PF2 = @Faktor2$

TA_Faktoren = ElapsedMilliseconds()
;Faktoren bearbeiten
;Zuerst die Ziffern gemäß reverse Verteilung neu setzen, dabei gleich in Long konvertieren
FakPos = 0
j = N >> 2
For i = (N / 4) - 1 To 0 Step -1
  RevPos = FakPos
  RevPos = ((RevPos >> 1) & $55555555) | ((RevPos & $55555555) << 1)
  RevPos = ((RevPos >> 2) & $33333333) | ((RevPos & $33333333) << 2)
  RevPos = ((RevPos >> 4) & $0F0F0F0F) | ((RevPos & $0F0F0F0F) << 4)
  RevPos = ((RevPos >> 8) & $00FF00FF) | ((RevPos & $00FF00FF) << 8)
  RevPos = ( RevPos >> 16            ) | ( RevPos              << 16)
  RevPos = ( RevPos >> (32 - Exponent)) & $FFFFFFFF

  RevPos << 1
  WL = PeekB(PF1 + i + j) & $F         ;statt -30h; sicherer bei anderer Quelle
  PokeL(BufferSinA + RevPos, WL)
  WL = PeekB(PF2 + i + j) & $F
  PokeL(BufferCosA + RevPos, WL)

  RevPos + 4
  WL = PeekB(PF1 + i) & $F
  PokeL(BufferSinA + RevPos, WL)
  WL = PeekB(PF2 + i) & $F
  PokeL(BufferCosA + RevPos, WL)

  FakPos + 1
Next

j = 0
!fninit  ;evtl.Ist-Zustand sichern und bei Programm-Ende zurück, aber hier nicht nötig
For i = 0 To 2 * N Step 4              ;Step 4 = Long
;  WL = PeekL(BufferSinA + i)
;  WR = WL
;  PokeD(BufferFaktor1A + j, WR)
;  PokeD(BufferFaktor1A + j + 16, WR)
P1 = BufferSinA + i
P2 = BufferFaktor1A + j
P3 = BufferFaktor1A + j + 20
!mov rax,[v_P1]
!fild dword[rax]
!fld st0                     ;st0 und st1
!mov rax,[v_P2]
!fstp tword[rax]
!mov rax,[v_P3]
!fstp tword[rax]

;  WL = PeekL(BufferCosA + i)
;  WR = WL
;  PokeD(BufferFaktor2A + j, WR)
;  PokeD(BufferFaktor2A + j + 16, WR)
P4 = BufferCosA + i
P5 = BufferFaktor2A + j
P6 = BufferFaktor2A + j + 20
!mov rax,[v_P4]
!fild dword[rax]
!fld st0                     ;st0 und st1
!mov rax,[v_P5]
!fstp tword[rax]
!mov rax,[v_P6]
!fstp tword[rax]

  j + 40
Next
TE_Faktoren = ElapsedMilliseconds() - TA_Faktoren

;Winkel erst hier, weil BufferSinA und BufferCosA oben "missbraucht" werden
TA_Winkel = ElapsedMilliseconds()
;Rad1 = Pi2 / N                         ;Konstante 
;Rad1 = Pi / LenF
!fldpi
!lea rax,[v_LenF]
!fild qword[rax]
!fdivp st1,st0
!lea rax,[Rad1]
!fstp tword[rax]

;SinCos; da dies lahm ist und sowieso beide Werte benötigt werden, wird nur bis Pi/4 (45°) ermittelt und der Rest (bis 180°) nur umkopiert
!mov [v_P00],0
;For k = 0 To (N >> 3)
!mov r8,[v_BufferSinA]
!mov r9,[v_BufferCosA]
!mov r10,10                            ;80Bit=10 Bytes
!lea r11,[Sin]
!lea r12,[Cos]
!mov rcx,[v_N]
!shr rcx,3
!@@:
  ;Rad = Rad1 * k                       ;nicht aufaddieren!     
  !lea rax,[Rad1]
  !fld tword[rax]
  !fild qword[v_P00]
  !fmulp st1,st0

  ;Si = Sin(Rad)
  ;Co = Cos(Rad)
  !fsincos
  !lea rax,[Cos]                       ;Variablen Cos und Sin nötig wegen 80-Bit. FST kann 80 nicht
  !fstp tword[rax]
  !lea rax,[Sin]
  !fstp tword[rax]

;  PokeD(BufferSinA + k * 8, Si)       ;0-45°
  !mov rax,[v_P00]
  !mul r10
  !mov r13,rax   ;k*8
  !add rax,r8
  !add r13,r9
  !fld tword[r11]
  !fstp tword[rax]                     ;Si 0-45°
  ;PokeD(BufferCosA + k * 8, Co)       ;0-45°
  !fld tword[r12]
  !fstp tword[r13]                     ;Co 0-45°

  ;PokeD(BufferCosA + ((N >> 2) - k) * 8, Si)    ;45-90°
  !mov rax,[v_LenF]
  !shr rax,1
  !sub rax,[v_P00]
  !mul r10
  !mov r13,rax
  !add rax,r9
  !add r13,r8
  !fld tword[r11]
  !fstp tword[rax]                     ;Co 45-90°

  ;PokeD(BufferSinA + ((N >> 2) - k) * 8, Co)    ;45-90°
  !fld tword[r12]
  !fstp tword[r13]                     ;Si 45-90°

  ;PokeD(BufferCosA + ((N >> 2) + k) * 8, -Si)   ;90-135°
  !mov rax,[v_LenF]
  !shr rax,1
  !add rax,[v_P00]
  !mul r10
  !mov r13,rax
  !add rax,r9
  !add r13,r8
  !fld tword[r11]
  !fchs
  !fstp tword[rax]                     ;Co 90-135°  

  ;PokeD(BufferSinA + ((N >> 2) + k) * 8, Co)    ;90-135°
  !fld tword[r12]
  !fstp tword[r13]                     ;Si 90-135°

  ;PokeD(BufferCosA + (LenF - k) * 8, -Co)   ;135-180°
  !mov rax,[v_LenF]
  !sub rax,[v_P00]
  !mul r10
  !mov r13,rax
  !add rax,r9
  !add r13,r8
  !fld tword[r12]
  !fchs
  !fstp tword[rax]                     ;Co 135-180°

  ;PokeD(BufferSinA + (LenF - k) * 8, Si)    ;135-180°
  !fld tword[r11]
  !fstp tword[r13]                     ;Si 135-180°
;Next
  !inc [v_P00]
  !dec rcx
!jns @b
TE_Winkel = ElapsedMilliseconds() - TA_Winkel
;==========================================================
;Faktor1
TA_FFT = ElapsedMilliseconds()
Pointer0 = 2
Pointer1 = 0
Pointer2 = Pointer0
While Pointer2 < N
  While Pointer2 < N
    For k = 1 To Pointer0
      EW = ((N / Pointer0 << 1) * Pointer1) * 10
P0 = Pointer2 * 20
P1 = BufferFaktor1A + P0
P2 = BufferCosA + EW
P3 = BufferFaktor1A + 10 + P0
P4 = BufferSinA + EW
;WR = (PeekD(BufferFaktor1A + 2*Pointer2 * 8) * PeekD(BufferCosA + EW) - PeekD(BufferFaktor1A+8 + 2*Pointer2 * 8) * PeekD(BufferSinA + EW))
;->WR = PeekD(P1) * PeekD(P2) - PeekD(P3) * PeekD(P4)
  !mov rax,[v_P1]
  !fld tword[rax]
  !mov rax,[v_P2]
  !fld tword[rax]
  !fmulp st1,st0
  !mov rax,[v_P3]
  !fld tword[rax]
  !mov rax,[v_P4]
  !fld tword[rax]
  !fmulp st1,st0
  !fsubp st1,st0
  !lea rax,[WR]
  !fstp tword[rax]

;WI = (PeekD(BufferFaktor1A+8 + 2*Pointer2 * 8) * PeekD(BufferCosA + EW) + PeekD(BufferFaktor1A + 2*Pointer2 * 8) * PeekD(BufferSinA + EW))
;->WI = PeekD(P3) * PeekD(P2) + PeekD(P1) * PeekD(P4)
  !mov rax,[v_P3]
  !fld tword[rax]
  !mov rax,[v_P2]
  !fld tword[rax]
  !fmulp st1,st0
  !mov rax,[v_P1]
  !fld tword[rax]
  !mov rax,[v_P4]
  !fld tword[rax]
  !fmulp st1,st0
  !faddp st1,st0
  !lea rax,[WI]
  !fstp tword[rax]

P5 = (Pointer2 - Pointer0) * 20
P6 = BufferFaktor1A + P5
;ZR = PeekD(BufferFaktor1A + 2*(Pointer2 - Pointer0) * 8)
;->ZR = PeekD(P6)
;PokeD(BufferFaktor1A + 2*(Pointer2 - Pointer0) * 8, ZR + WR)
;->PokeD(P6, ZR + WR)
  !mov rdx,[v_P6]
  !fld tword[rdx]
  !fld st0
  !lea rax,[ZR]
  !fstp tword[rax]

  !lea rax,[WR]
  !fld tword[rax]
  !faddp st1,st0
  !fstp tword[rdx]

P7 = P6 + 10
;ZI = PeekD(BufferFaktor1A+8 + 2*(Pointer2 - Pointer0) * 8)
;->ZI = PeekD(P7)
;PokeD(BufferFaktor1A+8 + 2*(Pointer2 - Pointer0) * 8, ZI + WI)
;->PokeD(P7, ZI + WI)
  !mov rdx,[v_P7]
  !fld tword[rdx]
  !fld st0
  !lea rax,[ZI]
  !fstp tword[rax]

  !lea rax,[WI]
  !fld tword[rax]
  !faddp st1,st0
  !fstp tword[rdx]

;PokeD(BufferFaktor1A + 2*Pointer2 * 8, ZR - WR)
;->PokeD(P1, ZR - WR)
  !lea rax,[ZR]
  !fld tword[rax]
  !lea rax,[WR]
  !fld tword[rax]
  !fsubp st1,st0
  !mov rdx,[v_P1]
  !fstp tword[rdx]

;PokeD(BufferFaktor1A+8 + 2*Pointer2 * 8, ZI - WI)
;->PokeD(P3, ZI - WI)
  !lea rax,[ZI]
  !fld tword[rax]
  !lea rax,[WI]
  !fld tword[rax]
  !fsubp st1,st0
  !mov rdx,[v_P3]
  !fstp tword[rdx]

      Pointer1 + 1
      Pointer2 + 1
    Next
    Pointer1 = 0
    Pointer2 + Pointer0
  Wend 
  Pointer0 << 1
  Pointer1 = 0
  Pointer2 = Pointer0
Wend

;==========================================================
;Faktor2
Pointer0 = 2
Pointer1 = 0
Pointer2 = Pointer0
While Pointer2 < N
  While Pointer2 < N
    For k = 1 To Pointer0
      EW = ((N / Pointer0 << 1) * Pointer1) * 10
P0 = Pointer2 * 20
P1 = BufferFaktor2A + P0
P2 = BufferCosA + EW
P3 = BufferFaktor2A + 10 + P0
P4 = BufferSinA + EW
;WR = (PeekD(BufferFaktor2A + 2*Pointer2 * 8) * PeekD(BufferCosA + EW) - PeekD(BufferFaktor2A+8 + 2*Pointer2 * 8) * PeekD(BufferSinA + EW))
;->WR = PeekD(P1) * PeekD(P2) - PeekD(P3) * PeekD(P4)
  !mov rax,[v_P1]
  !fld tword[rax]
  !mov rax,[v_P2]
  !fld tword[rax]
  !fmulp st1,st0
  !mov rax,[v_P3]
  !fld tword[rax]
  !mov rax,[v_P4]
  !fld tword[rax]
  !fmulp st1,st0
  !fsubp st1,st0
  !lea rax,[WR]
  !fstp tword[rax]

;WI = (PeekD(BufferFaktor2A+8 + 2*Pointer2 * 8) * PeekD(BufferCosA + EW) + PeekD(BufferFaktor2A + 2*Pointer2 * 8) * PeekD(BufferSinA + EW))
;->WI = PeekD(P3) * PeekD(P2) + PeekD(P1) * PeekD(P4)
  !mov rax,[v_P3]
  !fld tword[rax]
  !mov rax,[v_P2]
  !fld tword[rax]
  !fmulp st1,st0
  !mov rax,[v_P1]
  !fld tword[rax]
  !mov rax,[v_P4]
  !fld tword[rax]
  !fmulp st1,st0
  !faddp st1,st0
  !lea rax,[WI]
  !fstp tword[rax]

P5 = (Pointer2 - Pointer0) * 20
P6 = BufferFaktor2A + P5
;ZR = PeekD(BufferFaktor2A + 2*(Pointer2 - Pointer0) * 8)
;->ZR = PeekD(P6)
;PokeD(BufferFaktor2A + 2*(Pointer2 - Pointer0) * 8, ZR + WR)
;->PokeD(P6, ZR + WR)
  !mov rdx,[v_P6]
  !fld tword[rdx]
  !fld st0
  !lea rax,[ZR]
  !fstp tword[rax]

  !lea rax,[WR]
  !fld tword[rax]
  !faddp st1,st0
  !fstp tword[rdx]

P7 = P6 + 10
;ZI = PeekD(BufferFaktor2A+8 + 2*(Pointer2 - Pointer0) * 8)
;->ZI = PeekD(P7)
;PokeD(BufferFaktor2A+8 + 2*(Pointer2 - Pointer0) * 8, ZI + WI)
;->PokeD(P7, ZI + WI)
  !mov rdx,[v_P7]
  !fld tword[rdx]
  !fld st0
  !lea rax,[ZI]
  !fstp tword[rax]

  !lea rax,[WI]
  !fld tword[rax]
  !faddp st1,st0
  !fstp tword[rdx]

;PokeD(BufferFaktor2A + 2*Pointer2 * 8, ZR - WR)
;->PokeD(P1, ZR - WR)
  !lea rax,[ZR]
  !fld tword[rax]
  !lea rax,[WR]
  !fld tword[rax]
  !fsubp st1,st0
  !mov rdx,[v_P1]
  !fstp tword[rdx]

;PokeD(BufferFaktor2A+8 + 2*Pointer2 * 8, ZI - WI)
;->PokeD(P3, ZI - WI)
  !lea rax,[ZI]
  !fld tword[rax]
  !lea rax,[WI]
  !fld tword[rax]
  !fsubp st1,st0
  !mov rdx,[v_P3]
  !fstp tword[rdx]

      Pointer1 + 1
      Pointer2 + 1
    Next
    Pointer1 = 0
    Pointer2 + Pointer0
  Wend 
  Pointer0 << 1
  Pointer1 = 0
  Pointer2 = Pointer0
Wend
TE_FFT = ElapsedMilliseconds() - TA_FFT 

;==========================================================
;Und jetzt die beiden transformierten Vektoren miteinander multiplizieren
TA_Multi = ElapsedMilliseconds()
For k = 0 To N - 1

P0 = k * 20
P1 = BufferFaktor1A + P0
P2 = BufferFaktor2A + P0
P3 = BufferFaktor1A + 10 + P0
P4 = BufferFaktor2A + 10 + P0
;WR = (PeekD(BufferFaktor1A + 2*k * 8) * PeekD(BufferFaktor2A + 2*k * 8)) - (PeekD(BufferFaktor1A+8 + 2*k * 8) * PeekD(BufferFaktor2A+8 + 2*k * 8))    ;hier kann nicht direkt in BufferFaktor1RA geschrieben werden
;->WR = PeekD(P1) * PeekD(P2) - PeekD(P3) * PeekD(P4)
  !mov rax,[v_P1]
  !fld tword[rax]
  !mov rax,[v_P2]
  !fld tword[rax]
  !fmulp st1,st0
  !mov rax,[v_P3]
  !fld tword[rax]
  !mov rax,[v_P4]
  !fld tword[rax]
  !fmulp st1,st0
  !fsubp st1,st0
  !lea rax,[WR]
  !fstp tword[rax]

;PokeD(BufferFaktor1A+8 + 2*k * 8, (PeekD(BufferFaktor1A + 2*k * 8) * PeekD(BufferFaktor2A+8 + 2*k * 8)) + (PeekD(BufferFaktor1A+8 + 2*k * 8) * PeekD(BufferFaktor2A + 2*k * 8)))
;->PokeD(P3, PeekD(P1) * PeekD(P4) + PeekD(P3) * PeekD(P2))
  !mov rax,[v_P1]
  !fld tword[rax]
  !mov rax,[v_P4]
  !fld tword[rax]
  !fmulp st1,st0
  !mov rdx,[v_P3]
  !fld tword[rdx]
  !mov rax,[v_P2]
  !fld tword[rax]
  !fmulp st1,st0
  !faddp st1,st0
  !fstp tword[rdx]

;PokeD(BufferFaktor1A + 2*k * 8, WR)  ;weil vorher BufferFaktor1RA noch benötigt wird
;->PokeD(P1, WR)
  !lea rax,[WR]
  !fld tword[rax]
  !mov rdx,[v_P1]
  !fstp tword[rdx]

Next
TE_Multi = ElapsedMilliseconds() - TA_Multi 

;==========================================================
;Die Produkt-Werte wieder revers "verteilen"
TA_Rueck = ElapsedMilliseconds()
FakPos = 0
For i = 0 To (N/2) - 1 
  RevPos = FakPos
  RevPos = ((RevPos >> 1) & $55555555) | ((RevPos & $55555555) << 1)
  RevPos = ((RevPos >> 2) & $33333333) | ((RevPos & $33333333) << 2)
  RevPos = ((RevPos >> 4) & $0F0F0F0F) | ((RevPos & $0F0F0F0F) << 4)
  RevPos = ((RevPos >> 8) & $00FF00FF) | ((RevPos & $00FF00FF) << 8)
  RevPos = ( RevPos >> 16            ) | ( RevPos              << 16)
  RevPos = ( RevPos >> (32 - Exponent)) & $FFFFFFFF
  ;PokeD(BufferFaktor2A + 2*10 * RevPos, PeekD(BufferFaktor1A + 2*i * 10))
P1 = BufferFaktor2A + 2*10 * RevPos
P2 = BufferFaktor1A + 2*i * 10
  !mov rax,[v_P2]
  !fld tword[rax]
  !mov rdx,[v_P1]
  !fstp tword[rdx]

  ;PokeD(BufferFaktor2A+10 + 2*10 * RevPos, PeekD(BufferFaktor1A+10 + 2*i * 10))
P1 = BufferFaktor2A+10 + 2*10 * RevPos
P2 = BufferFaktor1A+10 + 2*i * 10
  !mov rax,[v_P2]
  !fld tword[rax]
  !mov rdx,[v_P1]
  !fstp tword[rdx]

  ;PokeD(BufferFaktor2A + 2*10 * (RevPos+1), PeekD(BufferFaktor1A + (2*i * 10) + N*10))
P1 = BufferFaktor2A + 2*10 * (RevPos+1)
P2 = BufferFaktor1A + (2*i * 10) + N*10
  !mov rax,[v_P2]
  !fld tword[rax]
  !mov rdx,[v_P1]
  !fstp tword[rdx]

  PokeD(BufferFaktor2A+10 + 2*10 * (RevPos+1), PeekD(BufferFaktor1A+10 + (2*i * 10) + N*10))
P1 = BufferFaktor2A+10 + 2*10 * (RevPos+1)
P2 = BufferFaktor1A+10 + (2*i * 10) + N*10
  !mov rax,[v_P2]
  !fld tword[rax]
  !mov rdx,[v_P1]
  !fstp tword[rdx]

  FakPos + 1
Next

FreeMemory(BufferFaktor1)

;Anfangswerte verteilen für FFT
For i = 0 To N - 2 Step 2
P0 = i * 20
P00 = (i + 1) * 20
P1 = BufferFaktor2A + P0
P2 = BufferFaktor2A + P00
P3 = BufferFaktor2A + 10 + P0
P4 = BufferFaktor2A + 10 + P00

;WR = PeekD(BufferFaktor2A + 2*i * 8) + PeekD(BufferFaktor2A + 2*(i + 1) * 8)
;->WR = PeekD(P1) + PeekD(P2)
  !mov rdx,[v_P1]
  !fld tword[rdx]
  !mov rax,[v_P2]
  !fld tword[rax]
  !faddp st1,st0
  !lea rdx,[WR]
  !fstp tword[rdx]

;WI = PeekD(BufferFaktor2A+8 + 2*i * 8) + PeekD(BufferFaktor2A+8 + 2*(i + 1) * 8)
;->WI = PeekD(P3) + PeekD(P4)
  !mov rax,[v_P3]
  !fld tword[rax]
  !mov rax,[v_P4]
  !fld tword[rax]
  !faddp st1,st0
  !lea rdx,[WI]
  !fstp tword[rdx]

;ZR = PeekD(BufferFaktor2A + 2*i * 8) - PeekD(BufferFaktor2A + 2*(i + 1) * 8)
;->ZR = PeekD(P1) - PeekD(P2)
  !mov rax,[v_P1]
  !fld tword[rax]
  !mov rax,[v_P2]
  !fld tword[rax]
  !fsubp st1,st0
  !fstp tword[rax]

;ZI = PeekD(BufferFaktor2A+8 + 2*i * 8) - PeekD(BufferFaktor2A+8 + 2*(i + 1) * 8)
;->ZI = PeekD(P3) - PeekD(P4)
  !mov rax,[v_P3]
  !fld tword[rax]
  !mov rax,[v_P4]
  !fld tword[rax]
  !fsubp st1,st0
  !fstp tword[rax]

;PokeD(BufferFaktor2A + 2*i * 8, WR)
  !lea rax,[WR]
  !fld tword[rax]
  !mov rdx,[v_P1]
  !fstp tword[rdx]

;PokeD(BufferFaktor2A+8 + 2*i * 8, WI)
  !lea rax,[WI]
  !fld tword[rax]
  !mov rdx,[v_P3]
  !fstp tword[rdx]

;PokeD(BufferFaktor2A + 2*(i + 1) * 8, ZR)
;PokeD(BufferFaktor2A+8 + 2*(i + 1) * 8, ZI)
Next

;==========================================================
;nochmal FFT, jetzt aber invers (mit neg.Sinus)
Pointer0 = 2
Pointer1 = 0
Pointer2 = Pointer0
While Pointer2 < N
  While Pointer2 < N
    For k = 1 To Pointer0
      EW = (N / Pointer0 << 1) * Pointer1 * 10
;      WR = (PeekD(BufferFaktor2A + 2*Pointer2 * 8) * PeekD(BufferCosA + EW) + PeekD(BufferFaktor2A+8 + 2*Pointer2 * 8) * PeekD(BufferSinA + EW))  ;neg.Sin, Operand geändert
;      WI = (PeekD(BufferFaktor2A+8 + 2*Pointer2 * 8) * PeekD(BufferCosA + EW) - PeekD(BufferFaktor2A + 2*Pointer2 * 8) * PeekD(BufferSinA + EW))  ;neg.Sin, Operand geändert
;      ZR = PeekD(BufferFaktor2A + 2*(Pointer2 - Pointer0) * 8)
;      ZI = PeekD(BufferFaktor2A+8 + 2*(Pointer2 - Pointer0) * 8)
;      PokeD(BufferFaktor2A + 2*(Pointer2 - Pointer0) * 8, ZR + WR)
;      PokeD(BufferFaktor2A+8 + 2*(Pointer2 - Pointer0) * 8, ZI + WI)
;      PokeD(BufferFaktor2A + 2*Pointer2 * 8, ZR - WR)
;      PokeD(BufferFaktor2A+8 + 2*Pointer2 * 8, ZI - WI)
P0 = Pointer2 * 20
P1 = BufferFaktor2A + P0
P2 = BufferCosA + EW
P3 = BufferFaktor2A + 10 + P0
P4 = BufferSinA + EW
;WR = (PeekD(BufferFaktor2A + 2*Pointer2 * 8) * PeekD(BufferCosA + EW) + PeekD(BufferFaktor2A+8 + 2*Pointer2 * 8) * PeekD(BufferSinA + EW))
;->WR = PeekD(P1) * PeekD(P2) + PeekD(P3) * PeekD(P4)
  !mov rax,[v_P1]
  !fld tword[rax]
  !mov rax,[v_P2]
  !fld tword[rax]
  !fmulp st1,st0
  !mov rax,[v_P3]
  !fld tword[rax]
  !mov rax,[v_P4]
  !fld tword[rax]
  !fmulp st1,st0
  !faddp st1,st0
  !lea rax,[WR]
  !fstp tword[rax]

;WI = (PeekD(BufferFaktor2A+8 + 2*Pointer2 * 8) * PeekD(BufferCosA + EW) - PeekD(BufferFaktor2A + 2*Pointer2 * 8) * PeekD(BufferSinA + EW))
;->WI = PeekD(P3) * PeekD(P2) - PeekD(P1) * PeekD(P4)
  !mov rax,[v_P3]
  !fld tword[rax]
  !mov rax,[v_P2]
  !fld tword[rax]
  !fmulp st1,st0
  !mov rax,[v_P1]
  !fld tword[rax]
  !mov rax,[v_P4]
  !fld tword[rax]
  !fmulp st1,st0
  !fsubp st1,st0
  !lea rax,[WI]
  !fstp tword[rax]

P5 = (Pointer2 - Pointer0) * 20
P6 = BufferFaktor2A + P5
;ZR = PeekD(BufferFaktor2A + 2*(Pointer2 - Pointer0) * 8)
;->ZR = PeekD(P6)
;PokeD(BufferFaktor2A + 2*(Pointer2 - Pointer0) * 8, ZR + WR)
;->PokeD(P6, ZR + WR)
  !mov rdx,[v_P6]
  !fld tword[rdx]
  !fld st0
  !lea rax,[ZR]
  !fstp tword[rax]

  !lea rax,[WR]
  !fld tword[rax]
  !faddp st1,st0
  !fstp tword[rdx]

P7 = P6 + 10
;ZI = PeekD(BufferFaktor2A+8 + 2*(Pointer2 - Pointer0) * 8)
;->ZI = PeekD(P7)
;PokeD(BufferFaktor2A+8 + 2*(Pointer2 - Pointer0) * 8, ZI + WI)
;->PokeD(P7, ZI + WI)
  !mov rdx,[v_P7]
  !fld tword[rdx]
  !fld st0
  !lea rax,[ZI]
  !fstp tword[rax]

  !lea rax,[WI]
  !fld tword[rax]
  !faddp st1,st0
  !fstp tword[rdx]

;PokeD(BufferFaktor2A + 2*Pointer2 * 8, ZR - WR)
;->PokeD(P1, ZR - WR)
  !lea rax,[ZR]
  !fld tword[rax]
  !lea rax,[WR]
  !fld tword[rax]
  !fsubp st1,st0
  !mov rdx,[v_P1]
  !fstp tword[rdx]

;PokeD(BufferFaktor2A+8 + 2*Pointer2 * 8, ZI - WI)
;->PokeD(P3, ZI - WI)
  !lea rax,[ZI]
  !fld tword[rax]
  !lea rax,[WI]
  !fld tword[rax]
  !fsubp st1,st0
  !mov rdx,[v_P3]
  !fstp tword[rdx]

      Pointer1 + 1
      Pointer2 + 1
    Next
    Pointer1 = 0
    Pointer2 + Pointer0
  Wend 
  Pointer0 << 1
  Pointer1 = 0
  Pointer2 = Pointer0
Wend
TE_Rueck = ElapsedMilliseconds() - TA_Rueck

FreeMemory(BufferCos)
FreeMemory(BufferSin)

TA_Ergebnis = ElapsedMilliseconds()
;die Doubles von FFT in Integer konvertieren und Koeffizienten aufaddieren mit Zehner-Versatz

BufferProdukt = AllocateMemory((N * 8) + 128)
If BufferProdukt
  BufferProduktA = BufferProdukt + (64 - (BufferProdukt & $3F)) + 64 ;64-er Alignment, +64 um Unterlauf zu vermeiden
 Else 
  MessageRequester("Fehler!", "Nicht genügend Speicher für BufferProdukt!")
  End
EndIf

BufferDez.q = AllocateMemory(32 + 128)
If BufferDez
  BufferDezA = BufferDez + (64 - (BufferDez & $3F))             ;64-er Alignment
 Else 
  MessageRequester("Fehler!", "Nicht genügend Speicher für BufferDez!")
  End 
EndIf

Restore Dezi                 ;sicher ist sicher!

!stmxcsr [v_MXCSR]           ;Control-und Status-Register (SSE) laden für Konvertierung Doubles in Quad-Integer. Aus Jux mit SSE2
MXCSR_Sicher = MXCSR         ;für Wiederherstellung
   ;Debug RSet(Bin(MXCSR), 32, "0")
!and [v_MXCSR],11111111111111111001111111111111b ;Bit 13 und 14 auf Null setzen (Round to Nearest)
!ldmxcsr [v_MXCSR]          ;speichern

For i = 0 To N - 1           ;vom Produkt alle Werte durch
  ;Division der Produktwerte durch N und Konvertierung Double in Integer
;  QuadWert = Round(PeekD(BufferFaktor2A + i*16), #PB_Round_Nearest)  ;Double zu Quad
!fninit                      ;hier nötig!!!
!mov rax,[v_i]
!mov r8,20
!mul r8
!mov r9,[v_BufferFaktor2A]

!fild [v_N]
!fld tword[r9+rax]
!fdiv st0,st1
;!fabs                       ;wohl nicht nötig  
!lea rdx,[WR]
!fstp qword[rdx]

;die Doubles in Quad-Integer konvertieren. Dies und damit die Rundungs-Kontrolle aus Jux mit SSE2 
;QuadWert = WR
!cvtsd2si rax,[rdx]
!mov [v_QuadWert],rax

 ;Hex2Dez
  QuadWert1 = QuadWert
  Ziffer = 0
  Pointer1 = 0
  Pointer2 = 0
  While PeekQ(?Dezi + Pointer1) <> 0
    While (QuadWert - PeekQ(?Dezi + Pointer1)) >= 0
      Ziffer + 1
      QuadWert - PeekQ(?Dezi + Pointer1)
      QuadWert1 = QuadWert
    Wend
    PokeB(BufferDezA + Pointer2, Ziffer)
    Pointer1 + 8
    Pointer2 + 1
    Ziffer = 0
    QuadWert = QuadWert1
  Wend
  PokeB(BufferDezA + Pointer2, QuadWert1)   ;letzte Dezimal-Ziffer abspeichern

  Pointer_Produkt1 = Pointer_Produkt 
  For k = 0 To 15
    Ziffer = PeekB(BufferProduktA + Pointer_Produkt1)
    Ziffer + PeekB(BufferDezA + Pointer2)
    PokeB(BufferProduktA + Pointer_Produkt1, Ziffer)
    If Ziffer > 9                           ;Übertrag
      Ziffer - 10
      PokeB(BufferProduktA + Pointer_Produkt1, Ziffer)
      Ziffer = PeekB(BufferProduktA + Pointer_Produkt1 - 1) + 1
      PokeB(BufferProduktA + Pointer_Produkt1 - 1, Ziffer)
    EndIf
    Pointer_Produkt1 - 1
    Pointer2 - 1
  Next

  Pointer_Produkt - 1
Next

!ldmxcsr [v_MXCSR_Sicher]   ;Control-und Status-Register alten Zustand wieder herstellen

;------------------------
;Ergebnis (Produkt) in ASCII-Ziffern abspeichern
For i = 0 To N Step 8
  PokeQ(BufferProduktA + i, PeekQ(BufferProduktA + i) | $3030303030303030)
Next
PokeB(BufferProduktA + i - 8, 0)                 ;Zero-Byte zur Sicherheit für String-Auslesen

FreeMemory(BufferFaktor2)
FreeMemory(BufferDez)

Produkt$ = LTrim(PeekS(BufferProduktA), "0")     ;evtl. führende Null(en) weg
FreeMemory(BufferProdukt)

SetClipboardText(Produkt$)        ;oder auskommentieren

TE_Ergebnis = ElapsedMilliseconds() - TA_Ergebnis

TE_Gesamt = ElapsedMilliseconds() - TA_Gesamt

Faktor$ = "Faktoren-Laenge: " + Str(Len(Faktor1$)) + " Bytes" + #CRLF$
Exponent$ = "Exponent 2^ für Faktoren-Laenge: " + Str(Exponent - 1) + #CRLF$
Winkel$ = "Zeit für Sinus/Cosinus-Berechnung: " + Str(TE_Winkel) + " ms" + #CRLF$
Faktoren$ = "Zeit für Faktoren-Aufbereitung: " + Str(TE_Faktoren) + " ms" + #CRLF$
FFT$ = "Zeit für Fast Fourier Transformation: " + Str(TE_FFT) + " ms" + #CRLF$
Multi$ = "Zeit für Multiplikation: " + Str(TE_Multi) + " ms" + #CRLF$
Rueck$ = "Zeit für Rueck-Transformation: " + Str(TE_Rueck) + " ms" + #CRLF$
Ergebnis$ = "Zeit für Ergebnis-Aufbereitung als String: " + Str(TE_Ergebnis) + " ms" + #CRLF$
Gesamt$ = "Zeit gesamt: " + Str(TE_Gesamt) + " ms" + #CRLF$
Info$ = Faktor$ + Exponent$ + Winkel$ + Faktoren$ + FFT$ + Multi$ + Rueck$ + Ergebnis$ + Gesamt$

If Len(Produkt$) > 65536 * 2      ;je nach Gusto und was Windows noch sicher anzeigt
  MessageRequester("Helles Multiplikation-FFT-Test", Info$)
 Else
  OpenWindow(0, 0, 0, 1000, 800, "Helles Multiplikation-FFT-Test", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
  StringGadget(0, 10, 10, 980, 780, Info$ + Produkt$, #PB_String_ReadOnly | #ES_MULTILINE | #ESB_DISABLE_LEFT | #ESB_DISABLE_RIGHT | #WS_VSCROLL)
  Repeat : Until WaitWindowEvent() = #PB_Event_CloseWindow
  CloseWindow(0)
EndIf

End

DataSection
  !Rad1    dt ?    ;10-Byte-Variablen
  !        dp ?    ;6 Bytes Dummy wegen Alignment
  !Sin     dt ?
  !        dp ?
  !Cos     dt ?
  !        dp ?
  !WR      dt ?
  !        dp ?
  !WI      dt ?
  !        dp ?
  !ZR      dt ?
  !        dp ?
  !ZI      dt ?
  !        dp ?
Dezi:
  Data.q 1000000000000000
  Data.q  100000000000000
  Data.q   10000000000000
  Data.q    1000000000000
  Data.q     100000000000
  Data.q      10000000000
  Data.q       1000000000
  Data.q        100000000
  Data.q         10000000
  Data.q          1000000
  Data.q           100000
  Data.q            10000
  Data.q             1000
  Data.q              100
  Data.q               10
  Data.q                0

EndDataSection
Viel Spaß!
Helle

Edit 29.12.2015: Speicherzuweisung BufferProduktA geändert
Zuletzt geändert von Helle am 29.12.2015 12:42, insgesamt 1-mal geändert.
Derren
Beiträge: 557
Registriert: 23.07.2011 02:08

Re: Schnelle Integer-Multiplikation mit FFT

Beitrag von Derren »

Was genau macht das?
Die großen Zahlen kann ich nicht überprüfen und bei kleinen Faktoren kommen trotzdem noch Millionen Milliarden als Ergebnis raus (oder nur chinesischens Geschreibsel, mit aktiviertem Unicode).
Sicher ist das Ding nicht dafür gedacht 10x15 zu rechnen, aber dass das Ergebnis 100 Stellen hat?

Ein, zwei Sätze zur Methodik wären für mich persönlich auch mal interessant. Lernen zwar in der Uni, wie dieses hin und her transformieren geht, aber was der Sinn davon ist, das sagt einem keiner. :?
Signatur und so
Benutzeravatar
Helle
Beiträge: 566
Registriert: 11.11.2004 16:13
Wohnort: Magdeburg

Re: Schnelle Integer-Multiplikation mit FFT

Beitrag von Helle »

Also, eine mathematisch fundierte Erklärung zur FFT maße ich mir nicht an. In Vorbereitung meines Codes habe ich mir bestimmt über 100 verschiedene Internet-Seiten angeschaut, von (für mich) unverständlich bis einfach nur Käse war da alles dabei. Ich habe mich dann auf eine Umsetzung der Abläufe des Schmetterlings-Graphen (s.Google) konzentriert. Das ist halbwegs verständlich und auch öfters im Internet zu finden (wenn auch hier Fehlerhaftes dabei ist).
Für Unicode habe ich jetzt eine Warnungs-Abfrage eingebaut; da ist aber jeder selbst für verantwortlich. Eine Behandlung kann man bei den Strings natürlich einbauen, aber das ist hier nicht das Thema.
Die Multiplikation kleiner Zahlen ist aber auch mit obigen Code möglich, sollte aber mit dem neuen Code (s.u.) noch einfacher sein (wenn auch mit Kanonen auf Spatzen geschossen wird).
Der neue Code hebt die Beschränkung auf Faktoren-Länge = 2-er Potenz auf. Wird jetzt (ohne Zusatz-Speicher!) bei der Faktoren-Verteilung (reverse Bits) erledigt.

Edit 31.12.2015: Nachfolgende Einschränkungen betr.Faktoren gelten nicht mehr!
[Zur Sicherheit gelten jetzt folgende Einschränkungen: Mindestens 1 Faktor muß mindestens 4 Byte (Ziffern) groß sein und beide Faktoren-Längen müssen gerade sein. Inwieweit dies tatsächlich notwendig ist muss ich mal nüchtern (also dieses Jahr nicht mehr :mrgreen: ) checken.]

Im neuen Code sind 4 Test-Szenarien auswählbar (Goto oder nicht als Überbrückung ):

Code: Alles auswählen

;- Schnelle Integer-Multiplikation mit FFT für große Zahlen
;- Bis auf die max.Faktoren-Länge keine Einschränkungen mehr!
;- Zur Erhöhung des Wertebereiches wird mit 80-Bit-FPU gerechnet
;- Getestet bis Faktoren-Länge 128MB mit CPU Intel i7-6700K @4.7GHz und RAM 32GB DDR4
;- Berechnungs-Schema läuft ab nach Schmetterlingsgraph
;- Keine Fremd-Bibliotheken!
;- Windows7/64, PureBasic 5.41 LTS (x64)
;- "Helle" Klaus Helbing, 31.12.2015

L1.q                         ;erstmal Länge Faktor1
L2.q
LNull1.q
LNull2.q
LNull11.q
LNull22.q
L1hinten.q
L1vorn.q
L2hinten.q
L2vorn.q
LenF.q                       ;Länge der angepassten Faktoren (2-er Potenz)
N.q                          ;entspr. Länge des Produkts (besser:2xLenF) 
PF1.q                        ;Pointer auf Faktor1 
PF2.q                        ;Pointer auf Faktor2
Exponent.q
Mantisse.q
EW.q
Pointer0.q
Pointer1.q
Pointer2.q
Pointer_Produkt.q
Pointer_Produkt1.q
RevPos.q
FakPos.q
QuadWert.q
QuadWert1.q
P0.q                         ;Hilfs-Pointer
P00.q
P1.q
P2.q
P3.q
P4.q
P5.q
P6.q
P7.q

MXCSR.l                      ;für Rundungs.Kontrolle Double -> Quad-Integer
MXCSR_Sicher.l
WL.l

Ziffer.b

If #PB_Compiler_Unicode      ;das jetzt noch für Unicode zu machen schenke ich mir
  MessageRequester("Achtung!", "Wenn Strings hier als Faktoren dienen darf kein Unicode verwendet werden!")
EndIf

;ein Auswahl-Menü schenke ich mir hier. Einen der Tests auswählen, Goto oder nicht Goto, das ist hier die Frage... .-)
;Quellen-Auswahl: (Faktoren)
;Goto NixLongString
Faktor1$ = "6582018229284824168619876730229402019930943462534319453394436096"  ;64-Bytes
Faktor2$ = "5486721320215405789450123459785103469751240504554109782315460125"
For i = 1 To 10         ;für Test. Faktoren-Länge: 14=1MB(2s); 15=2MB(6s); 16=4MB(13s); 17=8MB(30s); 18=16MB(66s); 19=32MB(143s); 20=64MB(307s); 21=128MB(656s); 22=Out of Memory (32GB!)
  Faktor1$ + Faktor1$
  Faktor2$ + Faktor2$  
Next
L1 = Len(Faktor1$)
L2 = Len(Faktor2$)
PF1 = @Faktor1$
PF2 = @Faktor2$
NixLongString:
;----------------------------------------------------------
Goto NixLongStringKrumm
;oder
Faktor1$ = "6730229402019930943462534319453394436096"
Faktor2$ = "9751240504554109782315460125"
For i = 1 To 10
  Faktor1$ + Faktor1$
  Faktor2$ + Faktor2$  
Next
L1 = Len(Faktor1$)
L2 = Len(Faktor2$)
PF1 = @Faktor1$
PF2 = @Faktor2$
NixLongStringKrumm:
;----------------------------------------------------------
Goto NixShortString
;oder
Faktor1$ = "10"
Faktor2$ = "15"
L1 = Len(Faktor1$)
L2 = Len(Faktor2$)
PF1 = @Faktor1$
PF2 = @Faktor2$
NixShortString:
;----------------------------------------------------------
Goto NixMem
;oder, wenn die Faktoren irgendwo im Speicher stehen, z.B.:
L1 = 6                       ;z.B.6 Ziffern
PF1 = AllocateMemory(L1)
For i = 0 To L1 - 1
  PokeB(PF1 + i, i + 1)      ;sollen die 6 Ziffern sein. Bei höher 9 natürlich andere Wertezuweisung (0-9)
Next
L2 = 4                       ;z.B.4 Ziffern
PF2 = AllocateMemory(L2)
For i = 0 To L2 - 1
  PokeB(PF2 + i, i + 1)      ;sollen die 4 Ziffern sein. Bei höher 9 natürlich andere Wertezuweisung (0-9)
Next
Nixmem:

;=========================================
LNull11 = L1                 ;echte Länge, auch hier Null-Test möglich
LNull22 = L2

If L1 < L2
  LNull1 = L1                ;hier als Zwischenspeicher
  L1 = L2                    ;größte Länge ermitteln; evtl. hier auf Null testen
  L2 = LNull1
EndIf

LenF = L1

!bsr rcx,[v_L1]              ;höchstes gesetztes Bit
!bsf rdx,[v_L2]              ;Test auf Länge Null
!jz @f
!mov [v_Exponent],rcx
!bsf rdx,[v_L1]              ;niedrigstes gesetztes Bit
!jmp Faktoren_OK
!@@:
MessageRequester("Fehler!", "Mindestens einer der Faktoren hat Länge Null!")
End

!Faktoren_OK:
!cmp rcx,rdx
!je IsZweier                 ;also nur 1 Bit (identisch) gesetzt
  !inc rcx
  !mov rax,1
  !shl rax,cl
  !mov [v_LenF],rax          ;LenF ist jetzt 2-er Potenz
  !add [v_Exponent],1
!IsZweier:
!add [v_Exponent],1

If Exponent > 28
  MessageRequester("Achtung!", "Diese Größenordnung konnte noch nicht getestet werden! Weiter ohne Garantie auf Richtigkeit des Ergebnisses!")
EndIf

While Exponent < 3           ;Produkt-Länge muss mindestens 8 sein, sonst an anderen Stellen Klimmzüge
  LenF << 1
  Exponent + 1
Wend

LNull1 = LenF - LNull11      ;LNull11=echte Länge. Bei LNull1 fängt hinterstes Byte an 
LNull2 = LenF - LNull22

L1hinten = LenF >> 1         ;LenF ist 2-er Potenz
If LNull11 < L1hinten
  L1hinten = LNull11
EndIf
L1vorn = LNull11 - L1hinten

L2hinten = LenF >> 1
If LNull22 < L2hinten
  L2hinten = LNull22
EndIf
L2vorn = LNull22 - L2hinten

N = LenF << 1
Pointer_Produkt = N - 1

;Speicher-Reservierung; 10=80Bit 
BufferSin = AllocateMemory((N * 10) + 128)
If BufferSin
  BufferSinA = BufferSin + (64 - (BufferSin & $3F))             ;64-er Alignment, klotzen, nicht kleckern!
 Else 
  MessageRequester("Fehler!", "Nicht genügend Speicher für BufferSin!")
  End
EndIf

BufferCos = AllocateMemory((N * 10) + 128)
If BufferCos
  BufferCosA = BufferCos + (64 - (BufferCos & $3F))             ;64-er Alignment
 Else 
  MessageRequester("Fehler!", "Nicht genügend Speicher für BufferCos!")
  End
EndIf

BufferFaktor1 = AllocateMemory((2 * N * 10) + 128)
If BufferFaktor1
  BufferFaktor1A = BufferFaktor1 + (64 - (BufferFaktor1 & $3F)) ;64-er Alignment
 Else 
  MessageRequester("Fehler!", "Nicht genügend Speicher für BufferFaktor1!")
  End
EndIf

BufferFaktor2 = AllocateMemory((2 * N * 10) + 128)
If BufferFaktor2
  BufferFaktor2A = BufferFaktor2 + (64 - (BufferFaktor2 & $3F)) ;64-er Alignment
 Else 
  MessageRequester("Fehler!", "Nicht genügend Speicher für BufferFaktor2!")
  End
EndIf

;========================================================================================
TA_Gesamt = ElapsedMilliseconds()
TA_Faktoren = ElapsedMilliseconds()
;Faktoren bearbeiten
;Zuerst die Ziffern gemäß reverse Verteilung neu setzen, dabei gleich in Long konvertieren
FakPos = 0
j = N >> 2

For i = (N / 4) - 1 To 0 Step -1
  RevPos = FakPos
  RevPos = ((RevPos >> 1) & $55555555) | ((RevPos & $55555555) << 1)
  RevPos = ((RevPos >> 2) & $33333333) | ((RevPos & $33333333) << 2)
  RevPos = ((RevPos >> 4) & $0F0F0F0F) | ((RevPos & $0F0F0F0F) << 4)
  RevPos = ((RevPos >> 8) & $00FF00FF) | ((RevPos & $00FF00FF) << 8)
  RevPos = ( RevPos >> 16            ) | ( RevPos              << 16)
  RevPos = ( RevPos >> (32 - Exponent)) & $FFFFFFFF
  RevPos << 1

  If L1hinten <= 0
    WL = 0
   Else
    WL = PeekB(PF1 + i + j - LNull1) & $F         ;statt -30h; sicherer bei anderer Quelle als String
    L1hinten - 1
  EndIf 
  PokeL(BufferSinA + RevPos, WL)

  If L2hinten <= 0
    WL = 0
   Else
    WL = PeekB(PF2 + i + j - LNull2) & $F
    L2hinten - 1
  EndIf
  PokeL(BufferCosA + RevPos, WL)

  RevPos + 4

  If L1vorn <= 0
    WL = 0
   Else
    WL = PeekB(PF1 + i - LNull1) & $F
   L1vorn - 1
  EndIf
  PokeL(BufferSinA + RevPos, WL)

  If L2vorn <= 0
    WL = 0
   Else
    WL = PeekB(PF2 + i - LNull2) & $F
   L2vorn - 1
  EndIf
  PokeL(BufferCosA + RevPos, WL)

  FakPos + 1
Next

j = 0
!fninit                                ;evtl.Ist-Zustand sichern und bei Programm-Ende zurück, aber hier nicht nötig
For i = 0 To 2 * N Step 4              ;Step 4 = Long
  ;WL = PeekL(BufferSinA + i)
  ;WR = WL
  ;PokeD(BufferFaktor1A + j, WR)
  ;PokeD(BufferFaktor1A + j + 16, WR)
  P1 = BufferSinA + i
  P2 = BufferFaktor1A + j
  P3 = BufferFaktor1A + j + 20
  !mov rax,[v_P1]
  !fild dword[rax]
  !fld st0                   ;st0 und st1
  !mov rax,[v_P2]
  !fstp tword[rax]
  !mov rax,[v_P3]
  !fstp tword[rax]

  ;WL = PeekL(BufferCosA + i)
  ;WR = WL
  ;PokeD(BufferFaktor2A + j, WR)
  ;PokeD(BufferFaktor2A + j + 16, WR)
  P4 = BufferCosA + i
  P5 = BufferFaktor2A + j
  P6 = BufferFaktor2A + j + 20
  !mov rax,[v_P4]
  !fild dword[rax]
  !fld st0                   ;st0 und st1
  !mov rax,[v_P5]
  !fstp tword[rax]
  !mov rax,[v_P6]
  !fstp tword[rax]

  j + 40
Next
TE_Faktoren = ElapsedMilliseconds() - TA_Faktoren

;Winkel erst hier, weil BufferSinA und BufferCosA oben "missbraucht" werden
TA_Winkel = ElapsedMilliseconds()
;Rad1 = Pi / LenF
!fldpi
!fild qword[v_LenF]
!fdivp st1,st0
!fstp tword[Rad1]

;SinCos; da dies lahm ist und sowieso beide Werte benötigt werden, wird nur bis Pi/4 (45°) ermittelt und der Rest (bis 180°) nur umkopiert
!mov [v_P00],0
;For k = 0 To (N >> 3)
!mov r8,[v_BufferSinA]
!mov r9,[v_BufferCosA]
!mov r10,10                            ;80Bit=10 Bytes
!lea r11,[Sin]
!lea r12,[Cos]
!mov rcx,[v_N]
!shr rcx,3
!@@:
  ;Rad = Rad1 * k                       ;nicht aufaddieren!     
  !fld tword[Rad1]
  !fild qword[v_P00]
  !fmulp st1,st0

  ;Si = Sin(Rad)
  ;Co = Cos(Rad)
  !fsincos
  !fstp tword[Cos]                     ;Variablen Cos und Sin nötig wegen 80-Bit. FST kann 80 nicht
  !fstp tword[Sin]

  ;PokeD(BufferSinA + k * 8, Si)       ;0-45°
  !mov rax,[v_P00]
  !mul r10
  !mov r13,rax   ;k * 8
  !add rax,r8
  !add r13,r9
  !fld tword[r11]
  !fstp tword[rax]                     ;Sin 0-45°
  ;PokeD(BufferCosA + k * 8, Co)       ;0-45°
  !fld tword[r12]
  !fstp tword[r13]                     ;Cos 0-45°

  ;PokeD(BufferCosA + ((N >> 2) - k) * 8, Si)    ;45-90°
  !mov rax,[v_LenF]
  !shr rax,1
  !sub rax,[v_P00]
  !mul r10
  !mov r13,rax
  !add rax,r9
  !add r13,r8
  !fld tword[r11]
  !fstp tword[rax]                     ;Cos 45-90°

  ;PokeD(BufferSinA + ((N >> 2) - k) * 8, Co)    ;45-90°
  !fld tword[r12]
  !fstp tword[r13]                     ;Sin 45-90°

  ;PokeD(BufferCosA + ((N >> 2) + k) * 8, -Si)   ;90-135°
  !mov rax,[v_LenF]
  !shr rax,1
  !add rax,[v_P00]
  !mul r10
  !mov r13,rax
  !add rax,r9
  !add r13,r8
  !fld tword[r11]
  !fchs
  !fstp tword[rax]                     ;Cos 90-135°  

  ;PokeD(BufferSinA + ((N >> 2) + k) * 8, Co)    ;90-135°
  !fld tword[r12]
  !fstp tword[r13]                     ;Sin 90-135°

  ;PokeD(BufferCosA + (LenF - k) * 8, -Co)   ;135-180°
  !mov rax,[v_LenF]
  !sub rax,[v_P00]
  !mul r10
  !mov r13,rax
  !add rax,r9
  !add r13,r8
  !fld tword[r12]
  !fchs
  !fstp tword[rax]                     ;Cos 135-180°

  ;PokeD(BufferSinA + (LenF - k) * 8, Si)    ;135-180°
  !fld tword[r11]
  !fstp tword[r13]                     ;Sin 135-180°
;Next
  !inc [v_P00]
  !dec rcx
!jns @b
TE_Winkel = ElapsedMilliseconds() - TA_Winkel
;==========================================================
;Faktor1
TA_FFT = ElapsedMilliseconds()
Pointer0 = 2
Pointer1 = 0
Pointer2 = Pointer0
While Pointer2 < N
  While Pointer2 < N
    For k = 1 To Pointer0
      EW = ((N / Pointer0 << 1) * Pointer1) * 10
      P0 = Pointer2 * 20
      P1 = BufferFaktor1A + P0
      P2 = BufferCosA + EW
      P3 = BufferFaktor1A + 10 + P0
      P4 = BufferSinA + EW
      ;WR = (PeekD(BufferFaktor1A + 2 * Pointer2 * 8) * PeekD(BufferCosA + EW) - PeekD(BufferFaktor1A + 8 + 2*Pointer2 * 8) * PeekD(BufferSinA + EW))
      ;->WR = PeekD(P1) * PeekD(P2) - PeekD(P3) * PeekD(P4)
      ;P5 = (Pointer2 - Pointer0) * 20  
      ;P6 = BufferFaktor1A + P5
      !mov rax,[v_Pointer2]
      !sub rax,[v_Pointer0]
      !mov rcx,20                 ;imul will ich nicht
      !mul rcx
      !add rax,[v_BufferFaktor1A] ;=P6

      !mov rcx,[v_P1]
      !mov rdx,[v_P2]
      !mov r8,[v_P3]
      !mov r9,[v_P4]

      !fld tword[rcx]
      !fld tword[rdx]
      !fmulp st1,st0
      !fld tword[r8]
      !fld tword[r9]
      !fmulp st1,st0
      !fsubp st1,st0
      !fstp tword[WR]

      ;WI = (PeekD(BufferFaktor1A + 8 + 2 * Pointer2 * 8) * PeekD(BufferCosA + EW) + PeekD(BufferFaktor1A + 2 * Pointer2 * 8) * PeekD(BufferSinA + EW))
      ;->WI = PeekD(P3) * PeekD(P2) + PeekD(P1) * PeekD(P4)
      !fld tword[r8]
      !fld tword[rdx]
      !fmulp st1,st0
      !fld tword[rcx]
      !fld tword[r9]
      !fmulp st1,st0
      !faddp st1,st0
      !fstp tword[WI]

      ;ZR = PeekD(BufferFaktor1A + 2 * (Pointer2 - Pointer0) * 8)
      ;->ZR = PeekD(P6)
      ;PokeD(BufferFaktor1A + 2 * (Pointer2 - Pointer0) * 8, ZR + WR)
      ;->PokeD(P6, ZR + WR)
      !fld tword[rax]
      !fld st0
      !fstp tword[ZR]

      !fld tword[WR]
      !faddp st1,st0
      !fstp tword[rax]

      ;P7 = P6 + 10          ;P6=rax
      !add rax,10            ;=P7

      ;ZI = PeekD(BufferFaktor1A + 8 + 2 * (Pointer2 - Pointer0) * 8)
      ;->ZI = PeekD(P7)
      ;PokeD(BufferFaktor1A + 8 + 2 * (Pointer2 - Pointer0) * 8, ZI + WI)
      ;->PokeD(P7, ZI + WI)
      !fld tword[rax]
      !fld st0
      !fstp tword[ZI]

      !fld tword[WI]
      !faddp st1,st0
      !fstp tword[rax]

      ;PokeD(BufferFaktor1A + 2 * Pointer2 * 8, ZR - WR)
      ;->PokeD(P1, ZR - WR)
      !fld tword[ZR]
      !fld tword[WR]
      !fsubp st1,st0
      !fstp tword[rcx]

      ;PokeD(BufferFaktor1A + 8 + 2 * Pointer2 * 8, ZI - WI)
      ;->PokeD(P3, ZI - WI)
      !fld tword[ZI]
      !fld tword[WI]
      !fsubp st1,st0
      !fstp tword[r8]

      Pointer1 + 1
      Pointer2 + 1
    Next
    Pointer1 = 0
    Pointer2 + Pointer0
  Wend 
  Pointer0 << 1
  Pointer1 = 0
  Pointer2 = Pointer0
Wend

;==========================================================
;Faktor2
Pointer0 = 2
Pointer1 = 0
Pointer2 = Pointer0
While Pointer2 < N
  While Pointer2 < N
    For k = 1 To Pointer0
      EW = ((N / Pointer0 << 1) * Pointer1) * 10
      P0 = Pointer2 * 20
      P1 = BufferFaktor2A + P0
      P2 = BufferCosA + EW
      P3 = BufferFaktor2A + 10 + P0
      P4 = BufferSinA + EW
      ;WR = (PeekD(BufferFaktor2A + 2 * Pointer2 * 8) * PeekD(BufferCosA + EW) - PeekD(BufferFaktor2A + 8 + 2 * Pointer2 * 8) * PeekD(BufferSinA + EW))
      ;->WR = PeekD(P1) * PeekD(P2) - PeekD(P3) * PeekD(P4)
      ;P5 = (Pointer2 - Pointer0) * 20  
      ;P6 = BufferFaktor2A + P5
      !mov rax,[v_Pointer2]
      !sub rax,[v_Pointer0]
      !mov rcx,20                 ;imul will ich nicht
      !mul rcx
      !add rax,[v_BufferFaktor2A] ;=P6

      !mov rcx,[v_P1]
      !mov rdx,[v_P2]
      !mov r8,[v_P3]
      !mov r9,[v_P4]

      !fld tword[rcx]
      !fld tword[rdx]
      !fmulp st1,st0
      !fld tword[r8]
      !fld tword[r9]
      !fmulp st1,st0
      !fsubp st1,st0
      !fstp tword[WR]

      ;WI = (PeekD(BufferFaktor2A + 8 + 2 * Pointer2 * 8) * PeekD(BufferCosA + EW) + PeekD(BufferFaktor2A + 2 * Pointer2 * 8) * PeekD(BufferSinA + EW))
      ;->WI = PeekD(P3) * PeekD(P2) + PeekD(P1) * PeekD(P4)
      !fld tword[r8]
      !fld tword[rdx]
      !fmulp st1,st0
      !fld tword[rcx]
      !fld tword[r9]
      !fmulp st1,st0
      !faddp st1,st0
      !fstp tword[WI]

      ;ZR = PeekD(BufferFaktor2A + 2 * (Pointer2 - Pointer0) * 8)
      ;->ZR = PeekD(P6)
      ;PokeD(BufferFaktor2A + 2 * (Pointer2 - Pointer0) * 8, ZR + WR)
      ;->PokeD(P6, ZR + WR)
      !fld tword[rax]
      !fld st0
      !fstp tword[ZR]

      !fld tword[WR]
      !faddp st1,st0
      !fstp tword[rax]

      ;P7 = P6 + 10          ;P6=rax 
      !add rax,10            ;=P7
      ;ZI = PeekD(BufferFaktor2A + 8 + 2 * (Pointer2 - Pointer0) * 8)
      ;->ZI = PeekD(P7)
      ;PokeD(BufferFaktor2A + 8 + 2 * (Pointer2 - Pointer0) * 8, ZI + WI)
      ;->PokeD(P7, ZI + WI)
      !fld tword[rax]
      !fld st0
      !fstp tword[ZI]

      !fld tword[WI]
      !faddp st1,st0
      !fstp tword[rax]

      ;PokeD(BufferFaktor2A + 2 * Pointer2 * 8, ZR - WR)
      ;->PokeD(P1, ZR - WR)
      !fld tword[ZR]
      !fld tword[WR]
      !fsubp st1,st0
      !fstp tword[rcx]

      ;PokeD(BufferFaktor2A + 8 + 2 * Pointer2 * 8, ZI - WI)
      ;->PokeD(P3, ZI - WI)
      !fld tword[ZI]
      !fld tword[WI]
      !fsubp st1,st0
      !fstp tword[r8]

      Pointer1 + 1
      Pointer2 + 1
    Next
    Pointer1 = 0
    Pointer2 + Pointer0
  Wend 
  Pointer0 << 1
  Pointer1 = 0
  Pointer2 = Pointer0
Wend
TE_FFT = ElapsedMilliseconds() - TA_FFT 

;==========================================================
;Und jetzt die beiden transformierten Vektoren miteinander multiplizieren
TA_Multi = ElapsedMilliseconds()
For k = 0 To N - 1
  P0 = k * 20
  P1 = BufferFaktor1A + P0
  P2 = BufferFaktor2A + P0
  P3 = BufferFaktor1A + 10 + P0
  P4 = BufferFaktor2A + 10 + P0
  ;WR = (PeekD(BufferFaktor1A + 2 * k * 8) * PeekD(BufferFaktor2A + 2 * k * 8)) - (PeekD(BufferFaktor1A + 8 + 2 * k * 8) * PeekD(BufferFaktor2A + 8 + 2 * k * 8))    ;hier kann nicht direkt in BufferFaktor1RA geschrieben werden
  ;->WR = PeekD(P1) * PeekD(P2) - PeekD(P3) * PeekD(P4)
  !mov rcx,[v_P1]
  !mov rdx,[v_P2]
  !mov r8,[v_P3]
  !mov r9,[v_P4]

  !fld tword[rcx]
  !fld tword[rdx]
  !fmulp st1,st0
  !fld tword[r8]
  !fld tword[r9]
  !fmulp st1,st0
  !fsubp st1,st0
  !fstp tword[WR]

  ;PokeD(BufferFaktor1A + 8 + 2 * k * 8, (PeekD(BufferFaktor1A + 2 * k * 8) * PeekD(BufferFaktor2A + 8 + 2 * k * 8)) + (PeekD(BufferFaktor1A + 8 + 2 * k * 8) * PeekD(BufferFaktor2A + 2 * k * 8)))
  ;->PokeD(P3, PeekD(P1) * PeekD(P4) + PeekD(P3) * PeekD(P2))
  !fld tword[rcx]
  !fld tword[r9]
  !fmulp st1,st0
  !fld tword[r8]
  !fld tword[rdx]
  !fmulp st1,st0
  !faddp st1,st0
  !fstp tword[r8]

  ;PokeD(BufferFaktor1A + 2 * k * 8, WR)  ;weil vorher BufferFaktor1RA noch benötigt wird
  ;->PokeD(P1, WR)
  !fld tword[WR]
  !mov rdx,[v_P1]
  !fstp tword[rdx]
Next
TE_Multi = ElapsedMilliseconds() - TA_Multi 

;==========================================================
;Die Produkt-Werte wieder revers "verteilen"
TA_Rueck = ElapsedMilliseconds()
FakPos = 0
For i = 0 To (N / 2) - 1 
  RevPos = FakPos
  RevPos = ((RevPos >> 1) & $55555555) | ((RevPos & $55555555) << 1)
  RevPos = ((RevPos >> 2) & $33333333) | ((RevPos & $33333333) << 2)
  RevPos = ((RevPos >> 4) & $0F0F0F0F) | ((RevPos & $0F0F0F0F) << 4)
  RevPos = ((RevPos >> 8) & $00FF00FF) | ((RevPos & $00FF00FF) << 8)
  RevPos = ( RevPos >> 16            ) | ( RevPos              << 16)
  RevPos = ( RevPos >> (32 - Exponent)) & $FFFFFFFF
  ;PokeD(BufferFaktor2A + 2 * 8 * RevPos, PeekD(BufferFaktor1A + 2 * i * 8))
  P1 = BufferFaktor2A + 2 * 10 * RevPos
  P2 = BufferFaktor1A + 2 * i * 10
  !mov rcx,[v_P1]
  !mov rdx,[v_P2]

  !fld tword[rdx]
  !fstp tword[rcx]

  ;PokeD(BufferFaktor2A + 8 + 2 * 8 * RevPos, PeekD(BufferFaktor1A + 8 + 2 * i * 8))
  ;P1 = P1 + 10       ;BufferFaktor2A + 10 + 2 * 10 * RevPos
  !add rcx,10
  ;P2 = P2 + 10       ;BufferFaktor1A + 10 + 2 * i * 10
  !add rdx,10  
  !fld tword[rdx]
  !fstp tword[rcx]

  ;PokeD(BufferFaktor2A + 2 * 8 * (RevPos + 1), PeekD(BufferFaktor1A + (2 * i * 8) + N * 8))
  P1 = BufferFaktor2A + 2 * 10 * (RevPos + 1)
  P2 = BufferFaktor1A + (2 * i * 10) + N * 10
  !mov rcx,[v_P1]
  !mov rdx,[v_P2]

  !fld tword[rdx]
  !fstp tword[rcx]

  ;PokeD(BufferFaktor2A + 8 + 2 * 8 * (RevPos + 1), PeekD(BufferFaktor1A + 8 + (2 * i * 8) + N * 8))
  ;P1 = P1 + 10       ;BufferFaktor2A + 10 + 2 * 10 * (RevPos + 1)
  !add rcx,10
  ;P2 = P2 + 10       ;BufferFaktor1A + 10 + (2 * i * 10) + N * 10
  !add rdx,10 
  !fld tword[rdx]
  !fstp tword[rcx]

  FakPos + 1
Next

FreeMemory(BufferFaktor1)

;Anfangswerte verteilen für FFT
For i = 0 To N - 2 Step 2
  P0 = i * 20
  P00 = (i + 1) * 20
  P1 = BufferFaktor2A + P0
  P2 = BufferFaktor2A + P00
  P3 = BufferFaktor2A + 10 + P0
  P4 = BufferFaktor2A + 10 + P00

  !mov rcx,[v_P1]
  !mov rdx,[v_P2]
  !mov r8,[v_P3]
  !mov r9,[v_P4]

  ;WR = PeekD(BufferFaktor2A + 2 * i * 8) + PeekD(BufferFaktor2A + 2 * (i + 1) * 8)
  ;->WR = PeekD(P1) + PeekD(P2)
  !fld tword[rcx]
  !fld tword[rdx]
  !faddp st1,st0
  !fstp tword[WR]

  ;WI = PeekD(BufferFaktor2A + 8 + 2 * i * 8) + PeekD(BufferFaktor2A + 8 + 2 * (i + 1) * 8)
  ;->WI = PeekD(P3) + PeekD(P4)
  !fld tword[r8]
  !fld tword[r9]
  !faddp st1,st0
  !fstp tword[WI]

  ;ZR = PeekD(BufferFaktor2A + 2 * i * 8) - PeekD(BufferFaktor2A + 2 * (i + 1) * 8)
  ;->ZR = PeekD(P1) - PeekD(P2)
  !fld tword[rcx]
  !fld tword[rdx]
  !fsubp st1,st0
  ;PokeD(BufferFaktor2A + 2 * (i + 1) * 8, ZR)
  !fstp tword[rdx]

  ;ZI = PeekD(BufferFaktor2A + 8 + 2 * i * 8) - PeekD(BufferFaktor2A + 8 + 2 * (i + 1) * 8)
  ;->ZI = PeekD(P3) - PeekD(P4)
  !fld tword[r8]
  !fld tword[r9]
  !fsubp st1,st0
  ;PokeD(BufferFaktor2A + 8 + 2 * (i + 1) * 8, ZI)
  !fstp tword[r9]

  ;PokeD(BufferFaktor2A + 2 * i * 8, WR)
  !fld tword[WR]
  !fstp tword[rcx]

  ;PokeD(BufferFaktor2A + 8 + 2 * i * 8, WI)
  !fld tword[WI]
  !fstp tword[r8]
Next

;==========================================================
;nochmal FFT, jetzt aber invers (mit neg.Sinus)
Pointer0 = 2
Pointer1 = 0
Pointer2 = Pointer0
While Pointer2 < N
  While Pointer2 < N
    For k = 1 To Pointer0
      EW = (N / Pointer0 << 1) * Pointer1 * 10
      ;WR = (PeekD(BufferFaktor2A + 2 * Pointer2 * 8) * PeekD(BufferCosA + EW) + PeekD(BufferFaktor2A + 8 + 2 * Pointer2 * 8) * PeekD(BufferSinA + EW))  ;neg.Sin, Operand geändert
      ;WI = (PeekD(BufferFaktor2A + 8 + 2 * Pointer2 * 8) * PeekD(BufferCosA + EW) - PeekD(BufferFaktor2A + 2 * Pointer2 * 8) * PeekD(BufferSinA + EW))  ;neg.Sin, Operand geändert
      ;ZR = PeekD(BufferFaktor2A + 2 * (Pointer2 - Pointer0) * 8)
      ;ZI = PeekD(BufferFaktor2A + 8 + 2 * (Pointer2 - Pointer0) * 8)
      ;PokeD(BufferFaktor2A + 2 * (Pointer2 - Pointer0) * 8, ZR + WR)
      ;PokeD(BufferFaktor2A + 8 + 2 * (Pointer2 - Pointer0) * 8, ZI + WI)
      ;PokeD(BufferFaktor2A + 2 * Pointer2 * 8, ZR - WR)
      ;PokeD(BufferFaktor2A + 8 + 2 * Pointer2 * 8, ZI - WI)
      P0 = Pointer2 * 20
      P1 = BufferFaktor2A + P0
      P2 = BufferCosA + EW
      P3 = BufferFaktor2A + 10 + P0
      P4 = BufferSinA + EW
      ;WR = (PeekD(BufferFaktor2A + 2 * Pointer2 * 8) * PeekD(BufferCosA + EW) + PeekD(BufferFaktor2A + 8 + 2 * Pointer2 * 8) * PeekD(BufferSinA + EW))
      ;->WR = PeekD(P1) * PeekD(P2) + PeekD(P3) * PeekD(P4)
      ;P5 = (Pointer2 - Pointer0) * 20  
      ;P6 = BufferFaktor2A + P5
      !mov rax,[v_Pointer2]
      !sub rax,[v_Pointer0]
      !mov rcx,20                 ;imul will ich nicht
      !mul rcx
      !add rax,[v_BufferFaktor2A] ;=P6

      !mov rcx,[v_P1]
      !mov rdx,[v_P2]
      !mov r8,[v_P3]
      !mov r9,[v_P4]

      !fld tword[rcx]
      !fld tword[rdx]
      !fmulp st1,st0
      !fld tword[r8]
      !fld tword[r9]
      !fmulp st1,st0
      !faddp st1,st0
      !fstp tword[WR]

      ;WI = (PeekD(BufferFaktor2A + 8 + 2 * Pointer2 * 8) * PeekD(BufferCosA + EW) - PeekD(BufferFaktor2A + 2 * Pointer2 * 8) * PeekD(BufferSinA + EW))
      ;->WI = PeekD(P3) * PeekD(P2) - PeekD(P1) * PeekD(P4)
      !fld tword[r8]
      !fld tword[rdx]
      !fmulp st1,st0
      !fld tword[rcx]
      !fld tword[r9]
      !fmulp st1,st0
      !fsubp st1,st0
      !fstp tword[WI]

      ;ZR = PeekD(BufferFaktor2A + 2 * (Pointer2 - Pointer0) * 8)
      ;->ZR = PeekD(P6)
      ;PokeD(BufferFaktor2A + 2 * (Pointer2 - Pointer0) * 8, ZR + WR)
      ;->PokeD(P6, ZR + WR)
      !fld tword[rax]
      !fld st0
      !fstp tword[ZR]

      !fld tword[WR]
      !faddp st1,st0
      !fstp tword[rax] 

      ;P7 = P6 + 10
      !add rax,10            ;=P7              
      ;ZI = PeekD(BufferFaktor2A + 8 + 2 * (Pointer2 - Pointer0) * 8)
      ;->ZI = PeekD(P7)
      ;PokeD(BufferFaktor2A + 8 + 2 * (Pointer2 - Pointer0) * 8, ZI + WI)
      ;->PokeD(P7, ZI + WI)
      !fld tword[rax]
      !fld st0
      !fstp tword[ZI]

      !fld tword[WI]
      !faddp st1,st0
      !fstp tword[rax]

      ;PokeD(BufferFaktor2A + 2 * Pointer2 * 8, ZR - WR)
      ;->PokeD(P1, ZR - WR)
      !fld tword[ZR]
      !fld tword[WR]
      !fsubp st1,st0
      !fstp tword[rcx]

      ;PokeD(BufferFaktor2A + 8 + 2 * Pointer2 * 8, ZI - WI)
      ;->PokeD(P3, ZI - WI)
      !fld tword[ZI]
      !fld tword[WI]
      !fsubp st1,st0
      !fstp tword[r8]

      Pointer1 + 1
      Pointer2 + 1
    Next
    Pointer1 = 0
    Pointer2 + Pointer0
  Wend 
  Pointer0 << 1
  Pointer1 = 0
  Pointer2 = Pointer0
Wend
TE_Rueck = ElapsedMilliseconds() - TA_Rueck

FreeMemory(BufferCos)
FreeMemory(BufferSin)

TA_Ergebnis = ElapsedMilliseconds()
;die Doubles von FFT in Integer konvertieren und Koeffizienten aufaddieren mit Zehner-Versatz
BufferProdukt = AllocateMemory((N * 8) + 128)
If BufferProdukt
  BufferProduktA = BufferProdukt + (64 - (BufferProdukt & $3F)) + 64 ;64-er Alignment, +64 zur Vermeidung Unterlauf (unsauber, aber schneller!)
 Else 
  MessageRequester("Fehler!", "Nicht genügend Speicher für BufferProdukt!")
  End
EndIf

BufferDez.q = AllocateMemory(32 + 128)
If BufferDez
  BufferDezA = BufferDez + (64 - (BufferDez & $3F))             ;64-er Alignment
 Else 
  MessageRequester("Fehler!", "Nicht genügend Speicher für BufferDez!")
  End 
EndIf

Restore Dezi                 ;sicher ist sicher!

!stmxcsr [v_MXCSR]           ;Control-und Status-Register (SSE) laden für Konvertierung Doubles in Quad-Integer. Aus Jux mit SSE2
MXCSR_Sicher = MXCSR         ;für Wiederherstellung
!and [v_MXCSR],11111111111111111001111111111111b ;Bit 13 und 14 auf Null setzen (=Round to Nearest)
!ldmxcsr [v_MXCSR]          ;speichern

For i = 0 To N - 1           ;vom Produkt alle Werte durch
  ;Division der Produktwerte durch N und Konvertierung Double in Integer
  ;QuadWert = Round(PeekD(BufferFaktor2A + i * 16), #PB_Round_Nearest)  ;Double zu Quad
  !fninit                    ;hier nötig!!!
  !mov rax,[v_i]
  !mov r8,20
  !mul r8
  !mov r9,[v_BufferFaktor2A]

  !fild [v_N]
  !fld tword[r9+rax]
  !fdiv st0,st1
  ;!fabs                      ;wohl nicht nötig  
  !lea rdx,[WR]               ;hier bleibt LEA wegen RDX
  !fstp qword[rdx]

  ;die Doubles in Quad-Integer konvertieren. Dies und damit die Rundungs-Kontrolle aus Jux mit SSE2 
  ;QuadWert = WR
  !cvtsd2si rax,[rdx]
  !mov [v_QuadWert],rax

 ;Hex2Dez
  QuadWert1 = QuadWert
  Ziffer = 0
  Pointer1 = 0
  Pointer2 = 0
  While PeekQ(?Dezi + Pointer1) <> 0
    While (QuadWert - PeekQ(?Dezi + Pointer1)) >= 0
      Ziffer + 1
      QuadWert - PeekQ(?Dezi + Pointer1)
      QuadWert1 = QuadWert
    Wend
    PokeB(BufferDezA + Pointer2, Ziffer)
    Pointer1 + 8
    Pointer2 + 1
    Ziffer = 0
    QuadWert = QuadWert1
  Wend
  PokeB(BufferDezA + Pointer2, QuadWert1)   ;letzte Dezimal-Ziffer abspeichern

  Pointer_Produkt1 = Pointer_Produkt 
  For k = 0 To 15
    Ziffer = PeekB(BufferProduktA + Pointer_Produkt1)
    Ziffer + PeekB(BufferDezA + Pointer2)
    PokeB(BufferProduktA + Pointer_Produkt1, Ziffer)
    If Ziffer > 9                           ;Übertrag
      Ziffer - 10
      PokeB(BufferProduktA + Pointer_Produkt1, Ziffer)
      Ziffer = PeekB(BufferProduktA + Pointer_Produkt1 - 1) + 1
      PokeB(BufferProduktA + Pointer_Produkt1 - 1, Ziffer)
    EndIf
    Pointer_Produkt1 - 1
    ;If Pointer_Produkt1 < 0 ;sauberer, aber langsamer! Muss ich mir nochmal anschauen, Ergebnis wird aber nicht beeinträchtigt. Irgendwie muss es aber einfacher gehen
      ;Break
    ;EndIf
    Pointer2 - 1
  Next
  Pointer_Produkt - 1
Next

!ldmxcsr [v_MXCSR_Sicher]   ;Control-und Status-Register alten Zustand wieder herstellen

;------------------------
;Ergebnis (Produkt) in ASCII-Ziffern abspeichern
For i = 0 To N Step 8
  PokeQ(BufferProduktA + i, PeekQ(BufferProduktA + i) | $3030303030303030)
Next
PokeB(BufferProduktA + i - 8, 0)                 ;Zero-Byte zur Sicherheit für String-Auslesen

FreeMemory(BufferFaktor2)
FreeMemory(BufferDez)

Produkt$ = LTrim(PeekS(BufferProduktA), "0")     ;evtl. führende Null(en) weg
If Produkt$ = ""
  Produkt$ = "0"
EndIf

FreeMemory(BufferProdukt)

SetClipboardText(Produkt$)        ;oder auskommentieren

TE_Ergebnis = ElapsedMilliseconds() - TA_Ergebnis

TE_Gesamt = ElapsedMilliseconds() - TA_Gesamt

Faktor$ = "Faktor-Laenge (max.): " + Str(L1) + " Bytes" + #CRLF$
Exponent$ = "Exponent 2^ für angepasste Faktoren-Laenge: " + Str(Exponent - 1) + #CRLF$
Winkel$ = "Zeit für Sinus/Cosinus-Berechnung: " + Str(TE_Winkel) + " ms" + #CRLF$
Faktoren$ = "Zeit für Faktoren-Aufbereitung: " + Str(TE_Faktoren) + " ms" + #CRLF$
FFT$ = "Zeit für Fast Fourier Transformation: " + Str(TE_FFT) + " ms" + #CRLF$
Multi$ = "Zeit für Multiplikation: " + Str(TE_Multi) + " ms" + #CRLF$
Rueck$ = "Zeit für Rueck-Transformation: " + Str(TE_Rueck) + " ms" + #CRLF$
Ergebnis$ = "Zeit für Ergebnis-Aufbereitung als String: " + Str(TE_Ergebnis) + " ms" + #CRLF$
Gesamt$ = "Zeit gesamt: " + Str(TE_Gesamt) + " ms" + #CRLF$
Info$ = Faktor$ + Exponent$ + Winkel$ + Faktoren$ + FFT$ + Multi$ + Rueck$ + Ergebnis$ + Gesamt$

If Len(Produkt$) > 131072    ;je nach Gusto und was Windows noch sicher anzeigt
  MessageRequester("Helles Multiplikation-FFT-Test", Info$)
 Else
  OpenWindow(0, 0, 0, 1000, 800, "Helles Multiplikation-FFT-Test", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
  StringGadget(0, 10, 10, 980, 780, Info$ + Produkt$, #PB_String_ReadOnly | #ES_MULTILINE | #ESB_DISABLE_LEFT | #ESB_DISABLE_RIGHT | #WS_VSCROLL)
  Repeat : Until WaitWindowEvent() = #PB_Event_CloseWindow
  CloseWindow(0)
EndIf

End

DataSection
  !Rad1    dt ?    ;10-Byte-Variablen
  !        dp ?    ;6 Bytes Dummy wegen Alignment
  !Sin     dt ?
  !        dp ?
  !Cos     dt ?
  !        dp ?
  !WR      dt ?
  !        dp ?
  !WI      dt ?
  !        dp ?
  !ZR      dt ?
  !        dp ?
  !ZI      dt ?
  !        dp ?
Dezi:
  Data.q 1000000000000000
  Data.q  100000000000000
  Data.q   10000000000000
  Data.q    1000000000000
  Data.q     100000000000
  Data.q      10000000000
  Data.q       1000000000
  Data.q        100000000
  Data.q         10000000
  Data.q          1000000
  Data.q           100000
  Data.q            10000
  Data.q             1000
  Data.q              100
  Data.q               10
  Data.q                0

EndDataSection
Wie immer: Viel Spaß!
Helle

Edit 31.12.2015: Kleine Optimierung (FPU-Adressierung)
Edit 31.12.2015: Weitere kleine Optimierung. UND: Bis auf die max. Faktoren-Länge keine Einschränkungen mehr!
Zuletzt geändert von Helle am 31.12.2015 14:02, insgesamt 2-mal geändert.
Derren
Beiträge: 557
Registriert: 23.07.2011 02:08

Re: Schnelle Integer-Multiplikation mit FFT

Beitrag von Derren »

Ach, dieser komische For-Loop unter den Faktoren gehört gar nicht zum Code... Jetzt geht's auch. :)
Signatur und so
Benutzeravatar
NicTheQuick
Ein Admin
Beiträge: 8679
Registriert: 29.08.2004 20:20
Computerausstattung: Ryzen 7 5800X, 32 GB DDR4-3200
Ubuntu 22.04.3 LTS
GeForce RTX 3080 Ti
Wohnort: Saarbrücken
Kontaktdaten:

Re: Schnelle Integer-Multiplikation mit FFT

Beitrag von NicTheQuick »

Gibt's das auch irgendwie als fertige Procedure oder sogar als Modul? :)
Braucht übrigens 210 ms bei mir. :allright:
Bild
Benutzeravatar
Helle
Beiträge: 566
Registriert: 11.11.2004 16:13
Wohnort: Magdeburg

Re: Schnelle Integer-Multiplikation mit FFT

Beitrag von Helle »

Habe obigen Code doch noch dieses Jahr (wie das klingt :mrgreen: !) überarbeitet. Bis auf die max. Faktoren-Länge gibt es keine Einschränkungen mehr!
@Nic: Module sind mir (noch) suspekt. Procedure ist kein Problem, aber generell stört mich mein eigener Mischmasch PB-ASM. War ja mal reiner PB-Code, aber wegen Wertebereich ist dann doch ASM (FPU 80-Bit) hinzugekommen. Da ich die "Verstringung" sowieso überarbeiten will, wird das wohl ASM werden (auch wegen Speed) und dann als Procedure.

Bis dahin allen guten Rutsch!
Helle
Antworten