import pytest
import numpy as np
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parent.parent))
from alice_zip.native_accelerator import (
is_available,
perlin_2d,
perlin_advanced,
fourier_generate,
sine_wave,
multi_sine,
fourier_analyze,
polynomial_generate,
polynomial_fit,
_python_perlin_2d,
_python_perlin_advanced,
_python_fourier_generate,
_python_sine_wave,
_python_multi_sine,
_python_fourier_analyze,
_python_polynomial_generate,
_python_polynomial_fit,
)
class TestIsAvailable:
def test_is_available_returns_bool(self):
result = is_available()
assert isinstance(result, bool)
class TestPerlin2D:
def test_basic_generation(self):
result = perlin_2d(64, 64, seed=42, scale=10.0, octaves=4)
assert result.shape == (64, 64)
assert result.dtype == np.float32
assert 0.0 <= result.min()
assert result.max() <= 1.0
def test_reproducibility(self):
result1 = perlin_2d(32, 32, seed=12345)
result2 = perlin_2d(32, 32, seed=12345)
np.testing.assert_array_equal(result1, result2)
def test_different_seeds(self):
result1 = perlin_2d(32, 32, seed=1)
result2 = perlin_2d(32, 32, seed=2)
assert not np.array_equal(result1, result2)
def test_different_sizes(self):
result_small = perlin_2d(16, 16)
result_large = perlin_2d(128, 128)
assert result_small.shape == (16, 16)
assert result_large.shape == (128, 128)
def test_rectangular_dimensions(self):
result = perlin_2d(width=100, height=50)
assert result.shape == (50, 100)
class TestPerlinAdvanced:
def test_basic_generation(self):
result = perlin_advanced(64, 64, seed=42)
assert result.shape == (64, 64)
assert result.dtype == np.float32
def test_persistence_effect(self):
result_low = perlin_advanced(32, 32, seed=42, persistence=0.3)
result_high = perlin_advanced(32, 32, seed=42, persistence=0.7)
assert np.std(result_low) != np.std(result_high)
def test_lacunarity_effect(self):
result_low = perlin_advanced(32, 32, seed=42, lacunarity=1.5)
result_high = perlin_advanced(32, 32, seed=42, lacunarity=3.0)
assert not np.array_equal(result_low, result_high)
def test_octaves_effect(self):
result_few = perlin_advanced(32, 32, seed=42, octaves=1)
result_many = perlin_advanced(32, 32, seed=42, octaves=6)
assert not np.array_equal(result_few, result_many)
class TestFourierGenerate:
def test_single_frequency(self):
coefficients = [(5, 1.0, 0.0)] result = fourier_generate(1000, coefficients, dc_offset=0.0)
assert result.shape == (1000,)
assert result.dtype == np.float32
def test_multiple_frequencies(self):
coefficients = [
(5, 1.0, 0.0),
(10, 0.5, np.pi/4),
(20, 0.25, np.pi/2)
]
result = fourier_generate(1000, coefficients, dc_offset=0.0)
assert result.shape == (1000,)
def test_dc_offset(self):
coefficients = [(10, 1.0, 0.0)]
result = fourier_generate(1000, coefficients, dc_offset=5.0)
np.testing.assert_almost_equal(np.mean(result), 5.0, decimal=1)
def test_empty_coefficients(self):
result = fourier_generate(100, [], dc_offset=3.0)
np.testing.assert_array_almost_equal(result, np.full(100, 3.0), decimal=5)
class TestSineWave:
def test_basic_sine(self):
result = sine_wave(1000, frequency=10.0, amplitude=1.0, phase=0.0)
assert result.shape == (1000,)
assert result.dtype == np.float32
np.testing.assert_almost_equal(np.max(np.abs(result)), 1.0, decimal=2)
def test_amplitude_scaling(self):
result_small = sine_wave(1000, frequency=10.0, amplitude=0.5)
result_large = sine_wave(1000, frequency=10.0, amplitude=2.0)
assert np.max(np.abs(result_small)) < np.max(np.abs(result_large))
def test_dc_offset(self):
result = sine_wave(1000, frequency=10.0, amplitude=1.0, dc_offset=5.0)
np.testing.assert_almost_equal(np.mean(result), 5.0, decimal=1)
def test_phase_shift(self):
result_0 = sine_wave(1000, frequency=5.0, phase=0.0)
result_90 = sine_wave(1000, frequency=5.0, phase=np.pi/2)
assert not np.allclose(result_0[:10], result_90[:10])
class TestMultiSine:
def test_single_component(self):
components = [(10.0, 1.0, 0.0)]
result = multi_sine(1000, components, dc_offset=0.0)
assert result.shape == (1000,)
def test_multiple_components(self):
components = [
(10.0, 1.0, 0.0),
(20.0, 0.5, 0.0),
(30.0, 0.25, 0.0)
]
result = multi_sine(1000, components, dc_offset=0.0)
assert result.shape == (1000,)
assert np.max(np.abs(result)) > 0.5
def test_dc_offset(self):
components = [(10.0, 1.0, 0.0)]
result = multi_sine(1000, components, dc_offset=3.0)
np.testing.assert_almost_equal(np.mean(result), 3.0, decimal=1)
class TestFourierAnalyze:
def test_analyze_single_sine(self):
t = np.arange(1000)
signal = np.sin(2 * np.pi * 10 * t / 1000)
coefficients, dc_offset = fourier_analyze(signal, max_coefficients=5)
assert len(coefficients) > 0
assert abs(dc_offset) < 0.1
def test_analyze_with_dc(self):
t = np.arange(1000)
signal = np.sin(2 * np.pi * 5 * t / 1000) + 3.0
coefficients, dc_offset = fourier_analyze(signal)
np.testing.assert_almost_equal(dc_offset, 3.0, decimal=1)
def test_analyze_returns_tuples(self):
signal = np.random.randn(500)
coefficients, dc_offset = fourier_analyze(signal, max_coefficients=10)
assert isinstance(coefficients, list)
assert isinstance(dc_offset, float)
for coef in coefficients:
assert len(coef) == 3
class TestPolynomialGenerate:
def test_linear(self):
coefficients = [2.0, 1.0]
result = polynomial_generate(100, coefficients)
assert result.shape == (100,)
np.testing.assert_almost_equal(result[0], 1.0, decimal=5)
np.testing.assert_almost_equal(result[-1], 3.0, decimal=5)
def test_quadratic(self):
coefficients = [1.0, 0.0, 0.0]
result = polynomial_generate(100, coefficients)
assert result.shape == (100,)
assert result[0] >= -0.01
def test_constant(self):
coefficients = [5.0]
result = polynomial_generate(50, coefficients)
np.testing.assert_array_almost_equal(result, np.full(50, 5.0), decimal=5)
def test_empty_coefficients(self):
result = polynomial_generate(100, [])
np.testing.assert_array_equal(result, np.zeros(100))
class TestPolynomialFit:
def test_fit_linear(self):
x = np.linspace(0, 1, 100)
data = 2 * x + 1
result = polynomial_fit(data, max_degree=3)
assert result is not None
coeffs, degree, error = result
assert degree >= 1
assert error < 0.001
def test_fit_quadratic(self):
x = np.linspace(0, 1, 100)
data = x**2 + 0.5*x + 0.1
result = polynomial_fit(data, max_degree=5)
assert result is not None
coeffs, degree, error = result
assert degree >= 2
assert error < 0.001
def test_fit_constant(self):
data = np.full(100, 5.0)
result = polynomial_fit(data, max_degree=5)
assert result is not None
coeffs, degree, error = result
assert degree == 0
assert error < 0.001
def test_fit_noise_may_fail(self):
data = np.random.randn(100)
result = polynomial_fit(data, max_degree=3, error_threshold=0.0001)
if result is not None:
coeffs, degree, error = result
assert error > 0.1 or degree == 3
class TestPythonFallbacks:
def test_python_perlin_2d(self):
result = _python_perlin_2d(32, 32, 42, 10.0, 4)
assert result.shape == (32, 32)
assert result.dtype == np.float32
assert 0.0 <= result.min()
assert result.max() <= 1.0
def test_python_fourier_generate(self):
coefficients = [(5, 1.0, 0.0)]
result = _python_fourier_generate(500, coefficients, 0.0)
assert result.shape == (500,)
assert result.dtype == np.float32
def test_python_sine_wave(self):
result = _python_sine_wave(1000, 10.0, 1.0, 0.0, 0.0)
assert result.shape == (1000,)
assert result.dtype == np.float32
def test_python_polynomial_generate(self):
coefficients = [1.0, 2.0, 3.0]
result = _python_polynomial_generate(100, coefficients)
assert result.shape == (100,)
assert result.dtype == np.float32
if __name__ == '__main__':
pytest.main([__file__, '-v'])