mirror of
https://github.com/jkl1337/SwiftPFFFT.git
synced 2026-01-02 11:44:36 -06:00
Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ba20b23d26 | |||
| 52d5bcd96d | |||
| fa3bce9237 | |||
| 437f984b11 | |||
| 795003432e | |||
| 2ffe635962 | |||
| ab36848004 | |||
| 157b187116 | |||
| 01c48650e9 | |||
| f52a4147b8 |
52
.github/workflows/test.yml
vendored
Normal file
52
.github/workflows/test.yml
vendored
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
name: test
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [master]
|
||||||
|
pull_request:
|
||||||
|
branches: [master]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test-linux:
|
||||||
|
name: Tests Linux
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}-${{ toJSON(matrix) }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
swift: ["5.9", "5.10", "6.0"]
|
||||||
|
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container: swift:${{ matrix.swift }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: swift build
|
||||||
|
|
||||||
|
- name: Run tests
|
||||||
|
run: swift test
|
||||||
|
|
||||||
|
test-macos:
|
||||||
|
name: Tests MacOS
|
||||||
|
runs-on: macos-14
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Select toolchain
|
||||||
|
uses: maxim-lobanov/setup-xcode@v1
|
||||||
|
with: { xcode-version: latest-stable }
|
||||||
|
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: swift build
|
||||||
|
|
||||||
|
- name: Run tests
|
||||||
|
run: swift test
|
||||||
5
.sourcekit-lsp/config.json
Normal file
5
.sourcekit-lsp/config.json
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"swiftPM": {
|
||||||
|
"cCompilerFlags:" ["-DPFFFT_SCALVEC_ENABLED=1", "-DPFFFT_ENABLE_NEON", "_USE_MATH_DEFINES", "NDEBUG"],
|
||||||
|
}
|
||||||
|
}
|
||||||
32
LICENSE
Normal file
32
LICENSE
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
BSD 3-Clause License
|
||||||
|
|
||||||
|
Copyright (c) 2024 John K. Luebs
|
||||||
|
Copyright (c) 2020 Dario Mambro (dario.mambro@gmail.com)
|
||||||
|
Copyright (c) 2019 Hayati Ayguen (h_ayguen@web.de)
|
||||||
|
Copyright (c) 2013 Julien Pommier (pommier@modartt.com)
|
||||||
|
Copyright (c) 2004 the University Corporation for Atmospheric Research ("UCAR"). All rights reserved. Developed by NCAR's Computational and Information Systems Laboratory, UCAR, www.cisl.ucar.edu.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
3. Neither the name of the copyright holder nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
37
LICENSE.txt
37
LICENSE.txt
@@ -1,37 +0,0 @@
|
|||||||
Copyright (c) 2024 John K. Luebs
|
|
||||||
Copyright (c) 2020 Dario Mambro ( dario.mambro@gmail.com )
|
|
||||||
Copyright (c) 2019 Hayati Ayguen ( h_ayguen@web.de )
|
|
||||||
Copyright (c) 2013 Julien Pommier ( pommier@modartt.com )
|
|
||||||
|
|
||||||
Copyright (c) 2004 the University Corporation for Atmospheric
|
|
||||||
Research ("UCAR"). All rights reserved. Developed by NCAR's
|
|
||||||
Computational and Information Systems Laboratory, UCAR,
|
|
||||||
www.cisl.ucar.edu.
|
|
||||||
|
|
||||||
Redistribution and use of the Software in source and binary forms,
|
|
||||||
with or without modification, is permitted provided that the
|
|
||||||
following conditions are met:
|
|
||||||
|
|
||||||
- Neither the names of NCAR's Computational and Information Systems
|
|
||||||
Laboratory, the University Corporation for Atmospheric Research,
|
|
||||||
nor the names of its sponsors or contributors may be used to
|
|
||||||
endorse or promote products derived from this Software without
|
|
||||||
specific prior written permission.
|
|
||||||
|
|
||||||
- Redistributions of source code must retain the above copyright
|
|
||||||
notices, this list of conditions, and the disclaimer below.
|
|
||||||
|
|
||||||
- Redistributions in binary form must reproduce the above copyright
|
|
||||||
notice, this list of conditions, and the disclaimer below in the
|
|
||||||
documentation and/or other materials provided with the
|
|
||||||
distribution.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO THE WARRANTIES OF
|
|
||||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
||||||
NONINFRINGEMENT. IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT
|
|
||||||
HOLDERS BE LIABLE FOR ANY CLAIM, INDIRECT, INCIDENTAL, SPECIAL,
|
|
||||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
|
||||||
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
||||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
|
|
||||||
SOFTWARE.
|
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
{
|
{
|
||||||
"originHash" : "d50b9049eb671b1ad14bc8ba592c78735f9e357a6b59835343af6e20e8be4701",
|
|
||||||
"pins" : [
|
"pins" : [
|
||||||
{
|
{
|
||||||
"identity" : "swift-numerics",
|
"identity" : "swift-numerics",
|
||||||
@@ -11,5 +10,5 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"version" : 3
|
"version" : 2
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// swift-tools-version: 6.0
|
// swift-tools-version: 5.9
|
||||||
// The swift-tools-version declares the minimum version of Swift required to build this package.
|
// The swift-tools-version declares the minimum version of Swift required to build this package.
|
||||||
|
|
||||||
import PackageDescription
|
import PackageDescription
|
||||||
@@ -20,14 +20,15 @@ let package = Package(
|
|||||||
publicHeadersPath: "include",
|
publicHeadersPath: "include",
|
||||||
cSettings: [
|
cSettings: [
|
||||||
.define("PFFFT_SCALVEC_ENABLED", to: "1"),
|
.define("PFFFT_SCALVEC_ENABLED", to: "1"),
|
||||||
|
.define("PFFFT_ENABLE_NEON"),
|
||||||
.define("_USE_MATH_DEFINES"),
|
.define("_USE_MATH_DEFINES"),
|
||||||
.define("NDEBUG"),
|
.define("NDEBUG"),
|
||||||
.unsafeFlags(["-O3"]),
|
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
.target(
|
.target(
|
||||||
name: "PFFFT",
|
name: "PFFFT",
|
||||||
dependencies: ["PFFFTLib", .product(name: "Numerics", package: "swift-numerics")]
|
dependencies: ["PFFFTLib", .product(name: "Numerics", package: "swift-numerics")],
|
||||||
|
swiftSettings: [.enableExperimentalFeature("AccessLevelOnImport")]
|
||||||
),
|
),
|
||||||
.testTarget(
|
.testTarget(
|
||||||
name: "PFFFTTests",
|
name: "PFFFTTests",
|
||||||
|
|||||||
@@ -1,3 +1,7 @@
|
|||||||
|
[](https://github.com/jkl1337/SwiftPFFFT/actions/workflows/swift.yml)
|
||||||
|
[](https://swiftpackageindex.com/jkl1337/SwiftPFFFT)
|
||||||
|
[](https://swiftpackageindex.com/jkl1337/SwiftPFFFT)
|
||||||
|
|
||||||
# SwiftPFFFT
|
# SwiftPFFFT
|
||||||
|
|
||||||
Swift package providing a PFFFT (Pretty Fast, Fast Fourier Transform) library with wrapper.
|
Swift package providing a PFFFT (Pretty Fast, Fast Fourier Transform) library with wrapper.
|
||||||
@@ -21,7 +25,7 @@ performance with much simpler usage and a permissive 3 clause BSD license.
|
|||||||
let fft = try FFT<Complex<Float>>(n: 16)
|
let fft = try FFT<Complex<Float>>(n: 16)
|
||||||
let signal = fft.makeSignalBuffer()
|
let signal = fft.makeSignalBuffer()
|
||||||
|
|
||||||
signal.mutateEach { (i, v) in
|
signal.enumerateInPlace { (i, v) in
|
||||||
v = Complex(Float(i) + 1.0, Float(i) - 2.0)
|
v = Complex(Float(i) + 1.0, Float(i) - 2.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,10 @@ import RealModule
|
|||||||
|
|
||||||
let bufferAlignment = 32
|
let bufferAlignment = 32
|
||||||
|
|
||||||
|
/// Thin wrapper around `UnsafeMutableBufferPointer` providing correct alignment.
|
||||||
|
/// PFFFT internally assumes all buffers passed are aligned to 16 or 32 bytes
|
||||||
|
/// depending on the platform. Thie type provides correctly aligned buffers
|
||||||
|
/// and provides some in place mutating methods.
|
||||||
@frozen
|
@frozen
|
||||||
public struct Buffer<T>: ~Copyable {
|
public struct Buffer<T>: ~Copyable {
|
||||||
public let buffer: UnsafeMutableBufferPointer<T>
|
public let buffer: UnsafeMutableBufferPointer<T>
|
||||||
@@ -32,13 +36,21 @@ public struct Buffer<T>: ~Copyable {
|
|||||||
try body(UnsafeMutableRawBufferPointer(buffer))
|
try body(UnsafeMutableRawBufferPointer(buffer))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return an array with results of mapping given closure over buffer elements.
|
||||||
|
/// - Parameter transform: A mapping closure. `transform` accepts an element of the buffer
|
||||||
|
/// as its parameter and returns a transformed value of any type.
|
||||||
|
/// - Returns: An array containing the transformed elements of the buffer.
|
||||||
@inlinable public func map<U>(_ transform: (T) throws -> U) rethrows -> [U] {
|
@inlinable public func map<U>(_ transform: (T) throws -> U) rethrows -> [U] {
|
||||||
try buffer.map(transform)
|
try buffer.map(transform)
|
||||||
}
|
}
|
||||||
|
|
||||||
@inlinable public func mutateEach(_ body: (Int, inout T) throws -> Void) rethrows {
|
/// Calls the given closure on each element in the buffer for mutation in place.
|
||||||
|
/// - Parameter body: A closure that accepts a zero-based enumeration index.
|
||||||
|
/// and must return a new value for the element at that index.
|
||||||
|
/// `body` may throw and the error will be propagated to the caller.
|
||||||
|
@inlinable public func mapInPlace(_ body: (Int) throws -> T) rethrows {
|
||||||
for i in 0 ..< buffer.count {
|
for i in 0 ..< buffer.count {
|
||||||
try body(i, &buffer[i])
|
try buffer[i] = body(i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -52,9 +64,23 @@ public protocol ComplexType {
|
|||||||
extension Complex: ComplexType {}
|
extension Complex: ComplexType {}
|
||||||
|
|
||||||
public extension Buffer where T: ComplexType {
|
public extension Buffer where T: ComplexType {
|
||||||
@inlinable public func mutateEachSwapLast(_ body: (Int, inout T) throws -> Void) rethrows {
|
/// Calls the given closure on each space in the buffer for mutation in place with
|
||||||
|
/// Nyquist replacement at the end.
|
||||||
|
///
|
||||||
|
/// When operating on Complex->Real transforms PFFFT internally uses a slightly more compact
|
||||||
|
/// but less common encoding of the DC (0) and Nyquist (n/2) components. Since these two
|
||||||
|
/// spectral components are always real, PFFFT places the DC (0) component
|
||||||
|
/// in the real part of the 0th element as expected, but places the Nyquist `(n/2)` component
|
||||||
|
/// in the imaginary part of the 0th element.
|
||||||
|
/// This enumerator works like `mapInPlace` but at the end places the real part of the
|
||||||
|
/// n/2 component into the imaginary part of the 0th element. In normal use it is expected
|
||||||
|
/// that a spectral buffer of 1 extra element is created such that `count == (n/2 + 1)`.
|
||||||
|
/// - Parameter body: A closure that accepts a zero-based enumeration index.
|
||||||
|
/// and must return a new value for the element at that index.
|
||||||
|
/// `body` may throw and the error will be propagated to the caller.
|
||||||
|
@inlinable func mapInPlaceSwapLast(_ body: (Int) throws -> T) rethrows {
|
||||||
for i in 0 ..< buffer.count {
|
for i in 0 ..< buffer.count {
|
||||||
try body(i, &buffer[i])
|
try buffer[i] = body(i)
|
||||||
}
|
}
|
||||||
buffer[0].imaginary = buffer[buffer.count - 1].real
|
buffer[0].imaginary = buffer[buffer.count - 1].real
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -159,14 +159,20 @@ public struct FFT<T: FFTElement>: ~Copyable {
|
|||||||
|
|
||||||
let ptr: OpaquePointer
|
let ptr: OpaquePointer
|
||||||
let n: Int
|
let n: Int
|
||||||
let work: Buffer<ScalarType>?
|
let work: Buffer<ScalarType>
|
||||||
let setup: Setup
|
let setup: Setup
|
||||||
|
|
||||||
public init(setup: Setup) {
|
public init(setup: Setup) {
|
||||||
self.setup = setup
|
self.setup = setup
|
||||||
ptr = setup.ptr
|
ptr = setup.ptr
|
||||||
n = setup.n
|
n = setup.n
|
||||||
work = n > 4096 ? Buffer<ScalarType>(capacity: T.self == ComplexType.self ? 2 * n : n) : nil
|
|
||||||
|
let workCapacity = if n > 4096 {
|
||||||
|
T.self == ComplexType.self ? 2 * n : n
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
work = Buffer(capacity: workCapacity)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Initialize the FFT implementation with the given size and type.
|
/// Initialize the FFT implementation with the given size and type.
|
||||||
@@ -202,10 +208,11 @@ public struct FFT<T: FFTElement>: ~Copyable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@inline(__always)
|
@inline(__always)
|
||||||
func toAddress(_ work: borrowing Buffer<ScalarType>?) -> UnsafeMutablePointer<ScalarType>? {
|
var workPtr: UnsafeMutablePointer<ScalarType>? {
|
||||||
switch work {
|
if work.count > 0 {
|
||||||
case let .some(b): return b.baseAddress
|
return work.baseAddress
|
||||||
case .none: return nil
|
} else {
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -271,12 +278,12 @@ public struct FFT<T: FFTElement>: ~Copyable {
|
|||||||
/// - sign: The direction of the FFT.
|
/// - sign: The direction of the FFT.
|
||||||
public func forward(signal: borrowing Buffer<T>, spectrum: borrowing Buffer<ComplexType>) {
|
public func forward(signal: borrowing Buffer<T>, spectrum: borrowing Buffer<ComplexType>) {
|
||||||
checkFftBufferCounts(signal: signal, spectrum: spectrum)
|
checkFftBufferCounts(signal: signal, spectrum: spectrum)
|
||||||
ScalarType.pffftTransformOrdered(ptr, rebind(signal), rebind(spectrum), toAddress(work), .forward)
|
ScalarType.pffftTransformOrdered(ptr, rebind(signal), rebind(spectrum), workPtr, .forward)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func inverse(spectrum: borrowing Buffer<ComplexType>, signal: borrowing Buffer<T>) {
|
public func inverse(spectrum: borrowing Buffer<ComplexType>, signal: borrowing Buffer<T>) {
|
||||||
checkFftBufferCounts(signal: signal, spectrum: spectrum)
|
checkFftBufferCounts(signal: signal, spectrum: spectrum)
|
||||||
ScalarType.pffftTransformOrdered(ptr, rebind(spectrum), rebind(signal), toAddress(work), .backward)
|
ScalarType.pffftTransformOrdered(ptr, rebind(spectrum), rebind(signal), workPtr, .backward)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Perform a forward FFT on the input buffer, with implementation defined order.
|
/// Perform a forward FFT on the input buffer, with implementation defined order.
|
||||||
@@ -290,12 +297,12 @@ public struct FFT<T: FFTElement>: ~Copyable {
|
|||||||
/// - sign: The direction of the FFT.
|
/// - sign: The direction of the FFT.
|
||||||
public func forwardToInternalLayout(signal: borrowing Buffer<T>, spectrum: borrowing Buffer<ScalarType>) {
|
public func forwardToInternalLayout(signal: borrowing Buffer<T>, spectrum: borrowing Buffer<ScalarType>) {
|
||||||
checkFftInternalLayoutBufferCounts(signal: signal, spectrum: spectrum)
|
checkFftInternalLayoutBufferCounts(signal: signal, spectrum: spectrum)
|
||||||
ScalarType.pffftTransform(ptr, rebind(signal), spectrum.baseAddress, toAddress(work), .forward)
|
ScalarType.pffftTransform(ptr, rebind(signal), spectrum.baseAddress, workPtr, .forward)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func inverseFromInternalLayout(spectrum: borrowing Buffer<ScalarType>, signal: borrowing Buffer<T>) {
|
public func inverseFromInternalLayout(spectrum: borrowing Buffer<ScalarType>, signal: borrowing Buffer<T>) {
|
||||||
checkFftInternalLayoutBufferCounts(signal: signal, spectrum: spectrum)
|
checkFftInternalLayoutBufferCounts(signal: signal, spectrum: spectrum)
|
||||||
ScalarType.pffftTransform(ptr, spectrum.baseAddress, rebind(signal), toAddress(work), .backward)
|
ScalarType.pffftTransform(ptr, spectrum.baseAddress, rebind(signal), workPtr, .backward)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func reorder(spectrum: borrowing Buffer<ScalarType>, output: borrowing Buffer<ComplexType>) {
|
public func reorder(spectrum: borrowing Buffer<ScalarType>, output: borrowing Buffer<ComplexType>) {
|
||||||
@@ -303,7 +310,7 @@ public struct FFT<T: FFTElement>: ~Copyable {
|
|||||||
fatalError("signal buffer too small")
|
fatalError("signal buffer too small")
|
||||||
}
|
}
|
||||||
guard output.count >= n else {
|
guard output.count >= n else {
|
||||||
fatalError("outputbuffer too small")
|
fatalError("output buffer too small")
|
||||||
}
|
}
|
||||||
ScalarType.pffftZreorder(ptr, spectrum.baseAddress, rebind(output), .forward)
|
ScalarType.pffftZreorder(ptr, spectrum.baseAddress, rebind(output), .forward)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1061,12 +1061,14 @@ SETUP_STRUCT *FUNC_NEW_SETUP(int N, pffft_transform_t transform) {
|
|||||||
if (transform == PFFFT_REAL) { if ((N%(2*SIMD_SZ*SIMD_SZ)) || N<=0) return s; }
|
if (transform == PFFFT_REAL) { if ((N%(2*SIMD_SZ*SIMD_SZ)) || N<=0) return s; }
|
||||||
if (transform == PFFFT_COMPLEX) { if ((N%( SIMD_SZ*SIMD_SZ)) || N<=0) return s; }
|
if (transform == PFFFT_COMPLEX) { if ((N%( SIMD_SZ*SIMD_SZ)) || N<=0) return s; }
|
||||||
s = (SETUP_STRUCT*)malloc(sizeof(SETUP_STRUCT));
|
s = (SETUP_STRUCT*)malloc(sizeof(SETUP_STRUCT));
|
||||||
|
if (!s) return s;
|
||||||
/* assert((N % 32) == 0); */
|
/* assert((N % 32) == 0); */
|
||||||
s->N = N;
|
s->N = N;
|
||||||
s->transform = transform;
|
s->transform = transform;
|
||||||
/* nb of complex simd vectors */
|
/* nb of complex simd vectors */
|
||||||
s->Ncvec = (transform == PFFFT_REAL ? N/2 : N)/SIMD_SZ;
|
s->Ncvec = (transform == PFFFT_REAL ? N/2 : N)/SIMD_SZ;
|
||||||
s->data = (v4sf*)FUNC_ALIGNED_MALLOC(2*s->Ncvec * sizeof(v4sf));
|
s->data = (v4sf*)FUNC_ALIGNED_MALLOC(2*s->Ncvec * sizeof(v4sf));
|
||||||
|
if (!s->data) { free(s); return 0; }
|
||||||
s->e = (float*)s->data;
|
s->e = (float*)s->data;
|
||||||
s->twiddle = (float*)(s->data + (2*s->Ncvec*(SIMD_SZ-1))/SIMD_SZ);
|
s->twiddle = (float*)(s->data + (2*s->Ncvec*(SIMD_SZ-1))/SIMD_SZ);
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +1,14 @@
|
|||||||
import Testing
|
|
||||||
import ComplexModule
|
import ComplexModule
|
||||||
@testable import PFFFT
|
@testable import PFFFT
|
||||||
|
import XCTest
|
||||||
|
|
||||||
@Test func fftFloat() async throws {
|
final class FFTTests: XCTestCase {
|
||||||
|
func testFftComplexFloat() throws {
|
||||||
let fft = try FFT<Complex<Float>>(n: 16)
|
let fft = try FFT<Complex<Float>>(n: 16)
|
||||||
let signal = fft.makeSignalBuffer()
|
let signal = fft.makeSignalBuffer()
|
||||||
let spectrum = fft.makeSpectrumBuffer()
|
let spectrum = fft.makeSpectrumBuffer()
|
||||||
|
|
||||||
signal.mutateEach { (i, v) in
|
signal.mapInPlace { Complex(Float($0) + 1.0, Float($0) - 2.0) }
|
||||||
v = Complex(Float(i) + 1.0, Float(i) - 2.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
fft.forward(signal: signal, spectrum: spectrum)
|
fft.forward(signal: signal, spectrum: spectrum)
|
||||||
|
|
||||||
@@ -33,18 +31,19 @@ import ComplexModule
|
|||||||
.init(11.313707, -27.31371),
|
.init(11.313707, -27.31371),
|
||||||
.init(32.218716, -48.218716),
|
.init(32.218716, -48.218716),
|
||||||
]
|
]
|
||||||
zip(result, expected).forEach { r, e in
|
for (r, e) in zip(result, expected) {
|
||||||
#expect(r.isApproximatelyEqual(to: e))
|
XCTAssert(r.isApproximatelyEqual(to: e))
|
||||||
}
|
}
|
||||||
|
|
||||||
fft.inverse(spectrum: spectrum, signal: signal)
|
fft.inverse(spectrum: spectrum, signal: signal)
|
||||||
|
|
||||||
let signalResult = signal.map { $0 }
|
let signalResult = signal.map { $0 }
|
||||||
let signalExpected = (0..<16).map { i in
|
let signalExpected = (0 ..< 16).map { i in
|
||||||
Complex(Float(i) + 1.0, Float(i) - 2.0) * 16
|
Complex(Float(i) + 1.0, Float(i) - 2.0) * 16
|
||||||
}
|
}
|
||||||
|
|
||||||
zip(signalResult, signalExpected).forEach { r, e in
|
for (r, e) in zip(signalResult, signalExpected) {
|
||||||
#expect(r.isApproximatelyEqual(to: e))
|
XCTAssert(r.isApproximatelyEqual(to: e))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user