template< u8 kernel_size, u8 half_size = kernel_size / 2 >

class                           MEDIAN

{

    union                       QUAD

    {

        u8                      bytes[ 4 ];

        u32                     value;

    };


    template< i8 unit >

    struct                      COUNTER

    {

        static  rigid   u32     size()

        {

            return  1 << ( 16 - unit );

        }


        using                   type =

            if_( kernel_size < size() )

            then_( u8 )

            else_( u32 );


        using                   read_type =

            if_( u32( kernel_size * 4 ) < size() )

            then_( QUAD )

            else_( type );


        static  rigid   u32     bytes()

        {

            return  sizeof( type ) * size();

        }


        static  INLINE  oo      next( const xx* src )

        {

            return  ( COUNTER< unit - 4 >::read_type* )

                ( ( COUNTER< unit >::type* )src + size() );

        }

    };


    template< i8 unit >

    static  rigid   u32         offset()

    {

        return  COUNTER< unit + 4 >::bytes() + offset< unit + 4 >();

    }

    template<>

    static  rigid   u32         offset< 12 >()

    {

        return  0;

    }


    static  rigid   u32         bytes()

    {

        return  offset< -4 >();

    }

    ///-------------------------+---------------------------------------------------------------


    template< i8 unit >

    struct                      LEVEL

    {

        template< class T >

        static  INLINE  oo      settle( const T* hist, const u32 base, const u32 counter )

        {

            return  LEVEL< unit >::sum( COUNTER< unit + 4 >::next( hist ), base, counter );

        }


        template< class T >

        static  INLINE  oo      sum( const T* hist, u32 base = 0, u32 counter = 0 )

        {

            T*    s = (T*)hist + ( base >> unit ) - 1;

            while( counter + *++s <= half_size )

                counter += *s;

            base = u32( s - hist ) << unit;

            return  LEVEL< unit - 4 >::settle( hist, base, counter );

        }


        template< class T >

        static  INLINE  oo      assort(

            const T* hist, const u32 base, const u32 counter, const u8 val )

        {

            if( counter + val > half_size )

                return  LEVEL< unit - 4 >::settle( hist, base, counter );

            return  LEVEL< unit - 4 >::settle( hist, base + ( 1 << unit ), counter + val );

        }


        static  INLINE  oo      sum( const QUAD* hist, u32 base = 0, u32 counter = 0 )

        {

            QUAD*   q = (QUAD*)( (u8*)hist + ( base >> unit ) ) - 1;

            while( 1 )

            {

                if( !( ++q )->value )

                    continue;

                const   u32     sum01 = ( q->bytes[ 0 ] + q->bytes[ 1 ] );

                const   u32     sum23 = ( q->bytes[ 2 ] + q->bytes[ 3 ] );

                const   u32     last = counter;

                counter += sum01 + sum23;

                if( counter > half_size )

                {

                    base = ( (u8*)q - (u8*)hist ) << unit;

                    if( last + sum01 > half_size )

                        return  assort( hist, base, last, q->bytes[ 0 ] );

                    return  assort( hist, base + ( 2 << unit ), last + sum01, q->bytes[ 2 ] );

                }

            }

        }

    };

    template<>  struct          LEVEL< -4 >

    {

        template< class T >

        static  INLINE  oo      settle( const T* hist, const u32 base, const u32 counter )

        {

            return  base;

        }

    };

    ///-------------------------+---------------------------------------------------------------


    template< i8 unit >

    static  INLINE  oo          get_counters( const xx* hist )

    {

        return  ( COUNTER< unit >::type* )hist + offset< unit >();

    }


    template< class T >

    static  INLINE  xx          count_up( const u8* hist, const T v )

    {

        get_counters<  0 >( hist )[ v ]++;

        get_counters<  4 >( hist )[ v >> 4 ]++;

        get_counters<  8 >( hist )[ v >> 8 ]++;

        get_counters< 12 >( hist )[ v >> 12 ]++;

    }


    template< class T >

    static  INLINE  xx          count_down( const u8* hist, const T v )

    {

        get_counters<  0 >( hist )[ v ]--;

        get_counters<  4 >( hist )[ v >> 4 ]--;

        get_counters<  8 >( hist )[ v >> 8 ]--;

        get_counters< 12 >( hist )[ v >> 12 ]--;

    }


public:

    template< class T >

    static  oo                  run( T* dst, const T* src, const uu size )

    {

        rigid   u32     back_step = kernel_size - 1;

        u8  histo[ MEDIAN< kernel_size >::bytes() ];

        memset( histo, 0, MEDIAN< kernel_size >::bytes() );


        std::memcpy( dst, src, half_size * sizeof *src );


        for( u32 i = 0; i < kernel_size - 1; ++i )

            count_up( histo, src[ i ] );


        T*  d = dst - half_size;

        T   old_median = ~src[ back_step ];

        i32 changed = 0;


        for( u32 i = back_step; i < size; ++i )

        {

            changed += quick_compare( old_median, src[ i ] );

            count_up( histo, src[ i ] );

            const   u32 o = i - back_step;

            if( changed )

            {

                changed = 0;

                old_median = LEVEL< 12 >::sum( ( COUNTER< 12 >::type* )histo );

            }

            count_down( histo, src[ o ] );

            changed += quick_compare( src[ o ], old_median );

            d[ i ] = old_median;

        }


        std::memcpy( size - half_size + dst, size - half_size + src, half_size * sizeof *src );

        return  dst;

    }

};