/*
 * math.cs
 * Copyright (c) 2008-2009 kbinani
 *
 * This file is part of bocoree.
 *
 * bocoree is free software; you can redistribute it and/or
 * modify it under the terms of the BSD License.
 *
 * bocoree is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 */
using System;
using System.Collections.Generic;

namespace bocoree {

    public partial class math {
        private const double _PI2 = 2.0 * Math.PI;
        private const double _PI4 = 4.0 * Math.PI;
        private const double _PI6 = 6.0 * Math.PI;
        private const double _PI8 = 8.0 * Math.PI;

        public enum WindowFunctionType {
            Hamming,
            rectangular,
            Gauss,
            Hann,
            Blackman,
            Bartlett,
            Nuttall,
            Blackman_Harris,
            Blackman_Nattall,
            flap_top,
            Parzen,
            Akaike,
            Welch,
            Kaiser,
        }

        public static double window_func( WindowFunctionType type, double x ) {
            switch ( type ) {
                case WindowFunctionType.Akaike:
                    return wnd_akaike( x );
                case WindowFunctionType.Bartlett:
                    return wnd_bartlett( x );
                case WindowFunctionType.Blackman:
                    return wnd_blackman( x );
                case WindowFunctionType.Blackman_Harris:
                    return wnd_blackman_harris( x );
                case WindowFunctionType.Blackman_Nattall:
                    return wnd_blackman_nattall( x );
                case WindowFunctionType.flap_top:
                    return wnd_flap_top( x );
                case WindowFunctionType.Gauss:
                    throw new ApplicationException( "too few argument for Gauss window function" );
                case WindowFunctionType.Hamming:
                    return wnd_hamming( x );
                case WindowFunctionType.Hann:
                    return wnd_hann( x );
                case WindowFunctionType.Kaiser:
                    throw new ApplicationException( "too few argument for Kaiser window function" );
                case WindowFunctionType.Nuttall:
                    return wnd_nuttall( x );
                case WindowFunctionType.Parzen:
                    return wnd_parzen( x );
                case WindowFunctionType.rectangular:
                    return wnd_rectangular( x );
                case WindowFunctionType.Welch:
                    return wnd_welch( x );
            }
            return 0.0;
        }

        public static double window_func( WindowFunctionType type, double x, params double[] param ) {
            switch ( type ) {
                case WindowFunctionType.Akaike:
                    return wnd_akaike( x );
                case WindowFunctionType.Bartlett:
                    return wnd_bartlett( x );
                case WindowFunctionType.Blackman:
                    return wnd_blackman( x );
                case WindowFunctionType.Blackman_Harris:
                    return wnd_blackman_harris( x );
                case WindowFunctionType.Blackman_Nattall:
                    return wnd_blackman_nattall( x );
                case WindowFunctionType.flap_top:
                    return wnd_flap_top( x );
                case WindowFunctionType.Gauss:
                    return wnd_gauss( x, param[0] );
                case WindowFunctionType.Hamming:
                    return wnd_hamming( x );
                case WindowFunctionType.Hann:
                    return wnd_hann( x );
                case WindowFunctionType.Kaiser:
                    return wnd_kaiser( x, param[0] );
                case WindowFunctionType.Nuttall:
                    return wnd_nuttall( x );
                case WindowFunctionType.Parzen:
                    return wnd_parzen( x );
                case WindowFunctionType.rectangular:
                    return wnd_rectangular( x );
                case WindowFunctionType.Welch:
                    return wnd_welch( x );
            }
            return 0.0;
        }

        public static double wnd_kaiser( double x, double alpha ) {
            if ( 0.0 <= x && x <= 1.0 ) {
                double t = 2.0 * x - 1.0;
                return besi0( Math.PI * alpha * Math.Sqrt( 1.0 - t * t ) ) / besi0( Math.PI * alpha );
            } else {
                return 0.0;
            }
        }

        public static double wnd_welch( double x ) {
            if ( 0.0 <= x && x <= 1.0 ) {
                return 4.0 * x * (1.0 - x);
            } else {
                return 0.0;
            }
        }

        public static double wnd_akaike( double x ) {
            if ( 0.0 <= x && x <= 1.0 ) {
                return 0.625 - 0.5 * Math.Cos( _PI2 * x )
                             - 0.125 * Math.Cos( _PI4 * x );
            } else {
                return 0.0;
            }
        }

        public static double wnd_parzen( double x ) {
            double x0 = Math.Abs( x );
            if ( x0 <= 1.0 ) {
                return (0.75 * x0 - 1.5) * x0 * x0 + 1.0;
            } else {
                x0 = 2.0 - x0;
                return 0.25 * x0 * x0 * x0;
            }
        }

        public static double wnd_flap_top( double x ) {
            if ( 0.0 <= x && x <= 1.0 ) {
                return 1.0 - 1.93 * Math.Cos( _PI2 * x )
                           + 1.29 * Math.Cos( _PI4 * x )
                           - 0.388 * Math.Cos( _PI6 * x )
                           + 0.032 * Math.Cos( _PI8 * x );
            } else {
                return 0.0;
            }
        }

        public static double wnd_blackman_nattall( double x ) {
            if ( 0.0 <= x && x <= 1.0 ) {
                return 0.3635819 - 0.4891775 * Math.Cos( _PI2 * x )
                                 + 0.1365995 * Math.Cos( _PI4 * x )
                                 - 0.0106411 * Math.Cos( _PI6 * x );
            } else {
                return 0.0;
            }
        }

        public static double wnd_blackman_harris( double x ) {
            if ( 0.0 <= x && x <= 1.0 ) {
                return 0.35875 - 0.48829 * Math.Cos( _PI2 * x )
                               + 0.14128 * Math.Cos( _PI4 * x )
                               - 0.01168 * Math.Cos( _PI6 * x );
            } else {
                return 0.0;
            }
        }

        public static double wnd_nuttall( double x ) {
            if ( 0.0 <= x && x <= 1.0 ) {
                return 0.355768 - 0.487396 * Math.Cos( _PI2 * x )
                                + 0.144232 * Math.Cos( _PI4 * x )
                                - 0.012604 * Math.Cos( _PI6 * x );
            } else {
                return 0.0;
            }
        }

        public static double wnd_bartlett( double x ) {
            if ( 0.0 <= x && x <= 1.0 ) {
                return 1.0 - 2.0 * Math.Abs( x - 0.5 );
            } else {
                return 0.0;
            }
        }

        public static double wnd_blackman( double x ) {
            if ( 0.0 <= x && x <= 1.0 ) {
                return 0.42 - 0.5 * Math.Cos( _PI2 * x ) + 0.08 * Math.Cos( _PI4 * x );
            } else {
                return 0.0;
            }
        }

        public static double wnd_hann( double x ) {
            if ( 0.0 <= x && x <= 1.0 ) {
                return 0.5 - 0.5 * Math.Cos( _PI2 * x );
            } else {
                return 0.0;
            }
        }

        public static double wnd_gauss( double x, double sigma ) {
            return Math.Exp( -x * x / (sigma * sigma) );
        }

        public static double wnd_rectangular( double x ) {
            if ( 0.0 <= x && x <= 1.0 ) {
                return 1.0;
            } else {
                return 0.0;
            }
        }

        public static double wnd_hamming( double x ) {
            if ( 0.0 <= x && x <= 1.0 ) {
                return 0.54 - 0.46 * Math.Cos( _PI2 * x );
            } else {
                return 0.0;
            }
        }

        public static double besi0( double x ) {
            int i;
            double w, wx375;
            double[] a = { 1.0,        3.5156229,  3.0899424,
                           1.2067492,  0.2659732,  0.0360768};
            double[] b = { 0.39894228,   0.013285917,  0.002253187,
                          -0.001575649,  0.009162808, -0.020577063,
                           0.026355372, -0.016476329};
            if ( x < 0.0 ) {
                return 0.0;
            }
            if ( x <= 3.75 ) {
                wx375 = x * x / 14.0625;
                w = 0.0045813;
                for ( i = 5; i >= 0; i-- ) {
                    w = w * wx375 + a[i];
                }
                return w;
            }
            wx375 = 3.75 / x;
            w = 0.003923767;
            for ( i = 7; i >= 0; i-- ) {
                w = w * wx375 + b[i];
            }
            return w / Math.Sqrt( x ) * Math.Exp( x );
        }

        public static double erfcc( double x ) {
            double t, z, res;
            z = Math.Abs( x );
            t = 1.0 / (1.0 + 0.5 * z);
            res = t * Math.Exp( -z * z - 1.26551223 + t * (1.00002368 + t * (0.37409196
               + t * (0.09678418 + t * (-0.18628806 + t * (0.27886807
               + t * (-1.13520398 + t * (1.48851587 + t * (-0.82215223
               + t * 0.17087277)))))))) );
            if ( x < 0.0 ) {
                res = 2.0 - res;
            }
            return res;
        }
    }

}
