maxlevel bug.

maxlevel bug.

#define chunksize %(chunksize)d

#define nb_level %(nb_level)d
#define levelavgsize %(levelavgsize)d
#define calibration %(calibration)8.4f
#define levelstep %(levelstep)8.4f
#define levelmax %(levelmax)d

__kernel void sos_filter(__global  float *input, __global  float *output, __constant  float *coefficients,
            __global float *zi, int direction, int nb_section) {
    /*
    This implement direct form II of lfilter
    https://ccrma.stanford.edu/~jos/fp/Direct_Form_II.html
    input shape is (total_channel, nb_section)
    output shape is (total_channel, nb_section)
    coefficient shape is (total_channel, nb_section, 6)
    zi shape is (total_channel, nb_section, 2)
    */

    int chan = get_global_id(0); //channel indice
    int section = get_global_id(1); //section indice

    int offset_buf = chan*chunksize;
    int offset_filt = chan*nb_section*6; //offset channel
    int offset_filt2;  //offset channel within section
    int offset_zi = chan*nb_section*2;

    // copy channel to local group
    __local float out_channel[chunksize];
    if (section ==0) for (int s=0; s<chunksize;s++) out_channel[s] = input[offset_buf+s];

    float w0, w1,w2;
    float y0;

    w1 = zi[offset_zi+section*2+0];
    w2 = zi[offset_zi+section*2+1];
    int s2;
    for (int s=0; s<chunksize+(3*nb_section);s++){
        barrier(CLK_LOCAL_MEM_FENCE);

        s2 = s-section*3;

        if (s2>=0 && (s2<chunksize)){

            if (direction==-1) s2 = chunksize - s2 - 1;  //this is for bacward

            offset_filt2 = offset_filt+section*6;
            w0 = out_channel[s2];
            w0 -= coefficients[offset_filt2+4] * w1;
            w0 -= coefficients[offset_filt2+5] * w2;
            out_channel[s2] = coefficients[offset_filt2+0] * w0 + coefficients[offset_filt2+1] * w1 +  coefficients[offset_filt2+2] * w2;
            w2 = w1; w1 =w0;
        }
    }
    zi[offset_zi+section*2+0] = w1;
    zi[offset_zi+section*2+1] = w2;

    if (section ==(nb_section-1)){
        for (int s=0; s<chunksize;s++) output[offset_buf+s] = out_channel[s];
    }

}

__kernel void forward_filter(__global  float *input, __global  float *output, __constant  float *coefficients, __global float *zi,  int nb_section){
    sos_filter(input, output, coefficients, zi,  1, nb_section);
}

__kernel void backward_filter(__global  float *input, __global  float *output, __constant  float *coefficients, __global float *zi,  int nb_section) {
    sos_filter(input, output, coefficients, zi,  -1, nb_section);
}

__kernel void estimate_leveldb(__global  float *input, __global  float *outlevels, __global float *previouslevel, __constant float *expdecays, long chunkcount) {

    int chan = get_global_id(0);

    int offset_buf = chan*chunksize;
    int offset_level = chan*levelavgsize;

    int pos = ((chunksize*chunkcount-1)%%levelavgsize);

    float prevlevel = previouslevel[offset_level+pos];
    float avlevel;
    float expdecay = expdecays[chan];

    for (int s=0; s<chunksize;s++) {
        //for (int k=levelavgsize-1; k>0; k--) previouslevel[offset_level+k] = previouslevel[offset_level+k-1];

        prevlevel = max( fabs(input[offset_buf+s]), prevlevel*expdecay);
        pos += 1;
        if (pos == levelavgsize) pos=0;
        previouslevel[offset_level+pos] = prevlevel;

        //average on a window
        avlevel = 0.0;
        for (int k=0; k<levelavgsize; k++) avlevel += (previouslevel[offset_level+k]);
        avlevel /= levelavgsize;
        //avlevel = prevlevel;

        //to dB and index dB
        avlevel = (20*log10(avlevel) + calibration);
        //if (avlevel>=levelmax) avlevel = levelmax-levelstep;
        if (avlevel>levelmax) avlevel = levelmax;
        if (avlevel<0.0f) avlevel = 0.0f;
        //outlevels[offset_buf+s] = avlevel/levelstep;
        outlevels[offset_buf+s] = avlevel;
    }
}

__kernel void dynamic_sos_filter(__global  float *input, __global  float * levels, __global  float *output, __global float *coefficients, __global float *zis, int nb_section) {

    int chan = get_global_id(0); //channel indice
    int section = get_global_id(1); //section indice

    int offset_buf = chan*chunksize;
    int offset_filt = chan*nb_level*nb_section*6;
    int offset_filt2;  //offset channel within section
    int offset_zi = chan*nb_section*2;

    // copy channel to local group
    __local float out_channel[chunksize];
    if (section ==0) for (int s=0; s<chunksize;s++) out_channel[s] = input[offset_buf+s];

    float w0, w1,w2;
    float y0;
    int filterindex;

    w1 = zis[offset_zi+section*2+0];
    w2 = zis[offset_zi+section*2+1];
    int s2;
    for (int s=0; s<chunksize+(3*nb_section);s++){
        barrier(CLK_LOCAL_MEM_FENCE);
        s2 = s-section*3;

        //filtering
        if (s2>=0 && (s2<chunksize)){
            //filterindex = (int) levels[offset_buf+s2];
            filterindex = (int) (levels[offset_buf+s2]/levelstep);

            offset_filt2 = offset_filt+filterindex*nb_section*6+section*6;
            w0 = out_channel[s2];
            w0 -= coefficients[offset_filt2+4] * w1;
            w0 -= coefficients[offset_filt2+5] * w2;
            out_channel[s2] = coefficients[offset_filt2+0] * w0 + coefficients[offset_filt2+1] * w1 +  coefficients[offset_filt2+2] * w2;
            w2 = w1; w1 =w0;
        }
    }
    zis[offset_zi+section*2+0] = w1;
    zis[offset_zi+section*2+1] = w2;

    if (section ==(nb_section-1)){
        for (int s=0; s<chunksize;s++) output[offset_buf+s] = out_channel[s];
    }
}

__kernel void reset_zis(__global  float *zis){
    int chan = get_group_id(0);
    zis[2*chan] = 0;
    zis[2*chan+1] = 0;
}

__kernel void dynamic_gain(__global  float *input, __global  float * levels, __global  float *output, __global float *gain_controlled) {

    int chan = get_global_id(0); //channel indice
    int pos = get_global_id(1); //sample index

    int offset_buf = chan*chunksize;
    int gainindex = (int) (levels[offset_buf+pos]/levelstep);
    output[offset_buf+pos] = input[offset_buf+pos] * gain_controlled[chan*nb_level+gainindex];

}

__kernel void transpose_and_repeat_channel(__global float *inbuffer, __global float *output,  int shape_in_1,   int nb_repeat){

    int r = get_global_id(0);
    int chan = get_global_id(1);

    int offset_out = (chan*nb_repeat*chunksize) + r*chunksize;

    for (int s=0; s<chunksize;s++) output[offset_out+s] = inbuffer[shape_in_1*s+chan];

}

__kernel void bychannel_gain(__global float *inbuffer, __global float *output, __global float *gains){

    int chan = get_global_id(0);
    int pos = get_global_id(1);

    int offset = chan*chunksize + pos;
    output[offset] = inbuffer[offset]*gains[chan];

}

#define chunksize %(chunksize)d

#define nb_level %(nb_level)d
#define levelavgsize %(levelavgsize)d
#define calibration %(calibration)8.4f
#define levelstep %(levelstep)8.4f
#define levelmax %(levelmax)d

__kernel void sos_filter(__global  float *input, __global  float *output, __constant  float *coefficients,
            __global float *zi, int direction, int nb_section) {
    /*
    This implement direct form II of lfilter
    https://ccrma.stanford.edu/~jos/fp/Direct_Form_II.html
    input shape is (total_channel, nb_section)
    output shape is (total_channel, nb_section)
    coefficient shape is (total_channel, nb_section, 6)
    zi shape is (total_channel, nb_section, 2)
    */

    int chan = get_global_id(0); //channel indice
    int section = get_global_id(1); //section indice

    int offset_buf = chan*chunksize;
    int offset_filt = chan*nb_section*6; //offset channel
    int offset_filt2;  //offset channel within section
    int offset_zi = chan*nb_section*2;

    // copy channel to local group
    __local float out_channel[chunksize];
    if (section ==0) for (int s=0; s<chunksize;s++) out_channel[s] = input[offset_buf+s];

    float w0, w1,w2;
    float y0;

    w1 = zi[offset_zi+section*2+0];
    w2 = zi[offset_zi+section*2+1];
    int s2;
    for (int s=0; s<chunksize+(3*nb_section);s++){
        barrier(CLK_LOCAL_MEM_FENCE);

        s2 = s-section*3;

        if (s2>=0 && (s2<chunksize)){

            if (direction==-1) s2 = chunksize - s2 - 1;  //this is for bacward

            offset_filt2 = offset_filt+section*6;
            w0 = out_channel[s2];
            w0 -= coefficients[offset_filt2+4] * w1;
            w0 -= coefficients[offset_filt2+5] * w2;
            out_channel[s2] = coefficients[offset_filt2+0] * w0 + coefficients[offset_filt2+1] * w1 +  coefficients[offset_filt2+2] * w2;
            w2 = w1; w1 =w0;
        }
    }
    zi[offset_zi+section*2+0] = w1;
    zi[offset_zi+section*2+1] = w2;

    if (section ==(nb_section-1)){
        for (int s=0; s<chunksize;s++) output[offset_buf+s] = out_channel[s];
    }

}

__kernel void forward_filter(__global  float *input, __global  float *output, __constant  float *coefficients, __global float *zi,  int nb_section){
    sos_filter(input, output, coefficients, zi,  1, nb_section);
}

__kernel void backward_filter(__global  float *input, __global  float *output, __constant  float *coefficients, __global float *zi,  int nb_section) {
    sos_filter(input, output, coefficients, zi,  -1, nb_section);
}

__kernel void estimate_leveldb(__global  float *input, __global  float *outlevels, __global float *previouslevel, __constant float *expdecays, long chunkcount) {

    int chan = get_global_id(0);

    int offset_buf = chan*chunksize;
    int offset_level = chan*levelavgsize;

    int pos = ((chunksize*chunkcount-1)%%levelavgsize);

    float prevlevel = previouslevel[offset_level+pos];
    float avlevel;
    float expdecay = expdecays[chan];

    for (int s=0; s<chunksize;s++) {
        //for (int k=levelavgsize-1; k>0; k--) previouslevel[offset_level+k] = previouslevel[offset_level+k-1];

        prevlevel = max( fabs(input[offset_buf+s]), prevlevel*expdecay);
        pos += 1;
        if (pos == levelavgsize) pos=0;
        previouslevel[offset_level+pos] = prevlevel;

        //average on a window
        avlevel = 0.0;
        for (int k=0; k<levelavgsize; k++) avlevel += (previouslevel[offset_level+k]);
        avlevel /= levelavgsize;
        //avlevel = prevlevel;

        //to dB and index dB
        avlevel = (20*log10(avlevel) + calibration);
        if (avlevel>=levelmax) avlevel = levelmax-levelstep;
        if (avlevel<0.0f) avlevel = 0.0f;
        //outlevels[offset_buf+s] = avlevel/levelstep;
        outlevels[offset_buf+s] = avlevel;
    }
}

__kernel void dynamic_sos_filter(__global  float *input, __global  float * levels, __global  float *output, __global float *coefficients, __global float *zis, int nb_section) {

    int chan = get_global_id(0); //channel indice
    int section = get_global_id(1); //section indice

    int offset_buf = chan*chunksize;
    int offset_filt = chan*nb_level*nb_section*6;
    int offset_filt2;  //offset channel within section
    int offset_zi = chan*nb_section*2;

    // copy channel to local group
    __local float out_channel[chunksize];
    if (section ==0) for (int s=0; s<chunksize;s++) out_channel[s] = input[offset_buf+s];

    float w0, w1,w2;
    float y0;
    int filterindex;

    w1 = zis[offset_zi+section*2+0];
    w2 = zis[offset_zi+section*2+1];
    int s2;
    for (int s=0; s<chunksize+(3*nb_section);s++){
        barrier(CLK_LOCAL_MEM_FENCE);
        s2 = s-section*3;

        //filtering
        if (s2>=0 && (s2<chunksize)){
            //filterindex = (int) levels[offset_buf+s2];
            filterindex = (int) (levels[offset_buf+s2]/levelstep);

            offset_filt2 = offset_filt+filterindex*nb_section*6+section*6;
            w0 = out_channel[s2];
            w0 -= coefficients[offset_filt2+4] * w1;
            w0 -= coefficients[offset_filt2+5] * w2;
            out_channel[s2] = coefficients[offset_filt2+0] * w0 + coefficients[offset_filt2+1] * w1 +  coefficients[offset_filt2+2] * w2;
            w2 = w1; w1 =w0;
        }
    }
    zis[offset_zi+section*2+0] = w1;
    zis[offset_zi+section*2+1] = w2;

    if (section ==(nb_section-1)){
        for (int s=0; s<chunksize;s++) output[offset_buf+s] = out_channel[s];
    }
}

__kernel void reset_zis(__global  float *zis){
    int chan = get_group_id(0);
    zis[2*chan] = 0;
    zis[2*chan+1] = 0;
}

__kernel void dynamic_gain(__global  float *input, __global  float * levels, __global  float *output, __global float *gain_controlled) {

    int chan = get_global_id(0); //channel indice
    int pos = get_global_id(1); //sample index

    int offset_buf = chan*chunksize;
    int gainindex = (int) (levels[offset_buf+pos]/levelstep);
    output[offset_buf+pos] = input[offset_buf+pos] * gain_controlled[chan*nb_level+gainindex];

}

__kernel void transpose_and_repeat_channel(__global float *inbuffer, __global float *output,  int shape_in_1,   int nb_repeat){

    int r = get_global_id(0);
    int chan = get_global_id(1);

    int offset_out = (chan*nb_repeat*chunksize) + r*chunksize;

    for (int s=0; s<chunksize;s++) output[offset_out+s] = inbuffer[shape_in_1*s+chan];

}

__kernel void bychannel_gain(__global float *inbuffer, __global float *output, __global float *gains){

    int chan = get_global_id(0);
    int pos = get_global_id(1);

    int offset = chan*chunksize + pos;
    output[offset] = inbuffer[offset]*gains[chan];

}


OpenCL implementation of RANLUX PRNG

OpenCL implementation of RANLUX PRNG

/******************************************************************************
 * @file     prngcl_ranlux.cl
 * @author   Vadim Demchik <vadimdi@yahoo.com>
 * @version  1.1.1
 *
 * @brief    [PRNGCL library]
 *           contains OpenCL implementation of RANLUX pseudo-random number generator
 *
 *
 * @section  CREDITS
 *
 *   Martin Luscher,
 *   "A portable high-quality random number generator for lattice field theory simulations",
 *   Computer Physics Communications, 79 (1994), 100--110.
 *
 *   F. James,
 *   "RANLUX: A Fortran implementation of the high-quality pseudorandom number generator of Luscher",
 *   Computer Physics Communications, 79 (1994), 111--114.
 *
 *
 * @section  LICENSE
 *
 * Copyright (c) 2013, 2014 Vadim Demchik
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 *    Redistributions of source code must retain the above copyright notice,
 *      this list of conditions and the following disclaimer.
 *
 *    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.
 *
 * 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.
 *
 *****************************************************************************/

#ifndef PRNGCL_RANLUX_CL
#define PRNGCL_RANLUX_CL

#include "prngcl_common.cl"

#define RL_min    (1.0/16777216.0)
#define RL_max    (16777215.0/16777216.0)
#define RL_k      (5.9604644775390625E-8) // 1/2^24

#define RL_zero     0.0f
#define RL_icons    2147483563
#define RL_itwo24   16777216 // 1<<24
#define RL_twom24   0.000000059604644775390625f
#define RL_twom12   0.000244140625f
#define RL_twom24sq RL_twom24*RL_twom24

#ifndef RL_skip
#define RL_skip     223
#endif

#define RL_skip_24 (RL_skip / 24)
#if RL_skip == 97
#define RL_RANLUX2
#define RL_skip_4   0
#define RL_skip_1   0
#endif
#if RL_skip == 223
#define RL_RANLUX3
#define RL_skip_4   0
#define RL_skip_1   0
#endif
#if RL_skip == 389
#define RL_RANLUX4
#define RL_skip_4   0
#define RL_skip_1   0
#endif
#ifndef RL_skip_4
#define RL_skip_4  ((RL_skip - RL_skip_24 * 24) / 4)
#endif
#ifndef RL_skip_1
#define RL_skip_1  (RL_skip % 4)
#endif

//________________________________________________________________________________________________________ RANLUX PRNG
__attribute__((always_inline)) int4
rlseedint(int RL_jseed)
{
	int4 RL_output_int;
		int k = RL_jseed / 53668;
		RL_jseed = 40014 * (RL_jseed - k * 53668) - k * 12211;
		if (RL_jseed < 0) {RL_jseed = RL_jseed + RL_icons;}
		RL_output_int.x = RL_jseed;

		k = RL_jseed / 53668;
		RL_jseed = 40014 * (RL_jseed - k * 53668) - k * 12211;
		if (RL_jseed < 0) {RL_jseed = RL_jseed + RL_icons;}
		RL_output_int.y = RL_jseed;

		k = RL_jseed / 53668;
		RL_jseed = 40014 * (RL_jseed - k * 53668) - k * 12211;
		if (RL_jseed < 0) {RL_jseed = RL_jseed + RL_icons;}
		RL_output_int.z = RL_jseed;

		k = RL_jseed / 53668;
		RL_jseed = 40014 * (RL_jseed - k * 53668) - k * 12211;
		if (RL_jseed < 0) {RL_jseed = RL_jseed + RL_icons;}
		RL_output_int.w = RL_jseed;

    return RL_output_int;
}

__attribute__((always_inline)) float4
rlseedint2(int4 RL_temp)
{
	float4 RL_output_int;
	float4 RL_tmp = (float4) (RL_twom24,RL_twom24,RL_twom24,RL_twom24);
		RL_output_int.x = ((float) (RL_temp.x % RL_itwo24));
		RL_output_int.y = ((float) (RL_temp.y % RL_itwo24));
		RL_output_int.z = ((float) (RL_temp.z % RL_itwo24));
		RL_output_int.w = ((float) (RL_temp.w % RL_itwo24));
	RL_output_int = RL_output_int * RL_tmp;
    return RL_output_int;
}

__kernel void
ranlux_init(const __global uint * seeds, __global float4 * seedtable)
{
    int RL_jseed = (int) seeds[GID];
	int4		RL_temp1 = rlseedint(RL_jseed);
	seedtable[GID + 0 * GID_SIZE] = rlseedint2(RL_temp1);
				RL_temp1 = rlseedint(RL_temp1.w);
	seedtable[GID + 1 * GID_SIZE] = rlseedint2(RL_temp1);
				RL_temp1 = rlseedint(RL_temp1.w);
	seedtable[GID + 2 * GID_SIZE] = rlseedint2(RL_temp1);
				RL_temp1 = rlseedint(RL_temp1.w);
	seedtable[GID + 3 * GID_SIZE] = rlseedint2(RL_temp1);
				RL_temp1 = rlseedint(RL_temp1.w);
	seedtable[GID + 4 * GID_SIZE] = rlseedint2(RL_temp1);
				RL_temp1 = rlseedint(RL_temp1.w);
	float4		RL_temp2 = rlseedint2(RL_temp1);
	seedtable[GID + 5 * GID_SIZE] = RL_temp2;
	float4		RL_carin = (float4) (23.0f, 9.0f, 6.0f, 0.0f);
	if (RL_temp2.w == 0) {RL_carin.w = RL_twom24;}
	seedtable[GID + 6 * GID_SIZE] = RL_carin; // RL_i24, RL_j24, RL_in24, RL_carry
}

__attribute__((always_inline)) float4
rlproducefloat4(float4 * RL_seeds_i24, const float4 RL_seeds_j24, const float RL_seeds_j24P, float * RL_carry)
{
	float RL_c = *RL_carry;
	float4 RL_output_int = RL_seeds_j24 - (*RL_seeds_i24).wzyx;
	RL_output_int.x = RL_output_int.x - RL_c;
		if (RL_output_int.x < 0.0f) { RL_output_int.x = RL_output_int.x + 1.0f; RL_c = RL_twom24; } else { RL_c = 0.0f; }
	RL_output_int.y = RL_output_int.y - RL_c;
		if (RL_output_int.y < 0.0f) { RL_output_int.y = RL_output_int.y + 1.0f; RL_c = RL_twom24; } else { RL_c = 0.0f; }
	RL_output_int.z = RL_output_int.z - RL_c;
		if (RL_output_int.z < 0.0f) { RL_output_int.z = RL_output_int.z + 1.0f; RL_c = RL_twom24; } else { RL_c = 0.0f; }
	RL_output_int.w = RL_output_int.w - RL_c;
		if (RL_output_int.w < 0.0f) { RL_output_int.w = RL_output_int.w + 1.0f; RL_c = RL_twom24; } else { RL_c = 0.0f; }

	*RL_seeds_i24 = RL_output_int.wzyx;

    float4 RL_temporary = RL_seeds_j24.yzwx;
    RL_temporary.w = RL_seeds_j24P;

	float4 RL_output_int2 = select(RL_output_int, (RL_output_int + RL_twom24 * RL_temporary), RL_output_int < (float4) RL_twom12);
			RL_output_int = select(RL_output_int2, RL_twom24sq, RL_output_int2 == (float4) 0.0f);

	*RL_carry = RL_c;
	return RL_output_int;
}

__attribute__((always_inline)) void
rlproduceupd4(float4 * RL_seeds_i24, const float4 RL_seeds_j24, float * RL_carry)
{
	float RL_c = *RL_carry;
	float4 RL_output_int = RL_seeds_j24 - (*RL_seeds_i24).wzyx;
	RL_output_int.x = RL_output_int.x - RL_c;
		if (RL_output_int.x < 0.0f) { RL_output_int.x = RL_output_int.x + 1.0f; RL_c = RL_twom24; } else { RL_c = 0.0f; }
	RL_output_int.y = RL_output_int.y - RL_c;
		if (RL_output_int.y < 0.0f) { RL_output_int.y = RL_output_int.y + 1.0f; RL_c = RL_twom24; } else { RL_c = 0.0f; }
	RL_output_int.z = RL_output_int.z - RL_c;
		if (RL_output_int.z < 0.0f) { RL_output_int.z = RL_output_int.z + 1.0f; RL_c = RL_twom24; } else { RL_c = 0.0f; }
	RL_output_int.w = RL_output_int.w - RL_c;
		if (RL_output_int.w < 0.0f) { RL_output_int.w = RL_output_int.w + 1.0f; RL_c = RL_twom24; } else { RL_c = 0.0f; }

	*RL_seeds_i24 = RL_output_int.wzyx;
	*RL_carry = RL_c;
}

__attribute__((always_inline)) float4
rl_step(float4* RL_seed0,float4* RL_seed1,float4* RL_seed2,float4* RL_seed3,
        float4* RL_seed4,float4* RL_seed5,float4* RL_carin)
{
	float4 RL_seeds_j24, uni, RL_seed_temp, RL_seed_temp2;
 	float RL_carry = (*RL_carin).w;
		if ((*RL_carin).z >= 6.0f) {
			for (int t=0; t<RL_skip_24; t++) {
				RL_seeds_j24.xy = (*RL_seed2).yx;
				RL_seeds_j24.zw = (*RL_seed1).wz;
					rlproduceupd4(RL_seed5,RL_seeds_j24,&RL_carry);
				RL_seeds_j24.xy = (*RL_seed1).yx;
				RL_seeds_j24.zw = (*RL_seed0).wz;
					rlproduceupd4(RL_seed4,RL_seeds_j24,&RL_carry);
				RL_seeds_j24.xy = (*RL_seed0).yx;
				RL_seeds_j24.zw = (*RL_seed5).wz;
					rlproduceupd4(RL_seed3,RL_seeds_j24,&RL_carry);
				RL_seeds_j24.xy = (*RL_seed5).yx;
				RL_seeds_j24.zw = (*RL_seed4).wz;
					rlproduceupd4(RL_seed2,RL_seeds_j24,&RL_carry);
				RL_seeds_j24.xy = (*RL_seed4).yx;
				RL_seeds_j24.zw = (*RL_seed3).wz;
					rlproduceupd4(RL_seed1,RL_seeds_j24,&RL_carry);
				RL_seeds_j24.xy = (*RL_seed3).yx;
				RL_seeds_j24.zw = (*RL_seed2).wz;
					rlproduceupd4(RL_seed0,RL_seeds_j24,&RL_carry);
			}
#if RL_skip_4 == 1
				RL_seeds_j24.xy = (*RL_seed2).yx;
				RL_seeds_j24.zw = (*RL_seed1).wz;
					rlproduceupd4(RL_seed5,RL_seeds_j24,&RL_carry);
                RL_seed_temp = (*RL_seed5);    // 5->4, 4->3, 3->2, 2->1, 1->0, 0->5
                (*RL_seed5) = (*RL_seed4);
                (*RL_seed4) = (*RL_seed3);
                (*RL_seed3) = (*RL_seed2);
                (*RL_seed2) = (*RL_seed1);
                (*RL_seed1) = (*RL_seed0);
                (*RL_seed0) = RL_seed_temp;
#elif RL_skip_4 == 2
				RL_seeds_j24.xy = (*RL_seed2).yx;
				RL_seeds_j24.zw = (*RL_seed1).wz;
					rlproduceupd4(RL_seed5,RL_seeds_j24,&RL_carry);
				RL_seeds_j24.xy = (*RL_seed1).yx;
				RL_seeds_j24.zw = (*RL_seed0).wz;
					rlproduceupd4(RL_seed4,RL_seeds_j24,&RL_carry);
                RL_seed_temp = (*RL_seed5);    // 5->3, 4->2, 3->1, 2->0, 1->5, 0->4
                (*RL_seed5) = (*RL_seed3);
                (*RL_seed3) = (*RL_seed1);
                (*RL_seed1) = RL_seed_temp;
                RL_seed_temp = (*RL_seed2);
                (*RL_seed2) = (*RL_seed0);
                (*RL_seed0) = (*RL_seed4);
                (*RL_seed4) = RL_seed_temp;
#elif RL_skip_4 == 3
				RL_seeds_j24.xy = (*RL_seed2).yx;
				RL_seeds_j24.zw = (*RL_seed1).wz;
					rlproduceupd4(RL_seed5,RL_seeds_j24,&RL_carry);
				RL_seeds_j24.xy = (*RL_seed1).yx;
				RL_seeds_j24.zw = (*RL_seed0).wz;
					rlproduceupd4(RL_seed4,RL_seeds_j24,&RL_carry);
				RL_seeds_j24.xy = (*RL_seed0).yx;
				RL_seeds_j24.zw = (*RL_seed5).wz;
					rlproduceupd4(RL_seed3,RL_seeds_j24,&RL_carry);
                RL_seed_temp = (*RL_seed5);    // 5->2, 4->1, 3->0, 2->5, 1->4, 0->3
                (*RL_seed5) = (*RL_seed2);
                (*RL_seed2) = RL_seed_temp;
                RL_seed_temp = (*RL_seed4);
                (*RL_seed4) = (*RL_seed1);
                (*RL_seed1) = RL_seed_temp;
                RL_seed_temp = (*RL_seed3);
                (*RL_seed3) = (*RL_seed0);
                (*RL_seed0) = RL_seed_temp;
#elif RL_skip_4 == 4
				RL_seeds_j24.xy = (*RL_seed2).yx;
				RL_seeds_j24.zw = (*RL_seed1).wz;
					rlproduceupd4(RL_seed5,RL_seeds_j24,&RL_carry);
				RL_seeds_j24.xy = (*RL_seed1).yx;
				RL_seeds_j24.zw = (*RL_seed0).wz;
					rlproduceupd4(RL_seed4,RL_seeds_j24,&RL_carry);
				RL_seeds_j24.xy = (*RL_seed0).yx;
				RL_seeds_j24.zw = (*RL_seed5).wz;
					rlproduceupd4(RL_seed3,RL_seeds_j24,&RL_carry);
				RL_seeds_j24.xy = (*RL_seed5).yx;
				RL_seeds_j24.zw = (*RL_seed4).wz;
					rlproduceupd4(RL_seed2,RL_seeds_j24,&RL_carry);
                RL_seed_temp = (*RL_seed5);    // 5->1, 4->0, 3->5, 2->4, 1->3, 0->2
                (*RL_seed5) = (*RL_seed1);
                (*RL_seed1) = (*RL_seed3);
                (*RL_seed3) = RL_seed_temp;
                RL_seed_temp = (*RL_seed4);
                (*RL_seed4) = (*RL_seed0);
                (*RL_seed0) = (*RL_seed2);
                (*RL_seed2) = RL_seed_temp;
#elif RL_skip_4 == 5
				RL_seeds_j24.xy = (*RL_seed2).yx;
				RL_seeds_j24.zw = (*RL_seed1).wz;
					rlproduceupd4(RL_seed5,RL_seeds_j24,&RL_carry);
				RL_seeds_j24.xy = (*RL_seed1).yx;
				RL_seeds_j24.zw = (*RL_seed0).wz;
					rlproduceupd4(RL_seed4,RL_seeds_j24,&RL_carry);
				RL_seeds_j24.xy = (*RL_seed0).yx;
				RL_seeds_j24.zw = (*RL_seed5).wz;
					rlproduceupd4(RL_seed3,RL_seeds_j24,&RL_carry);
				RL_seeds_j24.xy = (*RL_seed5).yx;
				RL_seeds_j24.zw = (*RL_seed4).wz;
					rlproduceupd4(RL_seed2,RL_seeds_j24,&RL_carry);
				RL_seeds_j24.xy = (*RL_seed4).yx;
				RL_seeds_j24.zw = (*RL_seed3).wz;
					rlproduceupd4(RL_seed1,RL_seeds_j24,&RL_carry);
                RL_seed_temp = (*RL_seed5);    // 5->0, 4->5, 3->4, 2->3, 1->2, 0->1
                (*RL_seed5) = (*RL_seed0);
                (*RL_seed0) = (*RL_seed1);
                (*RL_seed1) = (*RL_seed2);
                (*RL_seed2) = (*RL_seed3);
                (*RL_seed3) = (*RL_seed4);
                (*RL_seed4) = RL_seed_temp;
#endif
#if RL_skip_1 == 1
                float RL_output_int = (*RL_seed2).y - (*RL_seed5).w;
                RL_output_int = RL_output_int - RL_carry;
                    if (RL_output_int < 0.0f) { RL_output_int = RL_output_int + 1.0f; RL_carry = RL_twom24; } else { RL_carry = 0.0f; }
                (*RL_seed5).w = RL_output_int;

                RL_seed_temp = (*RL_seed5);
                (*RL_seed5).yzw = (*RL_seed5).xyz;
                (*RL_seed5).x = (*RL_seed4).w;
                (*RL_seed4).yzw = (*RL_seed4).xyz;
                (*RL_seed4).x = (*RL_seed3).w;
                (*RL_seed3).yzw = (*RL_seed3).xyz;
                (*RL_seed3).x = (*RL_seed2).w;
                (*RL_seed2).yzw = (*RL_seed2).xyz;
                (*RL_seed2).x = (*RL_seed1).w;
                (*RL_seed1).yzw = (*RL_seed1).xyz;
                (*RL_seed1).x = (*RL_seed0).w;
                (*RL_seed0).yzw = (*RL_seed0).xyz;
                (*RL_seed0).x = RL_seed_temp.w;
#elif RL_skip_1 == 2
                float4 RL_output_int;
                RL_output_int.xy = (*RL_seed2).yx - (*RL_seed5).wz;
                RL_output_int.x = RL_output_int.x - RL_carry;
                    if (RL_output_int.x < 0.0f) { RL_output_int.x = RL_output_int.x + 1.0f; RL_carry = RL_twom24; } else { RL_carry = 0.0f; }
                RL_output_int.y = RL_output_int.y - RL_carry;
                    if (RL_output_int.y < 0.0f) { RL_output_int.y = RL_output_int.y + 1.0f; RL_carry = RL_twom24; } else { RL_carry = 0.0f; }

                (*RL_seed5).zw = RL_output_int.yx;

                RL_seed_temp = (*RL_seed5);
                (*RL_seed5).zw = (*RL_seed5).xy;
                (*RL_seed5).xy = (*RL_seed4).zw;
                (*RL_seed4).zw = (*RL_seed4).xy;
                (*RL_seed4).xy = (*RL_seed3).zw;
                (*RL_seed3).zw = (*RL_seed3).xy;
                (*RL_seed3).xy = (*RL_seed2).zw;
                (*RL_seed2).zw = (*RL_seed2).xy;
                (*RL_seed2).xy = (*RL_seed1).zw;
                (*RL_seed1).zw = (*RL_seed1).xy;
                (*RL_seed1).xy = (*RL_seed0).zw;
                (*RL_seed0).zw = (*RL_seed0).xy;
                (*RL_seed0).xy = RL_seed_temp.zw;
#elif RL_skip_1 == 3
                float4 RL_output_int;
                RL_output_int.xy = (*RL_seed2).yx - (*RL_seed5).wz;
                RL_output_int.z = (*RL_seed1).w - (*RL_seed5).y;
                RL_output_int.x = RL_output_int.x - RL_carry;
                    if (RL_output_int.x < 0.0f) { RL_output_int.x = RL_output_int.x + 1.0f; RL_carry = RL_twom24; } else { RL_carry = 0.0f; }
                RL_output_int.y = RL_output_int.y - RL_carry;
                    if (RL_output_int.y < 0.0f) { RL_output_int.y = RL_output_int.y + 1.0f; RL_carry = RL_twom24; } else { RL_carry = 0.0f; }
                RL_output_int.z = RL_output_int.z - RL_carry;
                    if (RL_output_int.z < 0.0f) { RL_output_int.z = RL_output_int.z + 1.0f; RL_carry = RL_twom24; } else { RL_carry = 0.0f; }

                (*RL_seed5).yzw = RL_output_int.zyx;

                RL_seed_temp = (*RL_seed5);
                (*RL_seed5).w = (*RL_seed5).x;
                (*RL_seed5).xyz = (*RL_seed4).yzw;
                (*RL_seed4).w = (*RL_seed4).x;
                (*RL_seed4).xyz = (*RL_seed3).yzw;
                (*RL_seed3).w = (*RL_seed3).x;
                (*RL_seed3).xyz = (*RL_seed2).yzw;
                (*RL_seed2).w = (*RL_seed2).x;
                (*RL_seed2).xyz = (*RL_seed1).yzw;
                (*RL_seed1).w = (*RL_seed1).x;
                (*RL_seed1).xyz = (*RL_seed0).yzw;
                (*RL_seed0).w = (*RL_seed0).x;
                (*RL_seed0).xyz = RL_seed_temp.yzw;
#endif
#ifdef RL_RANLUX2
                float RL_output_int = (*RL_seed2).y - (*RL_seed5).w;
                RL_output_int = RL_output_int - RL_carry;
                    if (RL_output_int < 0.0f) { RL_output_int = RL_output_int + 1.0f; RL_carry = RL_twom24; } else { RL_carry = 0.0f; }
                (*RL_seed5).w = RL_output_int;

                RL_seed_temp = (*RL_seed5);
                (*RL_seed5).yzw = (*RL_seed5).xyz;
                (*RL_seed5).x = (*RL_seed4).w;
                (*RL_seed4).yzw = (*RL_seed4).xyz;
                (*RL_seed4).x = (*RL_seed3).w;
                (*RL_seed3).yzw = (*RL_seed3).xyz;
                (*RL_seed3).x = (*RL_seed2).w;
                (*RL_seed2).yzw = (*RL_seed2).xyz;
                (*RL_seed2).x = (*RL_seed1).w;
                (*RL_seed1).yzw = (*RL_seed1).xyz;
                (*RL_seed1).x = (*RL_seed0).w;
                (*RL_seed0).yzw = (*RL_seed0).xyz;
                (*RL_seed0).x = RL_seed_temp.w;
#endif
#ifdef RL_RANLUX3
                RL_seeds_j24.xy = (*RL_seed2).yx;
                RL_seeds_j24.zw = (*RL_seed1).wz;
                    rlproduceupd4(RL_seed5,RL_seeds_j24,&RL_carry);
                float4 RL_output_int;
                RL_output_int.xy = (*RL_seed1).yx - (*RL_seed4).wz;
                RL_output_int.z = (*RL_seed0).w - (*RL_seed4).y;
                RL_output_int.x = RL_output_int.x - RL_carry;
                    if (RL_output_int.x < 0.0f) { RL_output_int.x = RL_output_int.x + 1.0f; RL_carry = RL_twom24; } else { RL_carry = 0.0f; }
                RL_output_int.y = RL_output_int.y - RL_carry;
                    if (RL_output_int.y < 0.0f) { RL_output_int.y = RL_output_int.y + 1.0f; RL_carry = RL_twom24; } else { RL_carry = 0.0f; }
                RL_output_int.z = RL_output_int.z - RL_carry;
                    if (RL_output_int.z < 0.0f) { RL_output_int.z = RL_output_int.z + 1.0f; RL_carry = RL_twom24; } else { RL_carry = 0.0f; }

                RL_seed_temp.xyz  = RL_output_int.zyx;
                RL_seed_temp.w    = (*RL_seed5).x;
                RL_seed_temp2.xyz = (*RL_seed5).yzw;
                RL_seed_temp2.w   = (*RL_seed0).x;

                (*RL_seed5).w   = (*RL_seed4).x;
                (*RL_seed5).xyz = (*RL_seed3).yzw;
                (*RL_seed4).w   = (*RL_seed3).x;
                (*RL_seed4).xyz = (*RL_seed2).yzw;
                (*RL_seed3).w   = (*RL_seed2).x;
                (*RL_seed3).xyz = (*RL_seed1).yzw;
                (*RL_seed2).w   = (*RL_seed1).x;
                (*RL_seed2).xyz = (*RL_seed0).yzw;
                (*RL_seed1)  = RL_seed_temp2;
                (*RL_seed0)  = RL_seed_temp;
#endif
#ifdef RL_RANLUX4
                RL_seeds_j24.xy = (*RL_seed2).yx;
                RL_seeds_j24.zw = (*RL_seed1).wz;
                    rlproduceupd4(RL_seed5,RL_seeds_j24,&RL_carry);
                float RL_output_int = (*RL_seed1).y - (*RL_seed4).w;
                RL_output_int = RL_output_int - RL_carry;
                    if (RL_output_int < 0.0f) { RL_output_int = RL_output_int + 1.0f; RL_carry = RL_twom24; } else { RL_carry = 0.0f; }

                RL_seed_temp  = (*RL_seed5);
                RL_seed_temp2.x = RL_output_int;

                (*RL_seed5).yzw = (*RL_seed4).xyz;
                (*RL_seed5).x   = (*RL_seed3).w;
                (*RL_seed4).yzw = (*RL_seed3).xyz;
                (*RL_seed4).x   = (*RL_seed2).w;
                (*RL_seed3).yzw = (*RL_seed2).xyz;
                (*RL_seed3).x   = (*RL_seed1).w;
                (*RL_seed2).yzw = (*RL_seed1).xyz;
                (*RL_seed2).x   = (*RL_seed0).w;
                (*RL_seed1).yzw = (*RL_seed0).xyz;
                (*RL_seed1).x   = RL_seed_temp.w;
                (*RL_seed0).yzw = RL_seed_temp.xyz;
                (*RL_seed0).x   = RL_seed_temp2.x;
#endif
            (*RL_carin).z = 0.0f;
		}
        if ((*RL_carin).z < 1.0f) {
			RL_seeds_j24.xy = (*RL_seed2).yx;
			RL_seeds_j24.zw = (*RL_seed1).wz;
				uni = rlproducefloat4(RL_seed5,RL_seeds_j24,(*RL_seed1).y,&RL_carry);
			(*RL_carin).z = 1.0f;
		} else if ((*RL_carin).z == 1.0f) {
			RL_seeds_j24.xy = (*RL_seed1).yx;
			RL_seeds_j24.zw = (*RL_seed0).wz;
				uni = rlproducefloat4(RL_seed4,RL_seeds_j24,(*RL_seed0).y,&RL_carry);
			(*RL_carin).z = 2.0f;
		} else if ((*RL_carin).z == 2.0f) {
			RL_seeds_j24.xy = (*RL_seed0).yx;
			RL_seeds_j24.zw = (*RL_seed5).wz;
				uni = rlproducefloat4(RL_seed3,RL_seeds_j24,(*RL_seed5).y,&RL_carry);
			(*RL_carin).z = 3.0f;
		} else if ((*RL_carin).z == 3.0f) {
			RL_seeds_j24.xy = (*RL_seed5).yx;
			RL_seeds_j24.zw = (*RL_seed4).wz;
				uni = rlproducefloat4(RL_seed2,RL_seeds_j24,(*RL_seed4).y,&RL_carry);
			(*RL_carin).z = 4.0f;
		} else if ((*RL_carin).z == 4.0f) {
			RL_seeds_j24.xy = (*RL_seed4).yx;
			RL_seeds_j24.zw = (*RL_seed3).wz;
				uni = rlproducefloat4(RL_seed1,RL_seeds_j24,(*RL_seed3).y,&RL_carry);
			(*RL_carin).z = 5.0f;
		} else if ((*RL_carin).z == 5.0f) {
			RL_seeds_j24.xy = (*RL_seed3).yx;
			RL_seeds_j24.zw = (*RL_seed2).wz;
				uni = rlproducefloat4(RL_seed0,RL_seeds_j24,(*RL_seed2).y,&RL_carry);
			(*RL_carin).z = 6.0f;
		}
	(*RL_carin).w = RL_carry;
    return uni;
}

#ifdef PRECISION_DOUBLE  // if double precision is defined
__attribute__((always_inline)) hgpu_double4
rl_step_double(float4* RL_seed0,float4* RL_seed1,float4* RL_seed2,float4* RL_seed3,
               float4* RL_seed4,float4* RL_seed5,float4* RL_carin)
{
    hgpu_double4 result;
    hgpu_double2 rnd_double;
    float4 rnd1 = (float4) 0.0;
    float4 rnd2 = (float4) 0.0;
#ifndef PRNG_SKIP_CHECK
        while ((rnd1.x <= RL_min) || (rnd1.x >= RL_max) ||
               (rnd1.z <= RL_min) || (rnd1.z >= RL_max)
              )
#endif
            rnd1 = rl_step(RL_seed0,RL_seed1,RL_seed2,RL_seed3,RL_seed4,RL_seed5,RL_carin);
        rnd_double = hgpu_float4_to_double2(rnd1,RL_min,RL_max,RL_k);
        result.x = rnd_double.x;
        result.y = rnd_double.y;

#ifndef PRNG_SKIP_CHECK
        while ((rnd2.x <= RL_min) || (rnd2.x >= RL_max) ||
               (rnd2.z <= RL_min) || (rnd2.z >= RL_max)
              )
#endif
            rnd2 = rl_step(RL_seed0,RL_seed1,RL_seed2,RL_seed3,RL_seed4,RL_seed5,RL_carin);
        rnd_double = hgpu_float4_to_double2(rnd2,RL_min,RL_max,RL_k);
        result.z = rnd_double.x;
        result.w = rnd_double.y;

    return result;
}
#endif

__kernel void
ranlux(__global float4 * seedtable,__global hgpu_float4 * prns, const uint samples)
{
    uint giddst = GID;
		float4 RL_seed0 = seedtable[GID + 0 * GID_SIZE];
		float4 RL_seed1 = seedtable[GID + 1 * GID_SIZE];
		float4 RL_seed2 = seedtable[GID + 2 * GID_SIZE];
		float4 RL_seed3 = seedtable[GID + 3 * GID_SIZE];
		float4 RL_seed4 = seedtable[GID + 4 * GID_SIZE];
		float4 RL_seed5 = seedtable[GID + 5 * GID_SIZE];
		float4 RL_carin = seedtable[GID + 6 * GID_SIZE];    // RL_i24, RL_j24, RL_in24, RL_carry

	for (int i=0; i<samples; i++) {
#ifdef PRECISION_DOUBLE // if double precision is defined
        hgpu_double4 result = rl_step_double(&RL_seed0,&RL_seed1,&RL_seed2,&RL_seed3,&RL_seed4,&RL_seed5,&RL_carin);
        prns[giddst] = result;
#else
        float4 uni = rl_step(&RL_seed0,&RL_seed1,&RL_seed2,&RL_seed3,&RL_seed4,&RL_seed5,&RL_carin);
		prns[giddst] = uni;
#endif
		giddst += GID_SIZE;
	}

	seedtable[GID + 0 * GID_SIZE] = RL_seed0;
	seedtable[GID + 1 * GID_SIZE] = RL_seed1;
	seedtable[GID + 2 * GID_SIZE] = RL_seed2;
	seedtable[GID + 3 * GID_SIZE] = RL_seed3;
	seedtable[GID + 4 * GID_SIZE] = RL_seed4;
	seedtable[GID + 5 * GID_SIZE] = RL_seed5;
	seedtable[GID + 6 * GID_SIZE] = RL_carin;
}

#endif

/******************************************************************************
 * @file     prngcl_ranlux.cl
 * @author   Vadim Demchik <vadimdi@yahoo.com>
 * @version  1.1.1
 *
 * @brief    [PRNGCL library]
 *           contains OpenCL implementation of RANLUX pseudo-random number generator
 *
 *
 * @section  CREDITS
 *
 *   Martin Luscher,
 *   "A portable high-quality random number generator for lattice field theory simulations",
 *   Computer Physics Communications, 79 (1994), 100--110.
 *
 *   F. James,
 *   "RANLUX: A Fortran implementation of the high-quality pseudorandom number generator of Luscher",
 *   Computer Physics Communications, 79 (1994), 111--114.
 *
 *
 * @section  LICENSE
 *
 * Copyright (c) 2013, 2014 Vadim Demchik
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 *    Redistributions of source code must retain the above copyright notice,
 *      this list of conditions and the following disclaimer.
 *
 *    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.
 *
 * 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.
 *
 *****************************************************************************/

#ifndef PRNGCL_RANLUX_CL
#define PRNGCL_RANLUX_CL

#include "prngcl_common.cl"

#define RL_min    (1.0/16777216.0)
#define RL_max    (16777215.0/16777216.0)
#define RL_k      (5.9604644775390625E-8) // 1/2^24

#define RL_left   (RL_min+RL_k*RL_max)
#define RL_right  (RL_max+RL_k*RL_min)

#define RL_zero     0.0f
#define RL_icons    2147483563
#define RL_itwo24   16777216 // 1<<24
#define RL_twom24   0.000000059604644775390625f
#define RL_twom12   0.000244140625f
#define RL_twom24sq RL_twom24*RL_twom24

#ifndef RL_skip
#define RL_skip     223
#endif

#define RL_skip_24 (RL_skip / 24)
#if RL_skip == 97
#define RL_RANLUX2
#define RL_skip_4   0
#define RL_skip_1   0
#endif
#if RL_skip == 223
#define RL_RANLUX3
#define RL_skip_4   0
#define RL_skip_1   0
#endif
#if RL_skip == 389
#define RL_RANLUX4
#define RL_skip_4   0
#define RL_skip_1   0
#endif
#ifndef RL_skip_4
#define RL_skip_4  ((RL_skip - RL_skip_24 * 24) / 4)
#endif
#ifndef RL_skip_1
#define RL_skip_1  (RL_skip % 4)
#endif

//________________________________________________________________________________________________________ RANLUX PRNG
__attribute__((always_inline)) int4
rlseedint(int RL_jseed)
{
	int4 RL_output_int;
		int k = RL_jseed / 53668;
		RL_jseed = 40014 * (RL_jseed - k * 53668) - k * 12211;
		if (RL_jseed < 0) {RL_jseed = RL_jseed + RL_icons;}
		RL_output_int.x = RL_jseed;

		k = RL_jseed / 53668;
		RL_jseed = 40014 * (RL_jseed - k * 53668) - k * 12211;
		if (RL_jseed < 0) {RL_jseed = RL_jseed + RL_icons;}
		RL_output_int.y = RL_jseed;

		k = RL_jseed / 53668;
		RL_jseed = 40014 * (RL_jseed - k * 53668) - k * 12211;
		if (RL_jseed < 0) {RL_jseed = RL_jseed + RL_icons;}
		RL_output_int.z = RL_jseed;

		k = RL_jseed / 53668;
		RL_jseed = 40014 * (RL_jseed - k * 53668) - k * 12211;
		if (RL_jseed < 0) {RL_jseed = RL_jseed + RL_icons;}
		RL_output_int.w = RL_jseed;

    return RL_output_int;
}

__attribute__((always_inline)) float4
rlseedint2(int4 RL_temp)
{
	float4 RL_output_int;
	float4 RL_tmp = (float4) (RL_twom24,RL_twom24,RL_twom24,RL_twom24);
		RL_output_int.x = ((float) (RL_temp.x % RL_itwo24));
		RL_output_int.y = ((float) (RL_temp.y % RL_itwo24));
		RL_output_int.z = ((float) (RL_temp.z % RL_itwo24));
		RL_output_int.w = ((float) (RL_temp.w % RL_itwo24));
	RL_output_int = RL_output_int * RL_tmp;
    return RL_output_int;
}

__kernel void
ranlux_init(const __global uint * seeds, __global float4 * seedtable)
{
    int RL_jseed = (int) seeds[GID];
	int4		RL_temp1 = rlseedint(RL_jseed);
	seedtable[GID + 0 * GID_SIZE] = rlseedint2(RL_temp1);
				RL_temp1 = rlseedint(RL_temp1.w);
	seedtable[GID + 1 * GID_SIZE] = rlseedint2(RL_temp1);
				RL_temp1 = rlseedint(RL_temp1.w);
	seedtable[GID + 2 * GID_SIZE] = rlseedint2(RL_temp1);
				RL_temp1 = rlseedint(RL_temp1.w);
	seedtable[GID + 3 * GID_SIZE] = rlseedint2(RL_temp1);
				RL_temp1 = rlseedint(RL_temp1.w);
	seedtable[GID + 4 * GID_SIZE] = rlseedint2(RL_temp1);
				RL_temp1 = rlseedint(RL_temp1.w);
	float4		RL_temp2 = rlseedint2(RL_temp1);
	seedtable[GID + 5 * GID_SIZE] = RL_temp2;
	float4		RL_carin = (float4) (23.0f, 9.0f, 6.0f, 0.0f);
	if (RL_temp2.w == 0) {RL_carin.w = RL_twom24;}
	seedtable[GID + 6 * GID_SIZE] = RL_carin; // RL_i24, RL_j24, RL_in24, RL_carry
}

__attribute__((always_inline)) float4
rlproducefloat4(float4 * RL_seeds_i24, const float4 RL_seeds_j24, const float RL_seeds_j24P, float * RL_carry)
{
	float RL_c = *RL_carry;
	float4 RL_output_int = RL_seeds_j24 - (*RL_seeds_i24).wzyx;
	RL_output_int.x = RL_output_int.x - RL_c;
		if (RL_output_int.x < 0.0f) { RL_output_int.x = RL_output_int.x + 1.0f; RL_c = RL_twom24; } else { RL_c = 0.0f; }
	RL_output_int.y = RL_output_int.y - RL_c;
		if (RL_output_int.y < 0.0f) { RL_output_int.y = RL_output_int.y + 1.0f; RL_c = RL_twom24; } else { RL_c = 0.0f; }
	RL_output_int.z = RL_output_int.z - RL_c;
		if (RL_output_int.z < 0.0f) { RL_output_int.z = RL_output_int.z + 1.0f; RL_c = RL_twom24; } else { RL_c = 0.0f; }
	RL_output_int.w = RL_output_int.w - RL_c;
		if (RL_output_int.w < 0.0f) { RL_output_int.w = RL_output_int.w + 1.0f; RL_c = RL_twom24; } else { RL_c = 0.0f; }

	*RL_seeds_i24 = RL_output_int.wzyx;

    float4 RL_temporary = RL_seeds_j24.yzwx;
    RL_temporary.w = RL_seeds_j24P;

	float4 RL_output_int2 = select(RL_output_int, (RL_output_int + RL_twom24 * RL_temporary), RL_output_int < (float4) RL_twom12);
			RL_output_int = select(RL_output_int2, RL_twom24sq, RL_output_int2 == (float4) 0.0f);

	*RL_carry = RL_c;
	return RL_output_int;
}

__attribute__((always_inline)) void
rlproduceupd4(float4 * RL_seeds_i24, const float4 RL_seeds_j24, float * RL_carry)
{
	float RL_c = *RL_carry;
	float4 RL_output_int = RL_seeds_j24 - (*RL_seeds_i24).wzyx;
	RL_output_int.x = RL_output_int.x - RL_c;
		if (RL_output_int.x < 0.0f) { RL_output_int.x = RL_output_int.x + 1.0f; RL_c = RL_twom24; } else { RL_c = 0.0f; }
	RL_output_int.y = RL_output_int.y - RL_c;
		if (RL_output_int.y < 0.0f) { RL_output_int.y = RL_output_int.y + 1.0f; RL_c = RL_twom24; } else { RL_c = 0.0f; }
	RL_output_int.z = RL_output_int.z - RL_c;
		if (RL_output_int.z < 0.0f) { RL_output_int.z = RL_output_int.z + 1.0f; RL_c = RL_twom24; } else { RL_c = 0.0f; }
	RL_output_int.w = RL_output_int.w - RL_c;
		if (RL_output_int.w < 0.0f) { RL_output_int.w = RL_output_int.w + 1.0f; RL_c = RL_twom24; } else { RL_c = 0.0f; }

	*RL_seeds_i24 = RL_output_int.wzyx;
	*RL_carry = RL_c;
}

__attribute__((always_inline)) float4
rl_step(float4* RL_seed0,float4* RL_seed1,float4* RL_seed2,float4* RL_seed3,
        float4* RL_seed4,float4* RL_seed5,float4* RL_carin)
{
	float4 RL_seeds_j24, uni, RL_seed_temp, RL_seed_temp2;
 	float RL_carry = (*RL_carin).w;
		if ((*RL_carin).z >= 6.0f) {
			for (int t=0; t<RL_skip_24; t++) {
				RL_seeds_j24.xy = (*RL_seed2).yx;
				RL_seeds_j24.zw = (*RL_seed1).wz;
					rlproduceupd4(RL_seed5,RL_seeds_j24,&RL_carry);
				RL_seeds_j24.xy = (*RL_seed1).yx;
				RL_seeds_j24.zw = (*RL_seed0).wz;
					rlproduceupd4(RL_seed4,RL_seeds_j24,&RL_carry);
				RL_seeds_j24.xy = (*RL_seed0).yx;
				RL_seeds_j24.zw = (*RL_seed5).wz;
					rlproduceupd4(RL_seed3,RL_seeds_j24,&RL_carry);
				RL_seeds_j24.xy = (*RL_seed5).yx;
				RL_seeds_j24.zw = (*RL_seed4).wz;
					rlproduceupd4(RL_seed2,RL_seeds_j24,&RL_carry);
				RL_seeds_j24.xy = (*RL_seed4).yx;
				RL_seeds_j24.zw = (*RL_seed3).wz;
					rlproduceupd4(RL_seed1,RL_seeds_j24,&RL_carry);
				RL_seeds_j24.xy = (*RL_seed3).yx;
				RL_seeds_j24.zw = (*RL_seed2).wz;
					rlproduceupd4(RL_seed0,RL_seeds_j24,&RL_carry);
			}
#if RL_skip_4 == 1
				RL_seeds_j24.xy = (*RL_seed2).yx;
				RL_seeds_j24.zw = (*RL_seed1).wz;
					rlproduceupd4(RL_seed5,RL_seeds_j24,&RL_carry);
                RL_seed_temp = (*RL_seed5);    // 5->4, 4->3, 3->2, 2->1, 1->0, 0->5
                (*RL_seed5) = (*RL_seed4);
                (*RL_seed4) = (*RL_seed3);
                (*RL_seed3) = (*RL_seed2);
                (*RL_seed2) = (*RL_seed1);
                (*RL_seed1) = (*RL_seed0);
                (*RL_seed0) = RL_seed_temp;
#elif RL_skip_4 == 2
				RL_seeds_j24.xy = (*RL_seed2).yx;
				RL_seeds_j24.zw = (*RL_seed1).wz;
					rlproduceupd4(RL_seed5,RL_seeds_j24,&RL_carry);
				RL_seeds_j24.xy = (*RL_seed1).yx;
				RL_seeds_j24.zw = (*RL_seed0).wz;
					rlproduceupd4(RL_seed4,RL_seeds_j24,&RL_carry);
                RL_seed_temp = (*RL_seed5);    // 5->3, 4->2, 3->1, 2->0, 1->5, 0->4
                (*RL_seed5) = (*RL_seed3);
                (*RL_seed3) = (*RL_seed1);
                (*RL_seed1) = RL_seed_temp;
                RL_seed_temp = (*RL_seed2);
                (*RL_seed2) = (*RL_seed0);
                (*RL_seed0) = (*RL_seed4);
                (*RL_seed4) = RL_seed_temp;
#elif RL_skip_4 == 3
				RL_seeds_j24.xy = (*RL_seed2).yx;
				RL_seeds_j24.zw = (*RL_seed1).wz;
					rlproduceupd4(RL_seed5,RL_seeds_j24,&RL_carry);
				RL_seeds_j24.xy = (*RL_seed1).yx;
				RL_seeds_j24.zw = (*RL_seed0).wz;
					rlproduceupd4(RL_seed4,RL_seeds_j24,&RL_carry);
				RL_seeds_j24.xy = (*RL_seed0).yx;
				RL_seeds_j24.zw = (*RL_seed5).wz;
					rlproduceupd4(RL_seed3,RL_seeds_j24,&RL_carry);
                RL_seed_temp = (*RL_seed5);    // 5->2, 4->1, 3->0, 2->5, 1->4, 0->3
                (*RL_seed5) = (*RL_seed2);
                (*RL_seed2) = RL_seed_temp;
                RL_seed_temp = (*RL_seed4);
                (*RL_seed4) = (*RL_seed1);
                (*RL_seed1) = RL_seed_temp;
                RL_seed_temp = (*RL_seed3);
                (*RL_seed3) = (*RL_seed0);
                (*RL_seed0) = RL_seed_temp;
#elif RL_skip_4 == 4
				RL_seeds_j24.xy = (*RL_seed2).yx;
				RL_seeds_j24.zw = (*RL_seed1).wz;
					rlproduceupd4(RL_seed5,RL_seeds_j24,&RL_carry);
				RL_seeds_j24.xy = (*RL_seed1).yx;
				RL_seeds_j24.zw = (*RL_seed0).wz;
					rlproduceupd4(RL_seed4,RL_seeds_j24,&RL_carry);
				RL_seeds_j24.xy = (*RL_seed0).yx;
				RL_seeds_j24.zw = (*RL_seed5).wz;
					rlproduceupd4(RL_seed3,RL_seeds_j24,&RL_carry);
				RL_seeds_j24.xy = (*RL_seed5).yx;
				RL_seeds_j24.zw = (*RL_seed4).wz;
					rlproduceupd4(RL_seed2,RL_seeds_j24,&RL_carry);
                RL_seed_temp = (*RL_seed5);    // 5->1, 4->0, 3->5, 2->4, 1->3, 0->2
                (*RL_seed5) = (*RL_seed1);
                (*RL_seed1) = (*RL_seed3);
                (*RL_seed3) = RL_seed_temp;
                RL_seed_temp = (*RL_seed4);
                (*RL_seed4) = (*RL_seed0);
                (*RL_seed0) = (*RL_seed2);
                (*RL_seed2) = RL_seed_temp;
#elif RL_skip_4 == 5
				RL_seeds_j24.xy = (*RL_seed2).yx;
				RL_seeds_j24.zw = (*RL_seed1).wz;
					rlproduceupd4(RL_seed5,RL_seeds_j24,&RL_carry);
				RL_seeds_j24.xy = (*RL_seed1).yx;
				RL_seeds_j24.zw = (*RL_seed0).wz;
					rlproduceupd4(RL_seed4,RL_seeds_j24,&RL_carry);
				RL_seeds_j24.xy = (*RL_seed0).yx;
				RL_seeds_j24.zw = (*RL_seed5).wz;
					rlproduceupd4(RL_seed3,RL_seeds_j24,&RL_carry);
				RL_seeds_j24.xy = (*RL_seed5).yx;
				RL_seeds_j24.zw = (*RL_seed4).wz;
					rlproduceupd4(RL_seed2,RL_seeds_j24,&RL_carry);
				RL_seeds_j24.xy = (*RL_seed4).yx;
				RL_seeds_j24.zw = (*RL_seed3).wz;
					rlproduceupd4(RL_seed1,RL_seeds_j24,&RL_carry);
                RL_seed_temp = (*RL_seed5);    // 5->0, 4->5, 3->4, 2->3, 1->2, 0->1
                (*RL_seed5) = (*RL_seed0);
                (*RL_seed0) = (*RL_seed1);
                (*RL_seed1) = (*RL_seed2);
                (*RL_seed2) = (*RL_seed3);
                (*RL_seed3) = (*RL_seed4);
                (*RL_seed4) = RL_seed_temp;
#endif
#if RL_skip_1 == 1
                float RL_output_int = (*RL_seed2).y - (*RL_seed5).w;
                RL_output_int = RL_output_int - RL_carry;
                    if (RL_output_int < 0.0f) { RL_output_int = RL_output_int + 1.0f; RL_carry = RL_twom24; } else { RL_carry = 0.0f; }
                (*RL_seed5).w = RL_output_int;

                RL_seed_temp = (*RL_seed5);
                (*RL_seed5).yzw = (*RL_seed5).xyz;
                (*RL_seed5).x = (*RL_seed4).w;
                (*RL_seed4).yzw = (*RL_seed4).xyz;
                (*RL_seed4).x = (*RL_seed3).w;
                (*RL_seed3).yzw = (*RL_seed3).xyz;
                (*RL_seed3).x = (*RL_seed2).w;
                (*RL_seed2).yzw = (*RL_seed2).xyz;
                (*RL_seed2).x = (*RL_seed1).w;
                (*RL_seed1).yzw = (*RL_seed1).xyz;
                (*RL_seed1).x = (*RL_seed0).w;
                (*RL_seed0).yzw = (*RL_seed0).xyz;
                (*RL_seed0).x = RL_seed_temp.w;
#elif RL_skip_1 == 2
                float4 RL_output_int;
                RL_output_int.xy = (*RL_seed2).yx - (*RL_seed5).wz;
                RL_output_int.x = RL_output_int.x - RL_carry;
                    if (RL_output_int.x < 0.0f) { RL_output_int.x = RL_output_int.x + 1.0f; RL_carry = RL_twom24; } else { RL_carry = 0.0f; }
                RL_output_int.y = RL_output_int.y - RL_carry;
                    if (RL_output_int.y < 0.0f) { RL_output_int.y = RL_output_int.y + 1.0f; RL_carry = RL_twom24; } else { RL_carry = 0.0f; }

                (*RL_seed5).zw = RL_output_int.yx;

                RL_seed_temp = (*RL_seed5);
                (*RL_seed5).zw = (*RL_seed5).xy;
                (*RL_seed5).xy = (*RL_seed4).zw;
                (*RL_seed4).zw = (*RL_seed4).xy;
                (*RL_seed4).xy = (*RL_seed3).zw;
                (*RL_seed3).zw = (*RL_seed3).xy;
                (*RL_seed3).xy = (*RL_seed2).zw;
                (*RL_seed2).zw = (*RL_seed2).xy;
                (*RL_seed2).xy = (*RL_seed1).zw;
                (*RL_seed1).zw = (*RL_seed1).xy;
                (*RL_seed1).xy = (*RL_seed0).zw;
                (*RL_seed0).zw = (*RL_seed0).xy;
                (*RL_seed0).xy = RL_seed_temp.zw;
#elif RL_skip_1 == 3
                float4 RL_output_int;
                RL_output_int.xy = (*RL_seed2).yx - (*RL_seed5).wz;
                RL_output_int.z = (*RL_seed1).w - (*RL_seed5).y;
                RL_output_int.x = RL_output_int.x - RL_carry;
                    if (RL_output_int.x < 0.0f) { RL_output_int.x = RL_output_int.x + 1.0f; RL_carry = RL_twom24; } else { RL_carry = 0.0f; }
                RL_output_int.y = RL_output_int.y - RL_carry;
                    if (RL_output_int.y < 0.0f) { RL_output_int.y = RL_output_int.y + 1.0f; RL_carry = RL_twom24; } else { RL_carry = 0.0f; }
                RL_output_int.z = RL_output_int.z - RL_carry;
                    if (RL_output_int.z < 0.0f) { RL_output_int.z = RL_output_int.z + 1.0f; RL_carry = RL_twom24; } else { RL_carry = 0.0f; }

                (*RL_seed5).yzw = RL_output_int.zyx;

                RL_seed_temp = (*RL_seed5);
                (*RL_seed5).w = (*RL_seed5).x;
                (*RL_seed5).xyz = (*RL_seed4).yzw;
                (*RL_seed4).w = (*RL_seed4).x;
                (*RL_seed4).xyz = (*RL_seed3).yzw;
                (*RL_seed3).w = (*RL_seed3).x;
                (*RL_seed3).xyz = (*RL_seed2).yzw;
                (*RL_seed2).w = (*RL_seed2).x;
                (*RL_seed2).xyz = (*RL_seed1).yzw;
                (*RL_seed1).w = (*RL_seed1).x;
                (*RL_seed1).xyz = (*RL_seed0).yzw;
                (*RL_seed0).w = (*RL_seed0).x;
                (*RL_seed0).xyz = RL_seed_temp.yzw;
#endif
#ifdef RL_RANLUX2
                float RL_output_int = (*RL_seed2).y - (*RL_seed5).w;
                RL_output_int = RL_output_int - RL_carry;
                    if (RL_output_int < 0.0f) { RL_output_int = RL_output_int + 1.0f; RL_carry = RL_twom24; } else { RL_carry = 0.0f; }
                (*RL_seed5).w = RL_output_int;

                RL_seed_temp = (*RL_seed5);
                (*RL_seed5).yzw = (*RL_seed5).xyz;
                (*RL_seed5).x = (*RL_seed4).w;
                (*RL_seed4).yzw = (*RL_seed4).xyz;
                (*RL_seed4).x = (*RL_seed3).w;
                (*RL_seed3).yzw = (*RL_seed3).xyz;
                (*RL_seed3).x = (*RL_seed2).w;
                (*RL_seed2).yzw = (*RL_seed2).xyz;
                (*RL_seed2).x = (*RL_seed1).w;
                (*RL_seed1).yzw = (*RL_seed1).xyz;
                (*RL_seed1).x = (*RL_seed0).w;
                (*RL_seed0).yzw = (*RL_seed0).xyz;
                (*RL_seed0).x = RL_seed_temp.w;
#endif
#ifdef RL_RANLUX3
                RL_seeds_j24.xy = (*RL_seed2).yx;
                RL_seeds_j24.zw = (*RL_seed1).wz;
                    rlproduceupd4(RL_seed5,RL_seeds_j24,&RL_carry);
                float4 RL_output_int;
                RL_output_int.xy = (*RL_seed1).yx - (*RL_seed4).wz;
                RL_output_int.z = (*RL_seed0).w - (*RL_seed4).y;
                RL_output_int.x = RL_output_int.x - RL_carry;
                    if (RL_output_int.x < 0.0f) { RL_output_int.x = RL_output_int.x + 1.0f; RL_carry = RL_twom24; } else { RL_carry = 0.0f; }
                RL_output_int.y = RL_output_int.y - RL_carry;
                    if (RL_output_int.y < 0.0f) { RL_output_int.y = RL_output_int.y + 1.0f; RL_carry = RL_twom24; } else { RL_carry = 0.0f; }
                RL_output_int.z = RL_output_int.z - RL_carry;
                    if (RL_output_int.z < 0.0f) { RL_output_int.z = RL_output_int.z + 1.0f; RL_carry = RL_twom24; } else { RL_carry = 0.0f; }

                RL_seed_temp.xyz  = RL_output_int.zyx;
                RL_seed_temp.w    = (*RL_seed5).x;
                RL_seed_temp2.xyz = (*RL_seed5).yzw;
                RL_seed_temp2.w   = (*RL_seed0).x;

                (*RL_seed5).w   = (*RL_seed4).x;
                (*RL_seed5).xyz = (*RL_seed3).yzw;
                (*RL_seed4).w   = (*RL_seed3).x;
                (*RL_seed4).xyz = (*RL_seed2).yzw;
                (*RL_seed3).w   = (*RL_seed2).x;
                (*RL_seed3).xyz = (*RL_seed1).yzw;
                (*RL_seed2).w   = (*RL_seed1).x;
                (*RL_seed2).xyz = (*RL_seed0).yzw;
                (*RL_seed1)  = RL_seed_temp2;
                (*RL_seed0)  = RL_seed_temp;
#endif
#ifdef RL_RANLUX4
                RL_seeds_j24.xy = (*RL_seed2).yx;
                RL_seeds_j24.zw = (*RL_seed1).wz;
                    rlproduceupd4(RL_seed5,RL_seeds_j24,&RL_carry);
                float RL_output_int = (*RL_seed1).y - (*RL_seed4).w;
                RL_output_int = RL_output_int - RL_carry;
                    if (RL_output_int < 0.0f) { RL_output_int = RL_output_int + 1.0f; RL_carry = RL_twom24; } else { RL_carry = 0.0f; }

                RL_seed_temp  = (*RL_seed5);
                RL_seed_temp2.x = RL_output_int;

                (*RL_seed5).yzw = (*RL_seed4).xyz;
                (*RL_seed5).x   = (*RL_seed3).w;
                (*RL_seed4).yzw = (*RL_seed3).xyz;
                (*RL_seed4).x   = (*RL_seed2).w;
                (*RL_seed3).yzw = (*RL_seed2).xyz;
                (*RL_seed3).x   = (*RL_seed1).w;
                (*RL_seed2).yzw = (*RL_seed1).xyz;
                (*RL_seed2).x   = (*RL_seed0).w;
                (*RL_seed1).yzw = (*RL_seed0).xyz;
                (*RL_seed1).x   = RL_seed_temp.w;
                (*RL_seed0).yzw = RL_seed_temp.xyz;
                (*RL_seed0).x   = RL_seed_temp2.x;
#endif
            (*RL_carin).z = 0.0f;
		}
        if ((*RL_carin).z < 1.0f) {
			RL_seeds_j24.xy = (*RL_seed2).yx;
			RL_seeds_j24.zw = (*RL_seed1).wz;
				uni = rlproducefloat4(RL_seed5,RL_seeds_j24,(*RL_seed1).y,&RL_carry);
			(*RL_carin).z = 1.0f;
		} else if ((*RL_carin).z == 1.0f) {
			RL_seeds_j24.xy = (*RL_seed1).yx;
			RL_seeds_j24.zw = (*RL_seed0).wz;
				uni = rlproducefloat4(RL_seed4,RL_seeds_j24,(*RL_seed0).y,&RL_carry);
			(*RL_carin).z = 2.0f;
		} else if ((*RL_carin).z == 2.0f) {
			RL_seeds_j24.xy = (*RL_seed0).yx;
			RL_seeds_j24.zw = (*RL_seed5).wz;
				uni = rlproducefloat4(RL_seed3,RL_seeds_j24,(*RL_seed5).y,&RL_carry);
			(*RL_carin).z = 3.0f;
		} else if ((*RL_carin).z == 3.0f) {
			RL_seeds_j24.xy = (*RL_seed5).yx;
			RL_seeds_j24.zw = (*RL_seed4).wz;
				uni = rlproducefloat4(RL_seed2,RL_seeds_j24,(*RL_seed4).y,&RL_carry);
			(*RL_carin).z = 4.0f;
		} else if ((*RL_carin).z == 4.0f) {
			RL_seeds_j24.xy = (*RL_seed4).yx;
			RL_seeds_j24.zw = (*RL_seed3).wz;
				uni = rlproducefloat4(RL_seed1,RL_seeds_j24,(*RL_seed3).y,&RL_carry);
			(*RL_carin).z = 5.0f;
		} else if ((*RL_carin).z == 5.0f) {
			RL_seeds_j24.xy = (*RL_seed3).yx;
			RL_seeds_j24.zw = (*RL_seed2).wz;
				uni = rlproducefloat4(RL_seed0,RL_seeds_j24,(*RL_seed2).y,&RL_carry);
			(*RL_carin).z = 6.0f;
		}
	(*RL_carin).w = RL_carry;
    return uni;
}

#ifdef PRECISION_DOUBLE  // if double precision is defined
__attribute__((always_inline)) hgpu_double4
rl_step_double(float4* RL_seed0,float4* RL_seed1,float4* RL_seed2,float4* RL_seed3,
               float4* RL_seed4,float4* RL_seed5,float4* RL_carin)
{
    hgpu_double4 result;
    hgpu_double2 rnd_double;
    float4 rnd1,rnd2;
#ifndef PRNG_SKIP_CHECK
    bool flag = true;
    while (flag) {
#endif
        rnd1 = rl_step(RL_seed0,RL_seed1,RL_seed2,RL_seed3,RL_seed4,RL_seed5,RL_carin);
        rnd_double = hgpu_float4_to_double2(rnd1,RL_min,RL_max,RL_k);

#ifndef PRNG_SKIP_CHECK
        if ((rnd_double.x>=RL_left) && (rnd_double.x<RL_right) &&
            (rnd_double.y>=RL_left) && (rnd_double.y<RL_right)) flag = false;
    }
#endif
    result.x = rnd_double.x;
    result.y = rnd_double.y;

#ifndef PRNG_SKIP_CHECK
    flag = true;
    while (flag) {
#endif
        rnd2 = rl_step(RL_seed0,RL_seed1,RL_seed2,RL_seed3,RL_seed4,RL_seed5,RL_carin);
        rnd_double = hgpu_float4_to_double2(rnd2,RL_min,RL_max,RL_k);

#ifndef PRNG_SKIP_CHECK
        if ((rnd_double.x>=RL_left) && (rnd_double.x<RL_right) &&
            (rnd_double.y>=RL_left) && (rnd_double.y<RL_right)) flag = false;
    }
#endif
    result.z = rnd_double.x;
    result.w = rnd_double.y;

    return result;
}
#endif

__kernel void
ranlux(__global float4 * seedtable,__global hgpu_float4 * prns, const uint samples)
{
    uint giddst = GID;
		float4 RL_seed0 = seedtable[GID + 0 * GID_SIZE];
		float4 RL_seed1 = seedtable[GID + 1 * GID_SIZE];
		float4 RL_seed2 = seedtable[GID + 2 * GID_SIZE];
		float4 RL_seed3 = seedtable[GID + 3 * GID_SIZE];
		float4 RL_seed4 = seedtable[GID + 4 * GID_SIZE];
		float4 RL_seed5 = seedtable[GID + 5 * GID_SIZE];
		float4 RL_carin = seedtable[GID + 6 * GID_SIZE];    // RL_i24, RL_j24, RL_in24, RL_carry

	for (int i=0; i<samples; i++) {
#ifdef PRECISION_DOUBLE // if double precision is defined
        hgpu_double4 result = rl_step_double(&RL_seed0,&RL_seed1,&RL_seed2,&RL_seed3,&RL_seed4,&RL_seed5,&RL_carin);
        prns[giddst] = result;
#else
        float4 uni = rl_step(&RL_seed0,&RL_seed1,&RL_seed2,&RL_seed3,&RL_seed4,&RL_seed5,&RL_carin);
		prns[giddst] = uni;
#endif
		giddst += GID_SIZE;
	}

	seedtable[GID + 0 * GID_SIZE] = RL_seed0;
	seedtable[GID + 1 * GID_SIZE] = RL_seed1;
	seedtable[GID + 2 * GID_SIZE] = RL_seed2;
	seedtable[GID + 3 * GID_SIZE] = RL_seed3;
	seedtable[GID + 4 * GID_SIZE] = RL_seed4;
	seedtable[GID + 5 * GID_SIZE] = RL_seed5;
	seedtable[GID + 6 * GID_SIZE] = RL_carin;
}

#endif


Fixed a bug in the SYRK/SYR2K/HERK/HER2K routines that would occur with specific tuning parameters (2)

// =================================================================================================
// This file is part of the CLBlast project. The project is licensed under Apache Version 2.0. This
// project loosely follows the Google C++ styleguide and uses a tab-size of two spaces and a max-
// width of 100 characters per line.
//
// Author(s):
//   Cedric Nugteren <www.cedricnugteren.nl>
//
// This is part 3 of 3 of the GEMM kernel. See part 1 for more information.
//
// =================================================================================================

// Enables loading of this file using the C++ pre-processor's #include (C++11 standard raw string
// literal). Comment-out this line for syntax-highlighting when developing.
R"(

// =================================================================================================

// Main body of the matrix-multiplication algorithm. It calls the (inlined) functions above.
inline void XgemmBody(const int kSizeM, const int kSizeN, const int kSizeK,
                      const __global realM* restrict agm, const __global realN* restrict bgm,
                      __global realM* cgm, realM cpm[NWI][MWI/VWM]
                      #if SA == 1 && SB == 1
                        , __local realM* alm, __local realN* blm
                      #elif SA == 1
                        , __local realM* alm
                      #elif SB == 1
                        , __local realN* blm
                      #endif
                      ) {

  // Allocates workitem-private memory (registers)
  realM apm[MWI/VWM];
  realN bpm[NWI/VWN];

  // Combined thread identifier (volatile to disable caching)
  #if SA == 1 || SB == 1
    volatile int tid = get_local_id(0) + MDIMC*get_local_id(1);
  #endif

  // Initializes the accumulation registers
  InitAccRegisters(cpm);

  // Loops over all workgroup tiles
  for (int kwg=0; kwg<kSizeK; kwg+=KWG) {

    // Loads data: off-chip --> local (matrix A)
    #if SA == 1
      GlobalToLocalA(agm, alm, kSizeM, tid, kwg);
    #endif
    // Loads data: off-chip --> local (matrix B)
    #if SB == 1
      GlobalToLocalB(bgm, blm, kSizeN, tid, kwg);
    #endif
    #if SA == 1 || SB == 1
      barrier(CLK_LOCAL_MEM_FENCE);
    #endif

    // Loops over all workitem tiles, unrolled by a factor KWI
    for (int pwi=0; pwi<KWG; pwi+=KWI) {
      #pragma unroll
      for (int pit=0; pit<KWI; ++pit) {
        #if SA == 0 || SB == 0
          int idk = kwg + pwi + pit;
        #endif
        #if SA == 1 || SB == 1
          int kg = pwi+pit;
        #endif

        // Loads data: local --> private (matrix A)
        #if SA == 1
          LocalToPrivateA(alm, apm, kg);
        // Loads data: off-chip --> private (matrix A)
        #else
          GlobalToPrivateA(agm, apm, kSizeM, idk, kwg);
        #endif

        // Loads data: local --> private (matrix B)
        #if SB == 1
          LocalToPrivateB(blm, bpm, kg);
        // Loads data: off-chip --> private (matrix B)
        #else
          GlobalToPrivateB(bgm, bpm, kSizeN, idk);
        #endif

        // Performs the accumulation (Cpm += Apm * Bpm)
        MultiplyAccumulate(cpm, apm, bpm);
      }
    }
    #if SA == 1 || SB == 1
      barrier(CLK_LOCAL_MEM_FENCE);
    #endif
  }
  #if GLOBAL_MEM_FENCE == 1
    barrier(CLK_GLOBAL_MEM_FENCE);
  #endif
}

// =================================================================================================
// The upper-triangular and lower-triangular kernels are only used in special cases
#if defined(ROUTINE_SYRK) || defined(ROUTINE_HERK) || defined(ROUTINE_SYR2K) || defined(ROUTINE_HER2K)

// Main entry point of the kernel. This is the upper-triangular version.
__kernel __attribute__((reqd_work_group_size(MDIMC, NDIMC, 1)))
void XgemmUpper(const int kSizeN, const int kSizeK,
                const real_arg arg_alpha,
                const real_arg arg_beta,
                const __global realM* restrict agm,
                const __global realN* restrict bgm,
                __global realM* cgm) {
  const real alpha = GetRealArg(arg_alpha);
  const real beta = GetRealArg(arg_beta);

  // Skip these threads if they do not contain threads contributing to the upper-triangle
  if ((GetGroupID1() + 1)*NWG < GetGroupID0()*MWG) {
    return;
  }

  // Allocates workgroup-private memory (local memory)
  #if SA == 1
    __local realM alm[KWG * MWG/VWM];
  #endif
  #if SB == 1
    __local realN blm[KWG * NWG/VWN];
  #endif

  // Computes the matrix-multiplication and stores the result in register memory
  realM cpm[NWI][MWI/VWM];
  #if SA == 1 && SB == 1
    XgemmBody(kSizeN, kSizeN, kSizeK, agm, bgm, cgm, cpm, alm, blm);
  #elif SA == 1
    XgemmBody(kSizeN, kSizeN, kSizeK, agm, bgm, cgm, cpm, alm);
  #elif SB == 1
    XgemmBody(kSizeN, kSizeN, kSizeK, agm, bgm, cgm, cpm, blm);
  #else
    XgemmBody(kSizeN, kSizeN, kSizeK, agm, bgm, cgm, cpm);
  #endif

  // Stores an MWG * NWG tile of results and performs the multiplication with alpha and beta
  StoreResults(cgm, cpm, kSizeN, alpha, beta);
}

// Main entry point of the kernel. This is the lower-triangular version.
__kernel __attribute__((reqd_work_group_size(MDIMC, NDIMC, 1)))
void XgemmLower(const int kSizeN, const int kSizeK,
                const real_arg arg_alpha,
                const real_arg arg_beta,
                const __global realM* restrict agm,
                const __global realN* restrict bgm,
                __global realM* cgm) {
  const real alpha = GetRealArg(arg_alpha);
  const real beta = GetRealArg(arg_beta);

  // Skip these threads if they do not contain threads contributing to the lower-triangle
  if (GetGroupID1()*NWG > (GetGroupID0() + 1)*MWG) {
    return;
  }

  // Allocates workgroup-private memory (local memory)
  #if SA == 1
    __local realM alm[KWG * MWG/VWM];
  #endif
  #if SB == 1
    __local realN blm[KWG * NWG/VWN];
  #endif

  // Computes the matrix-multiplication and stores the result in register memory
  realM cpm[NWI][MWI/VWM];
  #if SA == 1 && SB == 1
    XgemmBody(kSizeN, kSizeN, kSizeK, agm, bgm, cgm, cpm, alm, blm);
  #elif SA == 1
    XgemmBody(kSizeN, kSizeN, kSizeK, agm, bgm, cgm, cpm, alm);
  #elif SB == 1
    XgemmBody(kSizeN, kSizeN, kSizeK, agm, bgm, cgm, cpm, blm);
  #else
    XgemmBody(kSizeN, kSizeN, kSizeK, agm, bgm, cgm, cpm);
  #endif

  // Stores an MWG * NWG tile of results and performs the multiplication with alpha and beta
  StoreResults(cgm, cpm, kSizeN, alpha, beta);
}

// =================================================================================================
// If not using a triangular version, include the regular kernel
#else

// Main entry point of the kernel. This is the regular full version.
__kernel __attribute__((reqd_work_group_size(MDIMC, NDIMC, 1)))
void Xgemm(const int kSizeM, const int kSizeN, const int kSizeK,
           const real_arg arg_alpha,
           const real_arg arg_beta,
           const __global realM* restrict agm,
           const __global realN* restrict bgm,
           __global realM* cgm) {
  const real alpha = GetRealArg(arg_alpha);
  const real beta = GetRealArg(arg_beta);

  // Allocates workgroup-private memory (local memory)
  #if SA == 1
    __local realM alm[KWG * MWG/VWM];
  #endif
  #if SB == 1
    __local realN blm[KWG * NWG/VWN];
  #endif

  // Computes the matrix-multiplication and stores the result in register memory
  realM cpm[NWI][MWI/VWM];
  #if SA == 1 && SB == 1
    XgemmBody(kSizeM, kSizeN, kSizeK, agm, bgm, cgm, cpm, alm, blm);
  #elif SA == 1
    XgemmBody(kSizeM, kSizeN, kSizeK, agm, bgm, cgm, cpm, alm);
  #elif SB == 1
    XgemmBody(kSizeM, kSizeN, kSizeK, agm, bgm, cgm, cpm, blm);
  #else
    XgemmBody(kSizeM, kSizeN, kSizeK, agm, bgm, cgm, cpm);
  #endif

  // Stores an MWG * NWG tile of results and performs the multiplication with alpha and beta
  StoreResults(cgm, cpm, kSizeM, alpha, beta);
}

#endif
// =================================================================================================

// End of the C++11 raw string literal
)"

// =================================================================================================

// =================================================================================================
// This file is part of the CLBlast project. The project is licensed under Apache Version 2.0. This
// project loosely follows the Google C++ styleguide and uses a tab-size of two spaces and a max-
// width of 100 characters per line.
//
// Author(s):
//   Cedric Nugteren <www.cedricnugteren.nl>
//
// This is part 3 of 3 of the GEMM kernel. See part 1 for more information.
//
// =================================================================================================

// Enables loading of this file using the C++ pre-processor's #include (C++11 standard raw string
// literal). Comment-out this line for syntax-highlighting when developing.
R"(

// =================================================================================================

// Main body of the matrix-multiplication algorithm. It calls the (inlined) functions above.
inline void XgemmBody(const int kSizeM, const int kSizeN, const int kSizeK,
                      const __global realM* restrict agm, const __global realN* restrict bgm,
                      __global realM* cgm, realM cpm[NWI][MWI/VWM]
                      #if SA == 1 && SB == 1
                        , __local realM* alm, __local realN* blm
                      #elif SA == 1
                        , __local realM* alm
                      #elif SB == 1
                        , __local realN* blm
                      #endif
                      ) {

  // Allocates workitem-private memory (registers)
  realM apm[MWI/VWM];
  realN bpm[NWI/VWN];

  // Combined thread identifier (volatile to disable caching)
  #if SA == 1 || SB == 1
    volatile int tid = get_local_id(0) + MDIMC*get_local_id(1);
  #endif

  // Initializes the accumulation registers
  InitAccRegisters(cpm);

  // Loops over all workgroup tiles
  for (int kwg=0; kwg<kSizeK; kwg+=KWG) {

    // Loads data: off-chip --> local (matrix A)
    #if SA == 1
      GlobalToLocalA(agm, alm, kSizeM, tid, kwg);
    #endif
    // Loads data: off-chip --> local (matrix B)
    #if SB == 1
      GlobalToLocalB(bgm, blm, kSizeN, tid, kwg);
    #endif
    #if SA == 1 || SB == 1
      barrier(CLK_LOCAL_MEM_FENCE);
    #endif

    // Loops over all workitem tiles, unrolled by a factor KWI
    for (int pwi=0; pwi<KWG; pwi+=KWI) {
      #pragma unroll
      for (int pit=0; pit<KWI; ++pit) {
        #if SA == 0 || SB == 0
          int idk = kwg + pwi + pit;
        #endif
        #if SA == 1 || SB == 1
          int kg = pwi+pit;
        #endif

        // Loads data: local --> private (matrix A)
        #if SA == 1
          LocalToPrivateA(alm, apm, kg);
        // Loads data: off-chip --> private (matrix A)
        #else
          GlobalToPrivateA(agm, apm, kSizeM, idk, kwg);
        #endif

        // Loads data: local --> private (matrix B)
        #if SB == 1
          LocalToPrivateB(blm, bpm, kg);
        // Loads data: off-chip --> private (matrix B)
        #else
          GlobalToPrivateB(bgm, bpm, kSizeN, idk);
        #endif

        // Performs the accumulation (Cpm += Apm * Bpm)
        MultiplyAccumulate(cpm, apm, bpm);
      }
    }
    #if SA == 1 || SB == 1
      barrier(CLK_LOCAL_MEM_FENCE);
    #endif
  }
  #if GLOBAL_MEM_FENCE == 1
    barrier(CLK_GLOBAL_MEM_FENCE);
  #endif
}

// =================================================================================================
// The upper-triangular and lower-triangular kernels are only used in special cases
#if defined(ROUTINE_SYRK) || defined(ROUTINE_HERK) || defined(ROUTINE_SYR2K) || defined(ROUTINE_HER2K)

// Main entry point of the kernel. This is the upper-triangular version.
__kernel __attribute__((reqd_work_group_size(MDIMC, NDIMC, 1)))
void XgemmUpper(const int kSizeN, const int kSizeK,
                const real_arg arg_alpha,
                const real_arg arg_beta,
                const __global realM* restrict agm,
                const __global realN* restrict bgm,
                __global realM* cgm) {
  const real alpha = GetRealArg(arg_alpha);
  const real beta = GetRealArg(arg_beta);

  // Skip these threads if they do not contain threads contributing to the upper-triangle
  if (GetGroupID1()*NWG < GetGroupID0()*MWG) {
    return;
  }

  // Allocates workgroup-private memory (local memory)
  #if SA == 1
    __local realM alm[KWG * MWG/VWM];
  #endif
  #if SB == 1
    __local realN blm[KWG * NWG/VWN];
  #endif

  // Computes the matrix-multiplication and stores the result in register memory
  realM cpm[NWI][MWI/VWM];
  #if SA == 1 && SB == 1
    XgemmBody(kSizeN, kSizeN, kSizeK, agm, bgm, cgm, cpm, alm, blm);
  #elif SA == 1
    XgemmBody(kSizeN, kSizeN, kSizeK, agm, bgm, cgm, cpm, alm);
  #elif SB == 1
    XgemmBody(kSizeN, kSizeN, kSizeK, agm, bgm, cgm, cpm, blm);
  #else
    XgemmBody(kSizeN, kSizeN, kSizeK, agm, bgm, cgm, cpm);
  #endif

  // Stores an MWG * NWG tile of results and performs the multiplication with alpha and beta
  StoreResults(cgm, cpm, kSizeN, alpha, beta);
}

// Main entry point of the kernel. This is the lower-triangular version.
__kernel __attribute__((reqd_work_group_size(MDIMC, NDIMC, 1)))
void XgemmLower(const int kSizeN, const int kSizeK,
                const real_arg arg_alpha,
                const real_arg arg_beta,
                const __global realM* restrict agm,
                const __global realN* restrict bgm,
                __global realM* cgm) {
  const real alpha = GetRealArg(arg_alpha);
  const real beta = GetRealArg(arg_beta);

  // Skip these threads if they do not contain threads contributing to the lower-triangle
  if (GetGroupID1()*NWG > (GetGroupID0() + 1)*MWG) {
    return;
  }

  // Allocates workgroup-private memory (local memory)
  #if SA == 1
    __local realM alm[KWG * MWG/VWM];
  #endif
  #if SB == 1
    __local realN blm[KWG * NWG/VWN];
  #endif

  // Computes the matrix-multiplication and stores the result in register memory
  realM cpm[NWI][MWI/VWM];
  #if SA == 1 && SB == 1
    XgemmBody(kSizeN, kSizeN, kSizeK, agm, bgm, cgm, cpm, alm, blm);
  #elif SA == 1
    XgemmBody(kSizeN, kSizeN, kSizeK, agm, bgm, cgm, cpm, alm);
  #elif SB == 1
    XgemmBody(kSizeN, kSizeN, kSizeK, agm, bgm, cgm, cpm, blm);
  #else
    XgemmBody(kSizeN, kSizeN, kSizeK, agm, bgm, cgm, cpm);
  #endif

  // Stores an MWG * NWG tile of results and performs the multiplication with alpha and beta
  StoreResults(cgm, cpm, kSizeN, alpha, beta);
}

// =================================================================================================
// If not using a triangular version, include the regular kernel
#else

// Main entry point of the kernel. This is the regular full version.
__kernel __attribute__((reqd_work_group_size(MDIMC, NDIMC, 1)))
void Xgemm(const int kSizeM, const int kSizeN, const int kSizeK,
           const real_arg arg_alpha,
           const real_arg arg_beta,
           const __global realM* restrict agm,
           const __global realN* restrict bgm,
           __global realM* cgm) {
  const real alpha = GetRealArg(arg_alpha);
  const real beta = GetRealArg(arg_beta);

  // Allocates workgroup-private memory (local memory)
  #if SA == 1
    __local realM alm[KWG * MWG/VWM];
  #endif
  #if SB == 1
    __local realN blm[KWG * NWG/VWN];
  #endif

  // Computes the matrix-multiplication and stores the result in register memory
  realM cpm[NWI][MWI/VWM];
  #if SA == 1 && SB == 1
    XgemmBody(kSizeM, kSizeN, kSizeK, agm, bgm, cgm, cpm, alm, blm);
  #elif SA == 1
    XgemmBody(kSizeM, kSizeN, kSizeK, agm, bgm, cgm, cpm, alm);
  #elif SB == 1
    XgemmBody(kSizeM, kSizeN, kSizeK, agm, bgm, cgm, cpm, blm);
  #else
    XgemmBody(kSizeM, kSizeN, kSizeK, agm, bgm, cgm, cpm);
  #endif

  // Stores an MWG * NWG tile of results and performs the multiplication with alpha and beta
  StoreResults(cgm, cpm, kSizeM, alpha, beta);
}

#endif
// =================================================================================================

// End of the C++11 raw string literal
)"

// =================================================================================================