-- | -- Module : Data.Periodic -- Copyright : (c) OleksandrZhabenko 2020 -- License : MIT -- Stability : Experimental -- Maintainer : olexandr543@yahoo.com -- -- A library for working with periodic polynomials (very basic functionality). -- Provides also simple tools to make a numerical function a periodic (or somewhat similar) one. -- module Data.Periodic ( -- * The simplest finite periodic polynomials polyG1 , trigPolyCos , trigPolySin , trigPoly -- * Periodizer functions , periodizer , concatPeriodizer -- * Periodizer applications , polyG2 , polyG3 ) where import qualified Data.Vector as V -- | A finite trigonometric polynomial of sines. The 'V.Vector' argument is used to produce its coefficients (weights) by applying to each of the element the function -- @f:: a -> a@ given as the first argument. trigPolySin :: (Floating a) => (a -> a) -> V.Vector a -> a -> a trigPolySin f = polyG1 f (sin) -- | A finite trigonometric polynomial of cosines. The 'V.Vector' argument is used to produce its coefficients (weights) by applying to each of the element the function -- @f:: a -> a@ given as the first argument. trigPolyCos :: (Floating a) => (a -> a) -> V.Vector a -> a -> a trigPolyCos f = polyG1 f (cos) -- | Sum of the sine and cosine finite trigonometric polynomials. Can represent Fourier series (without the first coefficient), but no numerical high accuracy is guaranteed. trigPoly :: (Floating a) => (a -> a) -> V.Vector a -> (a -> a) -> V.Vector a -> a -> a trigPoly f v1 g v2 y | V.null v1 = polyG1 g (sin) v2 y | V.null v2 = polyG1 f (cos) v1 y | otherwise = (V.sum . V.zipWith (*) (V.map g v2) . V.generate (V.length v2) $ (\i -> sin (fromIntegral (i + 1) * y))) + (V.sum . V.zipWith (*) (V.map f v1) . V.generate (V.length v1) $ (\i -> cos (fromIntegral (i + 1) * y))) -- | Makes a function @f :: a -> b@ periodic with the period given by the third argument and the starting point given by the second argument. periodizer :: (RealFrac a) => (a -> b) -> a -> a -> a -> b periodizer f x0 period x | period /= 0.0 = let delta = (x - x0) / abs period in f (x0 + (abs period) * (delta - (fromIntegral . truncate $ delta))) | otherwise = error "Data.Periodic.periodizer: Not defined for the zero period. " -- | Modified periodizer that tries to concat the pieces of the function so that it can be (generally speaking) continuous. Needs more mathematical studies. concatPeriodizer :: (RealFrac a, Num b) => (a -> b) -> a -> a -> a -> b concatPeriodizer f x0 period x | period /= 0.0 = let delta = (x - x0) / abs period deltaI = truncate delta in f (x0 + (abs period) * (delta - (fromIntegral deltaI))) + fromIntegral deltaI * (f (x0 + abs period) - f x0) | otherwise = error "Data.Periodic.concatPeriodizer: Not defined for the zero period. " -- | The first function @f :: a -> a@ is applied to the vector to produce weighted coefficients for the sum and the second one @g :: a -> a@ is used as a basis -- function. For the periodic function g the resulting function is also periodic with the same period. Among possible variants there are finite trigonometric -- polynomials. See as examples 'trigPolySin' and 'trigPolyCos' functions. polyG1 :: (Floating a) => (a -> a) -> (a -> a) -> V.Vector a -> a -> a polyG1 f g v y | V.null v = 0.0 | otherwise = V.sum . V.zipWith (*) (V.map f v) . V.generate (V.length v) $ (\i -> g (fromIntegral (i + 1) * y)) -- | Instead of simply use the second function in 'polyG1' it applies to it a 'periodizer' with the given arguments. polyG2 :: (RealFrac a) => (a -> a) -> (a -> a) -> a -> a -> V.Vector a -> a -> a polyG2 f g period y0 v y | V.null v = 0.0 | otherwise = V.sum . V.zipWith (*) (V.map f v) . V.generate (V.length v) $ (\i -> periodizer g y0 period (fromIntegral (i + 1) * y)) -- | Instead of simply use the second function in 'polyG1' it applies to it a 'concatPeriodizer' with the given arguments. polyG3 :: (RealFrac a) => (a -> a) -> (a -> a) -> a -> a -> V.Vector a -> a -> a polyG3 f g period y0 v y | V.null v = 0.0 | otherwise = V.sum . V.zipWith (*) (V.map f v) . V.generate (V.length v) $ (\i -> concatPeriodizer g y0 period (fromIntegral (i + 1) * y))