Went from 4 points per particle to 2

Went from 4 points per particle to 2

/*
    Move the particles
    Copyright (c) 2013-2014, Randall Maas

    Permission is hereby granted, free of charge, to any person obtaining a copy
    of this software and associated documentation files (the "Software"), to deal
    in the Software without restriction, including without limitation the rights
    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    copies of the Software, and to permit persons to whom the Software is
    furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be included in
    all copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    THE SOFTWARE.

    Inpsired by Cameron Beccario, https://github.com/cambecc/earth
*/

#define M_PI   3.1415926535897932384626433832795f

// --- Randomize particle location -----------------------
/**Returns a random number
   @param seed The Seed to the random number generator
   @param max  The max number to return
   @returns A random number
 */
float random(__global ulong* seed, uint max)
{
    *seed = (*seed * 0x5DEECE66DL + 0xBL) & ((1L << 48) - 1);
    uint result = (*seed) >> 16;
    return result % max;
}

/** Find a random location for a particle
    @param numXBins The number of bins in the x axis
    @param numYBins The number of bins in the y axis
    @param seed     The random number seed
    @returns A {float2} vector
*/
float2 particleRandomize(
                       int numXBins, int numYBins,
                       __global ulong* seed
                       )
{
    float n = (random(seed, numYBins*10-1)/(numYBins*10.0f))*M_PI;
    float c = cos(n);
    float y = c*90.0f+180.0f;
    if (y >= 180.0f) y -= 180.0f;
    y *= numYBins/180.0f;
    return (float2)( random(seed, numXBins*10-1)/10.0
                   , y
                   );
}

/** This randomizes a single particle
    @param positions   Set of vectors for each particle
    @param positions_t The set of new vectors for each particle
    @param numXBins The number of bins in the x axis
    @param numYBins The number of bins in the y axis
    @param dataGrid The grid of vectors (mapped to an array)
    @param seed     The random number seed
 */
__kernel void particleInit( __global float2*   vertex   // position of each particle
                          , int numXBins, int  numYBins    // The size of the data grid
                          , __global ulong*    seed        // A randomizer
                          )
{
    // The global id of the work item.  (the index i)
    int idx = get_global_id(0);
    // Particle isn't visible, but it still moves through the field.
    // particle has escaped the grid, never to return...
    float2 position = particleRandomize(numXBins, numYBins, seed);

    // Path from (x,y) to (xt,yt) is visible, so add this particle to the appropriate draw bucket.
#if 0
    // The positions are actually vertices (4)
    int idx4 = 4*idx;
#else
    // The positions are actually vertices (4)
    int idx4 = 2*idx;
#endif
    vertex[idx4  ] = position;
    vertex[idx4+1] = position;
#if 0
    vertex[idx4+2] = position;
    vertex[idx4+3] = position;
#endif
}

/** This moves a single particle
    @param positions   Set of vectors for each particle
    @param positions_t The set of new vectors for each particle
    @param numXBins    The number of bins in the x axis
    @param numYBins    The number of bins in the y axis
    @param vectorField The field of vectors (mapped to an array), with normalized magnitude within the field
    @param seed     The random number seed
*/
__kernel void particleMove(  __global float2*   vertex    // position of each particle box
                           , float              dT          // Range from [0, 1]
                           , int numXBins, int  numYBins    // The size of the vector field
                           , __global float2*   vectorField // The data in the grid
                           , __global ulong*    seed        // A randomizer
                           )
{
    // The global id of the work item.  (the index i)
    int idx = get_global_id(0);

#if 0
    // The positions are actually vertices (4)
    int idx4 = 4*idx;
#else
    // The positions are actually vertices (4)
    int idx4 = 2*idx;
#endif

    // The left two are the key ones
    // Get the particle position
    float2 position = vertex[idx4]+dT*(vertex[idx4+1]-vertex[idx4]);
    float m;
    float2 position_t, position_t2;
    float2 position2;
    float dX=0;
    if (position.x <= 0.0)
    {
        // We wrapped around
        float2 p = vertex[idx4+1];

        position.x = numXBins-0.01;
        // We could calculate the actual y position but this works better for now
        position.y = (position.y+p.y)*0.5;
        position2.x= numXBins+0.2;
        position2.y= position.y+0.9;
    }
    else if (position.x >= numXBins)
    {
        float2 p = vertex[idx4+1];
        position.x = 0.0;
        // We could calculate the actual y position but this works better for now
        position.y = (position.y+p.y)*0.5;
        position2.x= 0.2;
        position2.y= position.y+0.9;
        dX = -0.5;
    }
    else
    {
        position2 = (float2)(position.x+1.0f, position.y+1.0f);
    }

    if (position.y >= numYBins || position.y < 0)
    {
        // The particle is moving too fast or too slow; get rid of it
        position_t = particleRandomize(numXBins, numYBins, seed);
        position_t2= position_t;
        position = position_t;
        position2= position_t;
    }
    else
    {
        // vector at current position
        float2 v = vectorField[(int)position.x + ((int) position.y)*numXBins];
        m = v.x*v.x + v.y*v.y;

        position_t = position + v;
        position_t2= position2+ v;
        position.x += dX;

        position_t2.x+=0.5f;

        if (m <1.0)
        {
            // The particle is moving too fast or too slow; get rid of it
            position_t = particleRandomize(numXBins, numYBins, seed);
            position_t2= position_t;
            position = position_t;
            position2= position_t;
        }
    }

    // Path from (x,y) to (xt,yt) is visible, so add this particle to the appropriate draw bucket.
    vertex[idx4]   = position;
    vertex[idx4+1] = position_t;
#if 0
    vertex[idx4+2] = position_t2;
    vertex[idx4+3] = position2;
#endif
}

/*
    Move the particles
    Copyright (c) 2013-2014, Randall Maas

    Permission is hereby granted, free of charge, to any person obtaining a copy
    of this software and associated documentation files (the "Software"), to deal
    in the Software without restriction, including without limitation the rights
    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    copies of the Software, and to permit persons to whom the Software is
    furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be included in
    all copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    THE SOFTWARE.

    Inpsired by Cameron Beccario, https://github.com/cambecc/earth
*/

#define M_PI   3.1415926535897932384626433832795f

// --- Randomize particle location -----------------------
/**Returns a random number
   @param seed The Seed to the random number generator
   @param max  The max number to return
   @returns A random number
 */
float random(__global ulong* seed, uint max)
{
    *seed = (*seed * 0x5DEECE66DL + 0xBL) & ((1L << 48) - 1);
    uint result = (*seed) >> 16;
    return result % max;
}

/** Find a random location for a particle
    @param numXBins The number of bins in the x axis
    @param numYBins The number of bins in the y axis
    @param seed     The random number seed
    @returns A {float2} vector
*/
float2 particleRandomize(
                       int numXBins, int numYBins,
                       __global ulong* seed
                       )
{
    float n = (random(seed, numYBins*10-1)/(numYBins*10.0f))*M_PI;
    float c = cos(n);
    float y = c*90.0f+180.0f;
    if (y >= 180.0f) y -= 180.0f;
    y *= numYBins/180.0f;
    return (float2)( random(seed, numXBins*10-1)/10.0
                   , y
                   );
}

/** This randomizes a single particle
    @param positions   Set of vectors for each particle
    @param positions_t The set of new vectors for each particle
    @param numXBins The number of bins in the x axis
    @param numYBins The number of bins in the y axis
    @param dataGrid The grid of vectors (mapped to an array)
    @param seed     The random number seed
 */
__kernel void particleInit( __global float2*   vertex   // position of each particle
                          , int numXBins, int  numYBins    // The size of the data grid
                          , __global ulong*    seed        // A randomizer
                          )
{
    // The global id of the work item.  (the index i)
    int idx = get_global_id(0);
    // Particle isn't visible, but it still moves through the field.
    // particle has escaped the grid, never to return...
    float2 position = particleRandomize(numXBins, numYBins, seed);

    // Path from (x,y) to (xt,yt) is visible, so add this particle to the appropriate draw bucket.
    vertex[4*idx  ] = position;
    vertex[4*idx+1] = position;
    vertex[4*idx+2] = position;
    vertex[4*idx+3] = position;
}

/** This moves a single particle
    @param positions   Set of vectors for each particle
    @param positions_t The set of new vectors for each particle
    @param numXBins    The number of bins in the x axis
    @param numYBins    The number of bins in the y axis
    @param vectorField The field of vectors (mapped to an array), with normalized magnitude within the field
    @param seed     The random number seed
*/
__kernel void particleMove(  __global float2*   vertex    // position of each particle box
                           , float              dT          // Range from [0, 1]
                           , int numXBins, int  numYBins    // The size of the vector field
                           , __global float2*   vectorField // The data in the grid
                           , __global ulong*    seed        // A randomizer
                           )
{
    // The global id of the work item.  (the index i)
    int idx = get_global_id(0);

    // The positions are actually vertices (4)
    int idx4 = 4*idx;

    // The left two are the key ones
    // Get the particle position
    float2 position = vertex[idx4]+dT*(vertex[idx4+1]-vertex[idx4]);
    float m;
    float2 position_t, position_t2;
    float2 position2;
    float dX=0;
    if (position.x <= 0.0)
    {
        // We wrapped around
        float2 p = vertex[idx4+1];

        position.x = numXBins-0.01;
        // We could calculate the actual y position but this works better for now
        position.y = (position.y+p.y)*0.5;
        position2.x= numXBins+0.2;
        position2.y= position.y+0.9;
    }
    else if (position.x >= numXBins)
    {
        float2 p = vertex[idx4+1];
        position.x = 0.0;
        // We could calculate the actual y position but this works better for now
        position.y = (position.y+p.y)*0.5;
        position2.x= 0.2;
        position2.y= position.y+0.9;
        dX = -0.5;
    }
    else
    {
        position2 = (float2)(position.x+1.0f, position.y+1.0f);
    }

    if (position.y >= numYBins || position.y < 0)
    {
        // The particle is moving too fast or too slow; get rid of it
        position_t = particleRandomize(numXBins, numYBins, seed);
        position_t2= position_t;
        position = position_t;
        position2= position_t;
    }
    else
    {
        // vector at current position
        float2 v = vectorField[(int)position.x + ((int) position.y)*numXBins];
        m = v.x*v.x + v.y*v.y;

        position_t = position + v;
        position_t2= position2+ v;
        position.x += dX;

        position_t2.x+=0.5f;

        if (m <1.0)
        {
            // The particle is moving too fast or too slow; get rid of it
            position_t = particleRandomize(numXBins, numYBins, seed);
            position_t2= position_t;
            position = position_t;
            position2= position_t;
        }
    }

    // Path from (x,y) to (xt,yt) is visible, so add this particle to the appropriate draw bucket.
    vertex[idx4]   = position;
    vertex[idx4+1] = position_t;
    vertex[idx4+2] = position_t2;
    vertex[idx4+3] = position2;
}


BUG: Wrong laplacian for 19-point stencil.

#define __NINETEEN_STENCIL__
#pragma OPENCL EXTENSION cl_khr_3d_image_writes : enable
__constant sampler_t sampler = CLK_NORMALIZED_COORDS_TRUE | CLK_ADDRESS_REPEAT | CLK_FILTER_NEAREST;

__kernel void calc_3d(__read_only image3d_t Phi,
					  __write_only image3d_t PhiNext,
					  const float M,
					  const float a_2,
					  const float a_4,
					  const float K,
					  const float dx,
					  const float dt)
{
	int4 coord = {get_global_id(0),get_global_id(1),get_global_id(2),0};
	int4 size = {get_global_size(0),get_global_size(1),get_global_size(2),0};
    float4 normalizedCoord = (float4)((float)coord.x/size.x, (float)coord.y/size.y, (float)coord.z/size.z, 0);
//    float4 normalizedCoord = (float4)((coord.x +0.5f)/size.x, (coord.y+0.5f)/size.y,(coord.z+0.5f)/size.z,0);
    float incrementx = 1.0f/size.x;
    float incrementy = 1.0f/size.y;
    float incrementz = 1.0f/size.z;
    float4 phi = (read_imagef(Phi, sampler, normalizedCoord).x);
    float4 xm= (read_imagef(Phi,sampler,(normalizedCoord+(float4){-incrementx,0,0,0})));
    float4 xp= (read_imagef(Phi,sampler,(normalizedCoord+(float4){incrementx,0,0,0})));
    float4 ym= (read_imagef(Phi,sampler,(normalizedCoord+(float4){0,-incrementy,0,0})));
    float4 yp= (read_imagef(Phi,sampler,(normalizedCoord+(float4){0,incrementy,0,0})));
    float4 zm= (read_imagef(Phi,sampler,(normalizedCoord+(float4){0,0,-incrementz,0})));
    float4 zp= (read_imagef(Phi,sampler,(normalizedCoord+(float4){0,0,incrementz,0})));
#ifndef __NINETEEN_STENCIL__
    float4 laplacian =(xm+xp+ym+yp+zm+zp-6.0f*phi)/(dx*dx);  // 7-point stencil
#else
    float4 xym= (read_imagef(Phi,sampler,(normalizedCoord+(float4){-incrementx,-incrementy,0,0})));
    float4 xyp= (read_imagef(Phi,sampler,(normalizedCoord+(float4){incrementx,incrementy,0,0})));
    float4 xmyp= (read_imagef(Phi,sampler,(normalizedCoord+(float4){-incrementx,incrementy,0,0})));
    float4 xpym= (read_imagef(Phi,sampler,(normalizedCoord+(float4){incrementx,-incrementy,0,0})));

    float4 xzm= (read_imagef(Phi,sampler,(normalizedCoord+(float4){-incrementx,0,-incrementz,0})));
    float4 xzp= (read_imagef(Phi,sampler,(normalizedCoord+(float4){incrementx,0,incrementz,0})));
    float4 xmzp= (read_imagef(Phi,sampler,(normalizedCoord+(float4){-incrementx,0,incrementz,0})));
    float4 xpzm= (read_imagef(Phi,sampler,(normalizedCoord+(float4){incrementx,0,-incrementz,0})));

    float4 yzm= (read_imagef(Phi,sampler,(normalizedCoord+(float4){0,-incrementy,-incrementz,0})));
    float4 yzp= (read_imagef(Phi,sampler,(normalizedCoord+(float4){0,incrementy,incrementz,0})));
    float4 ymzp= (read_imagef(Phi,sampler,(normalizedCoord+(float4){0,-incrementy,incrementz,0})));
    float4 ypzm= (read_imagef(Phi,sampler,(normalizedCoord+(float4){0,incrementy,-incrementz,0})));

    float4 laplacian = ((xm+xp+ym+yp+zm+zp)/3.0f
                        + (xyp+xym+xpym+xmyp+xzm+xzp+xmzp+xpzm+yzm+yzp+ymzp+ypzm)/6.0f
                        - 4.0f*phi)/(dx*dx); // 19-point stencil
#undef __NINETEEN_STENCIL__
#endif
    float4 phi_next = phi + dt * M * ( 2.0f * K * laplacian - a_2 * phi - a_4 * phi * phi * phi);
    write_imagef(PhiNext,coord,phi_next);
}

#define __NINETEEN_STENCIL__
#pragma OPENCL EXTENSION cl_khr_3d_image_writes : enable
__constant sampler_t sampler = CLK_NORMALIZED_COORDS_TRUE | CLK_ADDRESS_REPEAT | CLK_FILTER_NEAREST;

__kernel void calc_3d(__read_only image3d_t Phi,
					  __write_only image3d_t PhiNext,
					  const float M,
					  const float a_2,
					  const float a_4,
					  const float K,
					  const float dx,
					  const float dt)
{
	int4 coord = {get_global_id(0),get_global_id(1),get_global_id(2),0};
	int4 size = {get_global_size(0),get_global_size(1),get_global_size(2),0};
    float4 normalizedCoord = (float4)((float)coord.x/size.x, (float)coord.y/size.y, (float)coord.z/size.z, 0);
//    float4 normalizedCoord = (float4)((coord.x +0.5f)/size.x, (coord.y+0.5f)/size.y,(coord.z+0.5f)/size.z,0);
    float incrementx = 1.0f/size.x;
    float incrementy = 1.0f/size.y;
    float incrementz = 1.0f/size.z;
    float4 phi = (read_imagef(Phi, sampler, normalizedCoord).x);
    float4 xm= (read_imagef(Phi,sampler,(normalizedCoord+(float4){-incrementx,0,0,0})));
    float4 xp= (read_imagef(Phi,sampler,(normalizedCoord+(float4){incrementx,0,0,0})));
    float4 ym= (read_imagef(Phi,sampler,(normalizedCoord+(float4){0,-incrementy,0,0})));
    float4 yp= (read_imagef(Phi,sampler,(normalizedCoord+(float4){0,incrementy,0,0})));
    float4 zm= (read_imagef(Phi,sampler,(normalizedCoord+(float4){0,0,-incrementz,0})));
    float4 zp= (read_imagef(Phi,sampler,(normalizedCoord+(float4){0,0,incrementz,0})));
#ifndef __NINETEEN_STENCIL__
    float4 laplacian =(xm+xp+ym+yp+zm+zp-6.0f*phi)/(dx*dx);  // 7-point stencil
#else
    float4 xym= (read_imagef(Phi,sampler,(normalizedCoord+(float4){-incrementx,-incrementy,0,0})));
    float4 xyp= (read_imagef(Phi,sampler,(normalizedCoord+(float4){incrementx,incrementy,0,0})));
    float4 xmyp= (read_imagef(Phi,sampler,(normalizedCoord+(float4){-incrementx,incrementy,0,0})));
    float4 xpym= (read_imagef(Phi,sampler,(normalizedCoord+(float4){incrementx,-incrementy,0,0})));

    float4 xzm= (read_imagef(Phi,sampler,(normalizedCoord+(float4){-incrementx,0,-incrementz,0})));
    float4 xzp= (read_imagef(Phi,sampler,(normalizedCoord+(float4){incrementx,0,incrementz,0})));
    float4 xmzp= (read_imagef(Phi,sampler,(normalizedCoord+(float4){-incrementx,0,incrementz,0})));
    float4 xpzm= (read_imagef(Phi,sampler,(normalizedCoord+(float4){incrementx,0,-incrementz,0})));

    float4 yzm= (read_imagef(Phi,sampler,(normalizedCoord+(float4){0,-incrementy,-incrementz,0})));
    float4 yzp= (read_imagef(Phi,sampler,(normalizedCoord+(float4){0,incrementy,incrementz,0})));
    float4 ymzp= (read_imagef(Phi,sampler,(normalizedCoord+(float4){0,-incrementy,incrementz,0})));
    float4 ypzm= (read_imagef(Phi,sampler,(normalizedCoord+(float4){0,incrementy,-incrementz,0})));

    float4 laplacian = ((xm+xp+ym+yp+zm+zp)/3.0f
                        + (xyp+xym+xpym+xmyp+xzm+xzp+xmzp+xpzm+yzm+yzp+ymzp+ypzm)/6.0f
                        - 24.0f*phi)/(dx*dx); // 19-point stencil
#undef __NINETEEN_STENCIL__
#endif
    float4 phi_next = phi + dt * M * ( 2.0f * K * laplacian - a_2 * phi - a_4 * phi * phi * phi);
    write_imagef(PhiNext,coord,phi_next);
}


svm_atomics_host.cl: fix __pocl_atomic_fetch_min & __pocl_atomic_fetch_max

code was written back when Clang didn't have builtins for C11 min/max atomics

/* OpenCL built-in library: OpenCL 2.0 Atomics (C11 subset) implementation for host device

   Copyright (c) 2015 Michal Babej / Tampere University of Technology

   This relies on Clang's C11 atomic builtins.

   Permission is hereby granted, free of charge, to any person obtaining a copy
   of this software and associated documentation files (the "Software"), to deal
   in the Software without restriction, including without limitation the rights
   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   copies of the Software, and to permit persons to whom the Software is
   furnished to do so, subject to the following conditions:

   The above copyright notice and this permission notice shall be included in
   all copies or substantial portions of the Software.

   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
   THE SOFTWARE.
*/

#ifndef _SVM_ATOMICS_H
#include "svm_atomics.h"

#define CONV_ORDER(mo) ((mo==memory_order_relaxed) ? __ATOMIC_RELAXED : \
                       ((mo==memory_order_acquire) ? __ATOMIC_ACQUIRE : \
                       ((mo==memory_order_release) ? __ATOMIC_RELEASE : \
                       ((mo==memory_order_acq_rel) ? __ATOMIC_ACQ_REL : \
                                                     __ATOMIC_SEQ_CST ))))
#define _SVM_ATOMICS_H
#endif

#if !defined(Q)

#  define Q __global
#  define QUAL(f) f ## __global
#  include "svm_atomics_host.cl"
#  undef Q
#  undef QUAL

#  define Q __local
#  define QUAL(f) f ## __local
#  include "svm_atomics_host.cl"
#  undef Q
#  undef QUAL

#elif !defined(ATOMIC_TYPE)

bool _CL_OVERLOADABLE QUAL(__pocl_atomic_flag_test_and_set) ( volatile Q atomic_int  *object ,
  memory_order order,
  memory_scope scope)
{
  return __c11_atomic_exchange(object, 1, CONV_ORDER(order));
}

void _CL_OVERLOADABLE QUAL(__pocl_atomic_flag_clear) ( volatile Q atomic_int  *object ,
  memory_order order,
  memory_scope scope)
{
  __c11_atomic_store(object, 0, CONV_ORDER(order));
}

#  define ATOMIC_TYPE atomic_int
#  define NONATOMIC_TYPE int
#  define IS_INT
#  include "svm_atomics_host.cl"
#  undef IS_INT
#  undef ATOMIC_TYPE
#  undef NONATOMIC_TYPE

#  define ATOMIC_TYPE atomic_uint
#  define NONATOMIC_TYPE uint
#  define IS_UINT
#  include "svm_atomics_host.cl"
#  undef IS_UINT
#  undef ATOMIC_TYPE
#  undef NONATOMIC_TYPE

#  define ATOMIC_TYPE atomic_float
#  define NONATOMIC_TYPE float
#  define NON_INTEGER
#  include "svm_atomics_host.cl"
#  undef NON_INTEGER
#  undef ATOMIC_TYPE
#  undef NONATOMIC_TYPE

#if defined(cl_khr_int64_base_atomics) && defined(cl_khr_int64_extended_atomics)

#  define ATOMIC_TYPE atomic_long
#  define NONATOMIC_TYPE long
#  include "svm_atomics_host.cl"
#  undef ATOMIC_TYPE
#  undef NONATOMIC_TYPE

#  define ATOMIC_TYPE atomic_ulong
#  define NONATOMIC_TYPE ulong
#  include "svm_atomics_host.cl"
#  undef ATOMIC_TYPE
#  undef NONATOMIC_TYPE

#endif

#ifdef cl_khr_fp64

#  define ATOMIC_TYPE atomic_double
#  define NONATOMIC_TYPE double
#  define NON_INTEGER
#  include "svm_atomics_host.cl"
#  undef NON_INTEGER
#  undef ATOMIC_TYPE
#  undef NONATOMIC_TYPE

#endif

#else

/************************************************************************/

_CL_OVERLOADABLE void QUAL(__pocl_atomic_store)( volatile Q ATOMIC_TYPE  *object,
                              NONATOMIC_TYPE  desired,
                              memory_order order,
                              memory_scope scope)
{
  __c11_atomic_store(object, desired, CONV_ORDER(order));
}

_CL_OVERLOADABLE NONATOMIC_TYPE QUAL(__pocl_atomic_load) ( volatile Q ATOMIC_TYPE  *object,
                                        memory_order order,
                                        memory_scope scope)
{
  return __c11_atomic_load(object, CONV_ORDER(order));
}

_CL_OVERLOADABLE NONATOMIC_TYPE QUAL(__pocl_atomic_exchange) ( volatile Q ATOMIC_TYPE  *object,
                                            NONATOMIC_TYPE  desired,
                                            memory_order order,
                                            memory_scope scope)
{
  return __c11_atomic_exchange(object, desired, CONV_ORDER(order));
}

bool _CL_OVERLOADABLE QUAL(__pocl_atomic_compare_exchange_strong) ( volatile Q ATOMIC_TYPE  *object,
  private NONATOMIC_TYPE  *expected,
  NONATOMIC_TYPE  desired,
  memory_order success,
  memory_order failure,
  memory_scope scope)
{
  return __c11_atomic_compare_exchange_strong(object,  expected, desired, CONV_ORDER(success), CONV_ORDER(failure));
}

bool _CL_OVERLOADABLE QUAL(__pocl_atomic_compare_exchange_weak) ( volatile Q ATOMIC_TYPE  *object,
  private NONATOMIC_TYPE  *expected,
  NONATOMIC_TYPE  desired,
  memory_order success,
  memory_order failure,
  memory_scope scope)
{
  return __c11_atomic_compare_exchange_weak(object,  expected, desired, CONV_ORDER(success), CONV_ORDER(failure));
}

#ifndef NON_INTEGER

NONATOMIC_TYPE _CL_OVERLOADABLE QUAL(__pocl_atomic_fetch_add) ( volatile Q ATOMIC_TYPE  *object,
  NONATOMIC_TYPE  operand,
  memory_order order,
  memory_scope scope)
{
  return __c11_atomic_fetch_add(object, operand, CONV_ORDER(order));
}

NONATOMIC_TYPE _CL_OVERLOADABLE QUAL(__pocl_atomic_fetch_sub) ( volatile Q ATOMIC_TYPE  *object,
  NONATOMIC_TYPE  operand,
  memory_order order,
  memory_scope scope)
{
  return __c11_atomic_fetch_sub(object, operand, CONV_ORDER(order));
}

NONATOMIC_TYPE _CL_OVERLOADABLE QUAL(__pocl_atomic_fetch_or) ( volatile Q ATOMIC_TYPE  *object,
  NONATOMIC_TYPE  operand,
  memory_order order,
  memory_scope scope)
{
  return __c11_atomic_fetch_or(object, operand, CONV_ORDER(order));
}

NONATOMIC_TYPE _CL_OVERLOADABLE QUAL(__pocl_atomic_fetch_xor) ( volatile Q ATOMIC_TYPE  *object,
  NONATOMIC_TYPE  operand,
  memory_order order,
  memory_scope scope)
{
  return __c11_atomic_fetch_xor(object, operand, CONV_ORDER(order));
}

NONATOMIC_TYPE _CL_OVERLOADABLE QUAL(__pocl_atomic_fetch_and) ( volatile Q ATOMIC_TYPE  *object,
  NONATOMIC_TYPE  operand,
  memory_order order,
  memory_scope scope)
{
  return __c11_atomic_fetch_and(object, operand, CONV_ORDER(order));
}

NONATOMIC_TYPE _CL_OVERLOADABLE QUAL(__pocl_atomic_fetch_min) ( volatile Q ATOMIC_TYPE  *object,
  NONATOMIC_TYPE  operand,
  memory_order order,
  memory_scope scope)
{
  return __c11_atomic_fetch_min(object, operand, CONV_ORDER(order));
}

NONATOMIC_TYPE _CL_OVERLOADABLE QUAL(__pocl_atomic_fetch_max) ( volatile Q ATOMIC_TYPE  *object,
  NONATOMIC_TYPE  operand,
  memory_order order,
  memory_scope scope)
{
  return __c11_atomic_fetch_max(object, operand, CONV_ORDER(order));
}

#endif

/************************************************************************/

#endif

/* OpenCL built-in library: OpenCL 2.0 Atomics (C11 subset) implementation for host device

   Copyright (c) 2015 Michal Babej / Tampere University of Technology

   This relies on Clang's C11 atomic builtins.

   Note: for some architectures, the host-specific llvm bitcode is used instead
   of this file (since Clang doesn't have proper builtins for 64bit min/max atomics,
   yet LLVM's atomicrmw can do them; using this file gives only limited min/max
   atomics).

   Permission is hereby granted, free of charge, to any person obtaining a copy
   of this software and associated documentation files (the "Software"), to deal
   in the Software without restriction, including without limitation the rights
   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   copies of the Software, and to permit persons to whom the Software is
   furnished to do so, subject to the following conditions:

   The above copyright notice and this permission notice shall be included in
   all copies or substantial portions of the Software.

   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
   THE SOFTWARE.
*/

#ifndef _SVM_ATOMICS_H
#include "svm_atomics.h"

#define CONV_ORDER(mo) ((mo==memory_order_relaxed) ? __ATOMIC_RELAXED : \
                       ((mo==memory_order_acquire) ? __ATOMIC_ACQUIRE : \
                       ((mo==memory_order_release) ? __ATOMIC_RELEASE : \
                       ((mo==memory_order_acq_rel) ? __ATOMIC_ACQ_REL : \
                                                     __ATOMIC_SEQ_CST ))))
#define _SVM_ATOMICS_H
#endif

#if !defined(Q)

#  define Q __global
#  define QUAL(f) f ## __global
#  include "svm_atomics_host.cl"
#  undef Q
#  undef QUAL

#  define Q __local
#  define QUAL(f) f ## __local
#  include "svm_atomics_host.cl"
#  undef Q
#  undef QUAL

#elif !defined(ATOMIC_TYPE)

bool _CL_OVERLOADABLE QUAL(__pocl_atomic_flag_test_and_set) ( volatile Q atomic_int  *object ,
  memory_order order,
  memory_scope scope)
{
  return __c11_atomic_exchange(object, 1, CONV_ORDER(order));
}

void _CL_OVERLOADABLE QUAL(__pocl_atomic_flag_clear) ( volatile Q atomic_int  *object ,
  memory_order order,
  memory_scope scope)
{
  __c11_atomic_store(object, 0, CONV_ORDER(order));
}

#  define ATOMIC_TYPE atomic_int
#  define NONATOMIC_TYPE int
#  define IS_INT
#  include "svm_atomics_host.cl"
#  undef IS_INT
#  undef ATOMIC_TYPE
#  undef NONATOMIC_TYPE

#  define ATOMIC_TYPE atomic_uint
#  define NONATOMIC_TYPE uint
#  define IS_UINT
#  include "svm_atomics_host.cl"
#  undef IS_UINT
#  undef ATOMIC_TYPE
#  undef NONATOMIC_TYPE

#  define ATOMIC_TYPE atomic_float
#  define NONATOMIC_TYPE float
#  define NON_INTEGER
#  include "svm_atomics_host.cl"
#  undef NON_INTEGER
#  undef ATOMIC_TYPE
#  undef NONATOMIC_TYPE

#if defined(cl_khr_int64_base_atomics) && defined(cl_khr_int64_extended_atomics)

#  define ATOMIC_TYPE atomic_long
#  define NONATOMIC_TYPE long
#  include "svm_atomics_host.cl"
#  undef ATOMIC_TYPE
#  undef NONATOMIC_TYPE

#  define ATOMIC_TYPE atomic_ulong
#  define NONATOMIC_TYPE ulong
#  include "svm_atomics_host.cl"
#  undef ATOMIC_TYPE
#  undef NONATOMIC_TYPE

#endif

#ifdef cl_khr_fp64

#  define ATOMIC_TYPE atomic_double
#  define NONATOMIC_TYPE double
#  define NON_INTEGER
#  include "svm_atomics_host.cl"
#  undef NON_INTEGER
#  undef ATOMIC_TYPE
#  undef NONATOMIC_TYPE

#endif

#else

/************************************************************************/

_CL_OVERLOADABLE void QUAL(__pocl_atomic_store)( volatile Q ATOMIC_TYPE  *object,
                              NONATOMIC_TYPE  desired,
                              memory_order order,
                              memory_scope scope)
{
  __c11_atomic_store(object, desired, CONV_ORDER(order));
}

_CL_OVERLOADABLE NONATOMIC_TYPE QUAL(__pocl_atomic_load) ( volatile Q ATOMIC_TYPE  *object,
                                        memory_order order,
                                        memory_scope scope)
{
  return __c11_atomic_load(object, CONV_ORDER(order));
}

_CL_OVERLOADABLE NONATOMIC_TYPE QUAL(__pocl_atomic_exchange) ( volatile Q ATOMIC_TYPE  *object,
                                            NONATOMIC_TYPE  desired,
                                            memory_order order,
                                            memory_scope scope)
{
  return __c11_atomic_exchange(object, desired, CONV_ORDER(order));
}

bool _CL_OVERLOADABLE QUAL(__pocl_atomic_compare_exchange_strong) ( volatile Q ATOMIC_TYPE  *object,
  private NONATOMIC_TYPE  *expected,
  NONATOMIC_TYPE  desired,
  memory_order success,
  memory_order failure,
  memory_scope scope)
{
  return __c11_atomic_compare_exchange_strong(object,  expected, desired, CONV_ORDER(success), CONV_ORDER(failure));
}

bool _CL_OVERLOADABLE QUAL(__pocl_atomic_compare_exchange_weak) ( volatile Q ATOMIC_TYPE  *object,
  private NONATOMIC_TYPE  *expected,
  NONATOMIC_TYPE  desired,
  memory_order success,
  memory_order failure,
  memory_scope scope)
{
  return __c11_atomic_compare_exchange_weak(object,  expected, desired, CONV_ORDER(success), CONV_ORDER(failure));
}

#ifndef NON_INTEGER

NONATOMIC_TYPE _CL_OVERLOADABLE QUAL(__pocl_atomic_fetch_add) ( volatile Q ATOMIC_TYPE  *object,
  NONATOMIC_TYPE  operand,
  memory_order order,
  memory_scope scope)
{
  return __c11_atomic_fetch_add(object, operand, CONV_ORDER(order));
}

NONATOMIC_TYPE _CL_OVERLOADABLE QUAL(__pocl_atomic_fetch_sub) ( volatile Q ATOMIC_TYPE  *object,
  NONATOMIC_TYPE  operand,
  memory_order order,
  memory_scope scope)
{
  return __c11_atomic_fetch_sub(object, operand, CONV_ORDER(order));
}

NONATOMIC_TYPE _CL_OVERLOADABLE QUAL(__pocl_atomic_fetch_or) ( volatile Q ATOMIC_TYPE  *object,
  NONATOMIC_TYPE  operand,
  memory_order order,
  memory_scope scope)
{
  return __c11_atomic_fetch_or(object, operand, CONV_ORDER(order));
}

NONATOMIC_TYPE _CL_OVERLOADABLE QUAL(__pocl_atomic_fetch_xor) ( volatile Q ATOMIC_TYPE  *object,
  NONATOMIC_TYPE  operand,
  memory_order order,
  memory_scope scope)
{
  return __c11_atomic_fetch_xor(object, operand, CONV_ORDER(order));
}

NONATOMIC_TYPE _CL_OVERLOADABLE QUAL(__pocl_atomic_fetch_and) ( volatile Q ATOMIC_TYPE  *object,
  NONATOMIC_TYPE  operand,
  memory_order order,
  memory_scope scope)
{
  return __c11_atomic_fetch_and(object, operand, CONV_ORDER(order));
}

NONATOMIC_TYPE _CL_OVERLOADABLE QUAL(__pocl_atomic_fetch_min) ( volatile Q ATOMIC_TYPE  *object,
  NONATOMIC_TYPE  operand,
  memory_order order,
  memory_scope scope)
{
#if defined(IS_INT)
  return __sync_fetch_and_min((volatile Q NONATOMIC_TYPE *)object, operand);
#elif defined(IS_UINT)
  return __sync_fetch_and_umin((volatile Q NONATOMIC_TYPE *)object, operand);
#else
  __builtin_trap();
  return 0;
#endif
}

NONATOMIC_TYPE _CL_OVERLOADABLE QUAL(__pocl_atomic_fetch_max) ( volatile Q ATOMIC_TYPE  *object,
  NONATOMIC_TYPE  operand,
  memory_order order,
  memory_scope scope)
{
#if defined(IS_INT)
  return __sync_fetch_and_max((volatile Q NONATOMIC_TYPE *)object, operand);
#elif defined(IS_UINT)
  return __sync_fetch_and_umax((volatile Q NONATOMIC_TYPE *)object, operand);
#else
  __builtin_trap();
  return 0;
#endif
}

#endif

/************************************************************************/

#endif


Update getSuperK2_M.cl

Update getSuperK2_M.cl

__kernel void getSuperK_M(
   __global uint* RSK_G, // Matrix of reads
   __global uint* counters, // Matrix of temporal output
   const unsigned int nr, // Number of reads
   const unsigned int r, // Read size
   const unsigned int k, // Kmer size
   const unsigned int m // m-mer size
   )
{
   uint x, y, yl, xl, p, start, offset, nmk, nm, min, a, b, c, d, idt, ts, nt, lsd, nkr, tmp, ls, start_tile, start_superk, nt1, nt2, nt3, nt4 = 0;
   bool flag;

   x = get_global_id(0);
   y = get_global_id(1);
   xl = get_local_id(0);
   yl = get_local_id(1);

  __local uint RSK[180]; // Vector to store reads, m-mers and superk-mers. This vector is overwritten in each stage, size: length of the read

  __local uint RCMT[7]; // Reverse complement of current m-mer of tile (32 bits), size: nt2
  __local uint MT[7]; // Current m-mer of tile (32 bits), size: Greater between nt2 and nt3
  __local uint counter, mp, nmp, minimizer; // minimizer position, new minimizer  position, minimizer

   nmk = k - m + 1;
   // Global to local
   // Reads in global memory to Read in local memory

   ts = get_local_size(0);
   nt1 = (r-1)/ts + 1; // nt: Number of tiles or number of sub-reads per read
   if ((x<ts) && (x<r) && (y<nr)){
      for (int i=0; i<nt1; i++) { // coalesced
        p=ts*i+x;
        if (p > r-1) {
          break;
        }
        RSK[p]=RSK_G[y*r+p];
      }
    }
    barrier(CLK_LOCAL_MEM_FENCE);

    // FUNCTION: GetM-mers
    // Read to decimal representation of m-mers
    lsd = get_local_size(0) / m; // lsd: Local space divisions
    nm = r - m + 1; // nm: Number of m-mers per read
    ts = ((nm-1)/lsd) + 1; // ts: Tile size for this process
    nt2 = ((nm-1)/ts) + 1 ; // nt2: Number of tiles for this process
    // a = (int) (pow(((double) 2, (double) (2*m)) - 1); // a:Mask (Sample: 63 for m = 4, to cancel all bits different to first 6)
    a = 4095; // Mask   2**(2m-2)cc

    if ((x < m*nt2) && (y<nr)) {
      // Parallel computing of the first m-mer of each tile

      idt = x/m; // Tile id
      start = ts*idt; // Position of the first base for each tile
      offset = x % m;
      p = (start) + (offset); // Position of the base corresponding a the thread
      MT[idt] = 0; MT Reset
      RCMT[idt] = 0; // RCMT Reset
      b = RSK[p]; // Each thread copies its base to b
      atomic_or(&MT[idt], (b << (m-(x%m)-1)*2)); // Calculating the first m-mer of each tile
      atomic_or(&RCMT[idt], (((~b) & 3) << (x%m)*2)); // Calculating the reverse complement of the first m-mer of each tile
    }
    barrier(CLK_LOCAL_MEM_FENCE);

    if ((x<nt2) && (y<nr)) { // Finding the first canonical m-mer of each tile
      // The canonical m-mers of a read are saved in the same vector where the read was stored (RSK)
      /* Each canonical m-mer is saved in the position where was its last base (This avoid the over-write of bases that Will be used by another tile) */
      idt = x;
      start = ts*idt;
      if (MT[idt]<RCMT[idt]){
        RSK[start + m -1] = MT[idt];
      } else {
      	RSK[start + m -1] = RCMT[idt];
      }
    }
    barrier(CLK_LOCAL_MEM_FENCE);

      // Serial computation of the m-mers remaining of each tile

    if ((x<nt2) && (y<nr))	{
       idt = x; // Tile id
       start = ts*idt; // Position of the first base for each tile
       c = MT[idt]; // Copy to c the decimal value of the first m-mer of each tile
       d = RCMT[idt]; // Copy to d the decimal value of the reverse complement of the first m-mer of each tile

       for (int z=0; z<(ts-1); z++) {
       		if (start+m+z > r-1) {
            break;
          }
           b = RSK[start+m+z]; // Copy to b the last base of the m-mer that will be calculated (current m-mer)
           c = ((c & a) << 2)|b;  // Calculating the current m-mer from the previous m-mer and the new base read (b)
           d = ((d>>2) & (a)) | (((~b) & 3)<<((m-1)*2)); // Calculating the reverse complement of the current m-mer from the reverse complement of the previous m-mer and the new base read (b)
           if (c<d)	{  // Finding the canonical m-mer
             RSK[start+m+z]=c;
            } else {
             RSK[start+m+z]=d;
            }
           }
      }

      barrier(CLK_LOCAL_MEM_FENCE);

   // PROCESS: getSuperks
   // Canonical m-mers to minimizers

      nt = ((nm-1)/ts) + 1; // nt: Number of tiles or number of sub-reads per read

   /* Initialization */
   if (x==0) {
     counter = 0;
     mp = 0;
     nmp = 0xFFFFFFFF;
     minimizer = 0xFFFFFFFF;
   }

  barrier(CLK_LOCAL_MEM_FENCE);

  start_tile = 0; // Start of the currently evaluated zone
  start_superk = 0;
  if (k <= 36) {  // Choosing the tile size for the reduction algorithm used in this process
    nt3 = 6;
  } else if (k <= 64) {
    nt3 = 8;
  } else if (k <= 100) {
    nt3 = 10;
  } else {
    nt3 = 12;
  }

   // Evaluating the first k-mers of each read

/* To find the minimizer of the first k-mers of each reading a two-stage atomic reduction methodology is used: First, the canonical m-mers of the k-mer are grouped into a number of sets equal to nt3; for each set an atomic operation is performed to find the minimum canonical m-mer of each set. Secondly, the minimums found in the previous step are reduced to one minimum, which is the minimizer of the k-mer. */

  if (x<nmk) {
    p = x;
    b = RSK[p+m-1];  // Each thread copies its canonical m-mer to b
  }

  if (x < nt3){
    MT[x] = b;  // The first “nt3” canonical m-mers are copied to MT
  }

  barrier(CLK_LOCAL_MEM_FENCE);

  if (x >= nt3 && x<nmk) {
    atomic_min(&MT[x%nt3], b);
  }

  barrier(CLK_LOCAL_MEM_FENCE);

  if (x<nt3) {
    atomic_min(&minimizer, MT[x]);
  }

  barrier(CLK_LOCAL_MEM_FENCE);

  if ((x<nmk) && (b==minimizer)){
    atomic_max(&mp, p);
  }

  barrier(CLK_LOCAL_MEM_FENCE);

  // Processing the remaining k-mers

  while ((start_tile+nmk-1)<(nm-1)) {
    flag = false;  // Flag to indicate if the position of the current minimizer is equal to start_zone (Start of the last k-mer evaluated)
    if ((x<nmk) && (mp == start_tile)){
      // If the position of the current minimizer is equal to start_zone, the flag is set to true and start_zone is incremented by one
      flag = true;
      start_tile = start_tile + 1;
      if (p < start_tile) {
        p = p + nmk;
        b = RSK[p + m - 1];
      }
    }

    if ( (x==0) && flag ) {

       /* If the flag is true it means that the current minimizer is no longer part of the next k-mer to evaluate, as consequence there ends a super k-mer. The code below is to store the representation of the super k-mer in the vector RSK, additionally counter is incremented and star_superk and minimizer are set. */

      a = (start_tile + k - 1 - start_superk) & 0x000000FF;
      RSK[counter] = ((minimizer<<18) & 0xFFFC0000) | ((start_superk<<8) & 0x0003FF00) | a;
      counter++;
      start_superk  = start_tile;
      minimizer = 0xFFFFFFFF;
    }

     /* If the flag is true, the next k-mer to evaluate does not have minimizer of reference, therefore this k-mer is evaluated in the same way as the first k-mer.*/

    if ((x<nt3) && (flag)) {
      MT[x] = b;
    }

    barrier(CLK_LOCAL_MEM_FENCE);

    if ((x >= nt3) && (x<nmk) && flag) {
      atomic_min(&MT[x%nt3], b);
    }

    barrier(CLK_LOCAL_MEM_FENCE);

    if ((x < nt3) && flag) {
      atomic_min(&minimizer, MT[x]);
    }

    barrier(CLK_LOCAL_MEM_FENCE);

    if ((x<nmk) && flag && (b == minimizer)) {
      atomic_max(&mp, p);
    }

    /* If the flag is false it means that The current minimizer is in a different position to the start of the last k-mer evaluated, therefore it is possible that the following k-mers share the same minimizer */

   /* From all the next k-mers that have the possibility to share the same minimizer only the last one is evaluated: its canónical m-mers (different to the first) are compared with the current minimizer (its first canonical m-mer), if there are canonical m-mers lower than the current minimizer, the new minimizer will be the one with the lowest position. */

    barrier(CLK_LOCAL_MEM_FENCE);

    if ((x<nmk) && flag==false) {
      start_tile = mp;
      if (p<start_tile) {
        p = p + nmk;
        if ( p < (nm -1)) {
          b = RSK[p+m-1];
          if (b < minimizer) {
            atomic_min(&nmp, p);
          }
        }
      }
    }

    barrier(CLK_LOCAL_MEM_FENCE);

     /* If a new minimizer is found, it means that a super k-mer ends. The code below is to save the representation of the super k-mer in the vector RSK, additionally counter is incremented and star_superk, minimizer, mp and nmp are set. */

    if ((x==0) && (flag==false) && (nmp != 0xFFFFFFFF)) {
      a = (nmp - start_superk + m - 1) & 0x000000FF;
      RSK[counter] = ((minimizer <<  18) & 0xFFFC0000) | ((start_superk << 8) & 0x0003FF00) | a;
      mp = nmp;
      minimizer = RSK[mp+m-1];
      counter++;
      start_superk = mp - nmk + 1;
      nmp = 0xFFFFFFFF;
    }

    barrier(CLK_LOCAL_MEM_FENCE);

  }

   /* When each read is fully evaluated, it is necessary to store the representation of last super k-mer in the vector RSK  */

  if (x==0) {
    a = (nm - start_superk + m - 1) & 0x000000FF;
    RSK[counter] = ((minimizer << 18) & 0xFFFC0000) | ((start_superk<<8) & 0x0003FF00) | a;
    counter ++;
  }

  barrier(CLK_LOCAL_MEM_FENCE);

   // PROCESS: local2Global
    //Reads in local memory to Read in global memory

    ts = get_local_size(0);  // ts: Tile size, work group size
    nt4 = (r-1)/ts + 1; // nt: Number of tiles for this process

    if ((x<ts) && (x<counter) && (y<nr)) {
      for (int i=0; i<nt4; i++){  // coalesced
        p=ts*i+x;
        if (p > counter-1)
          {
            break;
          }
        RSK_G[y*r+p] = RSK[p];
      }
    }

   // Extract counters
    if ((x == 0) && (y<nr))
     {
             counters[y] = counter;
      }
}

__kernel void getSuperK_M(
   __global uint* RSK_G, // Matrix of reads
   __global uint* counters, // Matrix of temporal output
   const unsigned int nr, // Number of reads
   const unsigned int r, // Read size
   const unsigned int k, // Kmer size
   const unsigned int m // m-mer size
   )
{
   uint x, y, yl, xl, p, start, offset, nmk, nm, min, a, b, c, d, idt, ts, nt, lsd, nkr, tmp, ls, start_tile, start_superk, nt1, nt2, nt3, nt4 = 0;
   bool flag;

   x = get_global_id(0);
   y = get_global_id(1);
   xl = get_local_id(0);
   yl = get_local_id(1);

  __local uint RSK[180]; // Vector of a read and super k-mers (32 bits) , len = lenght of reads

  __local uint RCMT[7]; // Position of minimizer in each tile, (nm-1)/(ts)   +  1, ts -> (nm-1)/lsd   + 1, nm=r-m-1, lsd=localSpaceSize/m
  __local uint MT[7]; // max(nt1, nt2) , max(nt anterior , nt nuevo)
  __local uint counter, mp, nmp, minimizer; // minimizer position, new minimizer  position, minimizer

   nmk = k - m + 1;
   // Global to local
   ts = get_local_size(0);
   nt1 = (r-1)/ts + 1; // nt: Number of tiles or number of sub-reads per read
   if ((x<ts) && (x<r) && (y<nr)){
      for (int i=0; i<nt1; i++) { // coalesced
        p=ts*i+x;
        if (p > r-1) {
          break;
        }
        RSK[p]=RSK_G[y*r+p];
      }
    }
    barrier(CLK_LOCAL_MEM_FENCE);

    // FUNCTION: GetM-mers
    // Read to decimal representation of m-mers
    lsd = get_local_size(0) / m;
    nm = r - m + 1; // nm: Number of m-mers per read
    ts = ((nm-1)/lsd) + 1; // ts: Tile size
    nt2 = ((nm-1)/ts) + 1 ; // nt: Number of tiles or number of sub-reads per read
    // a = (int) (pow(((double) 2, (double) (2*m)) - 1); // Mask
    a = 4095; // Mask   2**(2m-2)cc

    if ((x < m*nt2) && (y<nr)) {
      // Cómputo en pllo del primer m-mer de cada tile
      idt = x/m;
      start = ts*idt;
      offset = x % m;
      p = (start) + (offset);
      MT[idt] = 0;
      RCMT[idt] = 0;
      b = RSK[p];
      atomic_or(&MT[idt], (b << (m-(x%m)-1)*2));
      atomic_or(&RCMT[idt], (((~b) & 3) << (x%m)*2));
    }
    barrier(CLK_LOCAL_MEM_FENCE);

    if ((x<nt2) && (y<nr)) {
      idt = x;
      start = ts*idt;
      if (MT[idt]<RCMT[idt]){
        RSK[start + m -1] = MT[idt];
      } else {
      	RSK[start + m -1] = RCMT[idt];
      }
    }
    barrier(CLK_LOCAL_MEM_FENCE);

      // Cómputo en serie del resto de m-mers de cada tile
    if ((x<nt2) && (y<nr))	{
       idt = x;
       start = ts*idt;
       c = MT[idt]; // Almacena en C el valor decimal del primer m-mer del tile
       d = RCMT[idt];

       for (int z=0; z<(ts-1); z++) {
       		if (start+m+z > r-1) {
            break;
          }
           b = RSK[start+m+z]; // Obtiene la siguiente base
           c = ((c & a) << 2)|b; // Obtiene el valor decimal del m-mer (A partir del resultado   // anterior y la nueva base)
           d = ((d>>2) & (a)) | (((~b) & 3)<<((m-1)*2));
           if (c<d)	{
             RSK[start+m+z]=c;
            } else {
             RSK[start+m+z]=d;
            }
           }
      }

      barrier(CLK_LOCAL_MEM_FENCE);

      nt = ((nm-1)/ts) + 1; // nt: Number of tiles or number of sub-reads per read

   /* Initialization */
   if (x==0) {
     counter = 0;
     mp = 0;
     nmp = 0xFFFFFFFF;
     minimizer = 0xFFFFFFFF;
   }

  barrier(CLK_LOCAL_MEM_FENCE);

  start_tile = 0;
  start_superk = 0;
  if (k <= 36) {
    nt3 = 6;
  } else if (k <= 64) {
    nt3 = 8;
  } else if (k <= 100) {
    nt3 = 10;
  } else {
    nt3 = 12;
  }

  if (x<nmk) {
    p = x;
    b = RSK[p+m-1];
  }

  if (x < nt3){
    MT[x] = b;
  }

  barrier(CLK_LOCAL_MEM_FENCE);

  if (x >= nt3 && x<nmk) {
    atomic_min(&MT[x%nt3], b);
  }

  barrier(CLK_LOCAL_MEM_FENCE);

  if (x<nt3) {
    atomic_min(&minimizer, MT[x]);
  }

  barrier(CLK_LOCAL_MEM_FENCE);

  if ((x<nmk) && (b==minimizer)){
    atomic_max(&mp, p);
  }

  barrier(CLK_LOCAL_MEM_FENCE);

  // The reamaining k-mers
  while ((start_tile+nmk-1)<(nm-1)) {
    flag = false;
    if ((x<nmk) && (mp == start_tile)){
      flag = true;
      start_tile = start_tile + 1;
      if (p < start_tile) {
        p = p + nmk;
        b = RSK[p + m - 1];
      }
    }

    if ( (x==0) && flag ) {
      a = (start_tile + k - 1 - start_superk) & 0x000000FF;
      RSK[counter] = ((minimizer<<18) & 0xFFFC0000) | ((start_superk<<8) & 0x0003FF00) | a;
      counter++;
      start_superk  = start_tile;
      minimizer = 0xFFFFFFFF;
    }

    if ((x<nt3) && (flag)) {
      MT[x] = b;
    }

    barrier(CLK_LOCAL_MEM_FENCE);

    if ((x >= nt3) && (x<nmk) && flag) {
      atomic_min(&MT[x%nt3], b);
    }

    barrier(CLK_LOCAL_MEM_FENCE);

    if ((x < nt3) && flag) {
      atomic_min(&minimizer, MT[x]);
    }

    barrier(CLK_LOCAL_MEM_FENCE);

    if ((x<nmk) && flag && (b == minimizer)) {
      atomic_max(&mp, p);
    }

    barrier(CLK_LOCAL_MEM_FENCE);

    if ((x<nmk) && flag==false) {
      start_tile = mp;
      if (p<start_tile) {
        p = p + nmk;
        if ( p < (nm -1)) {
          b = RSK[p+m-1];
          if (b < minimizer) {
            atomic_min(&nmp, p);
          }
        }
      }
    }

    barrier(CLK_LOCAL_MEM_FENCE);

    if ((x==0) && (flag==false) && (nmp != 0xFFFFFFFF)) {
      a = (nmp - start_superk + m - 1) & 0x000000FF;
      RSK[counter] = ((minimizer <<  18) & 0xFFFC0000) | ((start_superk << 8) & 0x0003FF00) | a;
      mp = nmp;
      minimizer = RSK[mp+m-1];
      counter++;
      start_superk = mp - nmk + 1;
      nmp = 0xFFFFFFFF;
    }

    barrier(CLK_LOCAL_MEM_FENCE);

  }

  if (x==0) {
    a = (nm - start_superk + m - 1) & 0x000000FF;
    RSK[counter] = ((minimizer << 18) & 0xFFFC0000) | ((start_superk<<8) & 0x0003FF00) | a;
    counter ++;
  }

  barrier(CLK_LOCAL_MEM_FENCE);

    //FUNCTION: Local2Global
    //Reads in local memory to Read in global memory
    ts = get_local_size(0);  // ts: Tile size, work group size
    nt4 = (r-1)/ts + 1; // nt: Number of tiles

    if ((x<ts) && (x<counter) && (y<nr)) {
      for (int i=0; i<nt4; i++){  // coalesced
        p=ts*i+x;
        if (p > counter-1)
          {
            break;
          }
        RSK_G[y*r+p] = RSK[p];
      }
    }

   // Extract counters
    if ((x == 0) && (y<nr))
     {
             counters[y] = counter;
      }
}