Fix SAFE_MATH option

Fix SAFE_MATH option

/**********************************************************************
Copyright (c) 2016 Advanced Micro Devices, Inc. All rights reserved.

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.
********************************************************************/

/*************************************************************************
DEFINES
**************************************************************************/
#define PI 3.14159265358979323846f
#define KERNEL __kernel
#define GLOBAL __global
#define INLINE __attribute__((always_inline))
#define HIT_MARKER 1
#define MISS_MARKER -1
#define INVALID_IDX -1

/*************************************************************************
EXTENSIONS
**************************************************************************/
#ifdef AMD_MEDIA_OPS
#pragma OPENCL EXTENSION cl_amd_media_ops2 : enable
#endif

/*************************************************************************
TYPES
**************************************************************************/

// Axis aligned bounding box
typedef struct
{
    float4 pmin;
    float4 pmax;
} bbox;

// Ray definition
typedef struct
{
    float4 o;
    float4 d;
    int2 extra;
    int2 padding;
} ray;

// Intersection definition
typedef struct
{
    int shape_id;
    int prim_id;
    int2 padding;

    float4 uvwt;
} Intersection;

/*************************************************************************
HELPER FUNCTIONS
**************************************************************************/
INLINE
int ray_get_mask(ray const* r)
{
    return r->extra.x;
}

INLINE
int ray_is_active(ray const* r)
{
    return r->extra.y;
}

INLINE
float ray_get_maxt(ray const* r)
{
    return r->o.w;
}

INLINE
float ray_get_time(ray const* r)
{
    return r->d.w;
}

/*************************************************************************
FUNCTIONS
**************************************************************************/
#ifndef APPLE
INLINE
float4 make_float4(float x, float y, float z, float w)
{
    float4 res;
    res.x = x;
    res.y = y;
    res.z = z;
    res.w = w;
    return res;
}
INLINE
float3 make_float3(float x, float y, float z)
{
    float3 res;
    res.x = x;
    res.y = y;
    res.z = z;
    return res;
}
INLINE
float2 make_float2(float x, float y)
{
    float2 res;
    res.x = x;
    res.y = y;
    return res;
}
INLINE
int2 make_int2(int x, int y)
{
    int2 res;
    res.x = x;
    res.y = y;
    return res;
}
INLINE
int3 make_int3(int x, int y, int z)
{
    int3 res;
    res.x = x;
    res.y = y;
    res.z = z;
    return res;
}
#endif

INLINE float min3(float a, float b, float c)
{
#ifdef AMD_MEDIA_OPS
    return amd_min3(a, b, c);
#else
    return min(min(a,b), c);
#endif
}

INLINE float max3(float a, float b, float c)
{
#ifdef AMD_MEDIA_OPS
    return amd_max3(a, b, c);
#else
    return max(max(a,b), c);
#endif
}

// Intersect ray against a triangle and return intersection interval value if it is in
// (0, t_max], return t_max otherwise.
INLINE
float fast_intersect_triangle(ray r, float3 v1, float3 v2, float3 v3, float t_max)
{
    float3 const e1 = v2 - v1;
    float3 const e2 = v3 - v1;
    float3 const s1 = cross(r.d.xyz, e2);

#ifdef USE_SAFE_MATH
    float const invd = 1.f / dot(s1, e1);
#else
    float const invd = native_recip(dot(s1, e1));
#endif

    float3 const d = r.o.xyz - v1;
    float const b1 = dot(d, s1) * invd;
    float3 const s2 = cross(d, e1);
    float const b2 = dot(r.d.xyz, s2) * invd;
    float const temp = dot(e2, s2) * invd;

    if (b1 < 0.f || b1 > 1.f || b2 < 0.f || b1 + b2 > 1.f || temp < 0.f || temp > t_max)
    {
        return t_max;
    }
    else
    {
        return temp;
    }
}

INLINE
float3 safe_invdir(ray r)
{
    float const dirx = r.d.x;
    float const diry = r.d.y;
    float const dirz = r.d.z;
    float const ooeps = 1e-8;
    float3 invdir;
    invdir.x = 1.0f / (fabs(dirx) > ooeps ? dirx : copysign(ooeps, dirx));
    invdir.y = 1.0f / (fabs(diry) > ooeps ? diry : copysign(ooeps, diry));
    invdir.z = 1.0f / (fabs(dirz) > ooeps ? dirz : copysign(ooeps, dirz));
    return invdir;
}

// Intersect rays vs bbox and return intersection span.
// Intersection criteria is ret.x <= ret.y
INLINE
float2 fast_intersect_bbox1(bbox box, float3 invdir, float3 oxinvdir, float t_max)
{
    float3 const f = mad(box.pmax.xyz, invdir, oxinvdir);
    float3 const n = mad(box.pmin.xyz, invdir, oxinvdir);
    float3 const tmax = max(f, n);
    float3 const tmin = min(f, n);
    float const t1 = min(min3(tmax.x, tmax.y, tmax.z), t_max);
    float const t0 = max(max3(tmin.x, tmin.y, tmin.z), 0.f);
    return make_float2(t0, t1);
}

// Given a point in triangle plane, calculate its barycentrics
INLINE
float2 triangle_calculate_barycentrics(float3 p, float3 v1, float3 v2, float3 v3)
{
    float3 const e1 = v2 - v1;
    float3 const e2 = v3 - v1;
    float3 const e = p - v1;
    float const d00 = dot(e1, e1);
    float const d01 = dot(e1, e2);
    float const d11 = dot(e2, e2);
    float const d20 = dot(e, e1);
    float const d21 = dot(e, e2);

#ifdef USE_SAFE_MATH
    float const invdenom = 1.f / (d00 * d11 - d01 * d01);
#else
    float const invdenom = native_recip(d00 * d11 - d01 * d01);
#endif

    float const b1 = (d11 * d20 - d01 * d21) * invdenom;
    float const b2 = (d00 * d21 - d01 * d20) * invdenom;
    return make_float2(b1, b2);
}

/**********************************************************************
Copyright (c) 2016 Advanced Micro Devices, Inc. All rights reserved.

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.
********************************************************************/

/*************************************************************************
DEFINES
**************************************************************************/
#define PI 3.14159265358979323846f
#define KERNEL __kernel
#define GLOBAL __global
#define INLINE __attribute__((always_inline))
#define HIT_MARKER 1
#define MISS_MARKER -1
#define INVALID_IDX -1

/*************************************************************************
EXTENSIONS
**************************************************************************/
#ifdef AMD_MEDIA_OPS
#pragma OPENCL EXTENSION cl_amd_media_ops2 : enable
#endif

/*************************************************************************
TYPES
**************************************************************************/

// Axis aligned bounding box
typedef struct
{
    float4 pmin;
    float4 pmax;
} bbox;

// Ray definition
typedef struct
{
    float4 o;
    float4 d;
    int2 extra;
    int2 padding;
} ray;

// Intersection definition
typedef struct
{
    int shape_id;
    int prim_id;
    int2 padding;

    float4 uvwt;
} Intersection;

/*************************************************************************
HELPER FUNCTIONS
**************************************************************************/
INLINE
int ray_get_mask(ray const* r)
{
    return r->extra.x;
}

INLINE
int ray_is_active(ray const* r)
{
    return r->extra.y;
}

INLINE
float ray_get_maxt(ray const* r)
{
    return r->o.w;
}

INLINE
float ray_get_time(ray const* r)
{
    return r->d.w;
}

/*************************************************************************
FUNCTIONS
**************************************************************************/
#ifndef APPLE
INLINE
float4 make_float4(float x, float y, float z, float w)
{
    float4 res;
    res.x = x;
    res.y = y;
    res.z = z;
    res.w = w;
    return res;
}
INLINE
float3 make_float3(float x, float y, float z)
{
    float3 res;
    res.x = x;
    res.y = y;
    res.z = z;
    return res;
}
INLINE
float2 make_float2(float x, float y)
{
    float2 res;
    res.x = x;
    res.y = y;
    return res;
}
INLINE
int2 make_int2(int x, int y)
{
    int2 res;
    res.x = x;
    res.y = y;
    return res;
}
INLINE
int3 make_int3(int x, int y, int z)
{
    int3 res;
    res.x = x;
    res.y = y;
    res.z = z;
    return res;
}
#endif

INLINE float min3(float a, float b, float c)
{
#ifdef AMD_MEDIA_OPS
    return amd_min3(a, b, c);
#else
    return min(min(a,b), c);
#endif
}

INLINE float max3(float a, float b, float c)
{
#ifdef AMD_MEDIA_OPS
    return amd_max3(a, b, c);
#else
    return max(max(a,b), c);
#endif
}

// Intersect ray against a triangle and return intersection interval value if it is in
// (0, t_max], return t_max otherwise.
INLINE
float fast_intersect_triangle(ray r, float3 v1, float3 v2, float3 v3, float t_max)
{
    float3 const e1 = v2 - v1;
    float3 const e2 = v3 - v1;
    float3 const s1 = cross(r.d.xyz, e2);

	#ifdef USE_SAFE_MATH
	float const invd = 1.0/(dot(s1, e1));
	#else
    float const invd = native_recip(dot(s1, e1));
	#endif

    float3 const d = r.o.xyz - v1;
    float const b1 = dot(d, s1) * invd;
    float3 const s2 = cross(d, e1);
    float const b2 = dot(r.d.xyz, s2) * invd;
    float const temp = dot(e2, s2) * invd;

    if (b1 < 0.f || b1 > 1.f || b2 < 0.f || b1 + b2 > 1.f || temp < 0.f || temp > t_max)
    {
        return t_max;
    }
    else
    {
        return temp;
    }
}

INLINE
float3 safe_invdir(ray r)
{
#ifdef USE_SAFE_MATH
    float const dirx = r.d.x;
    float const diry = r.d.y;
    float const dirz = r.d.z;
    float const ooeps = exp2(-80.0f); // Avoid div by zero.
    float3 invdir;
    invdir.x = 1.0f / (fabs(dirx) > ooeps ? dirx : copysign(ooeps, dirx));
    invdir.y = 1.0f / (fabs(diry) > ooeps ? diry : copysign(ooeps, diry));
    invdir.z = 1.0f / (fabs(dirz) > ooeps ? dirz : copysign(ooeps, dirz));
    return invdir;
#else
    return native_recip(r.d.xyz);
#endif
}

// Intersect rays vs bbox and return intersection span.
// Intersection criteria is ret.x <= ret.y
INLINE
float2 fast_intersect_bbox1(bbox box, float3 invdir, float3 oxinvdir, float t_max)
{
    float3 const f = mad(box.pmax.xyz, invdir, oxinvdir);
    float3 const n = mad(box.pmin.xyz, invdir, oxinvdir);
    float3 const tmax = max(f, n);
    float3 const tmin = min(f, n);
    float const t1 = min(min3(tmax.x, tmax.y, tmax.z), t_max);
    float const t0 = max(max3(tmin.x, tmin.y, tmin.z), 0.f);
    return make_float2(t0, t1);
}

// Given a point in triangle plane, calculate its barycentrics
INLINE
float2 triangle_calculate_barycentrics(float3 p, float3 v1, float3 v2, float3 v3)
{
    float3 const e1 = v2 - v1;
    float3 const e2 = v3 - v1;
    float3 const e = p - v1;
    float const d00 = dot(e1, e1);
    float const d01 = dot(e1, e2);
    float const d11 = dot(e2, e2);
    float const d20 = dot(e, e1);
    float const d21 = dot(e, e2);

	#ifdef USE_SAFE_MATH
		float const invdenom = 1.0 / (d00 * d11 - d01 * d01);
	#else
		float const invdenom = native_recip(d00 * d11 - d01 * d01);
	#endif

    float const b1 = (d11 * d20 - d01 * d21) * invdenom;
    float const b2 = (d00 * d21 - d01 * d20) * invdenom;
    return make_float2(b1, b2);
}


[Add] Start of OpenCL kernel count_interactions.

#define SQUARE(a) ((a) * (a))
#define INTERACTION_CUTOFF 10
#define INTERACTION_CUTOFF2 100

kernel
void count_interactions(
        global int *inter_space, uint4 inter_space_shape,
        global float4 *fixed_coor, uint fixed_coor_size,
        global float4 *scanning_coor, uint scanning_coor_size,
        global uint *inter_hist,
        local uint *loc_inter_hist,
        int nrestraints,
        )
{
    // Count the number of interactions each residue makes for complexes
    // consistent with nrestraints.

    size_t lid = get_local_id(0);
    size_t lstride = get_local_size(0);

    int nz, ny, nx, i, j;
    int2 inter_ind;
    float dist2;

    // Set local histogram to 0
    if (lid < (scanning_coor_size + fixed_coor_size)) {
        for (i = lid; i < (scanning_coor_size + fixed_coor_size); i += lstride)
            loc_inter_hist[i] = 0;
    }
    barrier(CLK_LOCAL_MEM_FENCE);

    // Loop over the interaction space, and count interactions for
    // conformations that are consistent with nrestraints.
    for (nz = get_global_id(0); nz < inter_space_shape.s0; nz += get_global_size(0)) {
        inter_ind.s0 = nz * inter_space_shape.s0;
        for (ny = get_global_id(1); ny < inter_space_shape.s1; ny += get_global_size(1)) {
            inter_ind.s1 = inter_ind.s0 + ny * inter_space_shape.s2;
            for (nx = get_global_id(2); nx < inter_space_shape.s2; nx += get_global_size(2)) {

                // Only investigate conformations consistent with nrestraints
                if (inter_space[inter_ind.s1 + nx] != nrestraints)
                    continue;

                // Calculate the number of interactions for each residue
                for (i = 0; i < scanning_coor_size; i++) {
                    coor.s0 = scanning_coor[i].s0 + nx;
                    coor.s1 = scanning_coor[i].s1 + ny;
                    coor.s2 = scanning_coor[i].s2 + nz;
                    for (j = 0; j < fixed_coor_size; j++) {
                        dist2 = SQUARE(coor.s0 - fixed_coor[j].s0) +
                                SQUARE(coor.s1 - fixed_coor[j].s1) +
                                SQUARE(coor.s2 - fixed_coor[j].s2);
                        if (dist2 <= INTERACTION_CUTOFF2) {
                            // Increase the counter using atomics
                            atomic_inc(loc_inter_hist[j]);
                            atomic_inc(loc_inter_hist[i + fixed_coor_size]);
                        }
                    }
                }
            }
        }
    }
    barrier(CLK_LOCAL_MEM_FENCE);

    // Combine the local histograms into the global memory
    if (lid < (scanning_coor_size + fixed_coor_size)) {
        offset = (scanning_coor_size + fixed_coor_size) * get_group_id(0);
        for (i = lid; i < scanning_coor_size; i += lstride)
            inter_hist[offset + i] += loc_inter_hist[i];
    }
}

__kernel
void count_violations(__global float8 *restraints,
                      float16 rotmat,
                      __global int *access_interspace,
                      __global float *viol_counter,
                      __local float *loc_viol,
                      __local float4 *restraints_center,
                      __local float *mindist2,
                      __local float *maxdist2,
                      uint nrestraints, uint4 shape, float weight)
{
    uint id = get_global_id(0);
    uint stride = get_global_size(0);
    uint lid = get_local_id(0);
    uint slice = shape.s2 * shape.s1;

    //set loc_viol to 0
    uint nrestraints2 = nrestraints*nrestraints;
    uint ind_z = lid*nrestraints2;
    for (uint i = 0; i < nrestraints2; i++)
        loc_viol[i + ind_z] = 0;

    // calculate the center of the restraints
    if (lid < nrestraints) {
        restraints_center[lid].s0 = restraints[lid].s0 -
                                   (rotmat.s0*restraints[lid].s3 +
                                    rotmat.s1*restraints[lid].s4 +
                                    rotmat.s2*restraints[lid].s5);
        restraints_center[lid].s1 = restraints[lid].s1 -
                                   (rotmat.s3*restraints[lid].s3 +
                                    rotmat.s4*restraints[lid].s4 +
                                    rotmat.s5*restraints[lid].s5);
        restraints_center[lid].s2 = restraints[lid].s2 -
                                   (rotmat.s6*restraints[lid].s3 +
                                    rotmat.s7*restraints[lid].s4 +
                                    rotmat.s8*restraints[lid].s5);
        mindist2[lid] = SQUARE(restraints[lid].s6);
        maxdist2[lid] = SQUARE(restraints[lid].s7);
    }
    barrier(CLK_LOCAL_MEM_FENCE);

    // loop over the accessable interaction space
    for (uint i = id; i < shape.s3; i += stride) {
        int consistent = access_interspace[i];
        if ((consistent == 0) || (consistent == nrestraints))
            continue;
        consistent--;

        // get x, y, z map coordinate
        uint z = i/slice;
        uint y = (i - slice*z)/shape.s2;
        uint x = i - slice*z - y*shape.s2;

        // check which restraints are violated for a certain number
        // of consistent restraints
        for (uint j = 0; j < nrestraints; j++){
            float dist2 = SQUARE(x - restraints_center[j].s0) +
                          SQUARE(y - restraints_center[j].s1) +
                          SQUARE(z - restraints_center[j].s2);
            if ((dist2 < mindist2[j]) || (dist2 > maxdist2[j])){
                uint ind = ind_z + nrestraints*consistent + j;
                loc_viol[ind] += weight;
            }
        }
    }
    barrier(CLK_LOCAL_MEM_FENCE);

    // merge the work item subhistograms into work group histogram
    if (lid < nrestraints2) {
        uint gind_z = get_group_id(0)*nrestraints2;
        for (uint i = lid; i < nrestraints2; i += get_local_size(0)){

            float sum = 0;
            for (uint j = 0; j < get_local_size(0); j++)
                sum += loc_viol[nrestraints2*j + i];

            viol_counter[gind_z + i] += sum;
        }
    }
}

__kernel
void copy_partial(__global float *part, __global float *full, int4 part_size, int4 full_size)
{
    uint z, y, x, full_ind;
    uint part_slice = part_size.s2*part_size.s1;
    uint full_slice = full_size.s2*full_size.s1;

    for (uint i = get_global_id(0); i < part_size.s3; i += get_global_size(0)) {
        z = i/part_slice;
        y = (i - z*part_slice)/part_size.s2;
        x = i - z*part_slice - y*part_size.s2;

        full_ind = z * full_slice + y * full_size.s2 + x;
        full[full_ind] = part[i];
    }
}

__kernel
void histogram(__global int *data,
                 __global float *subhists,
                 __local float *local_hist,
                 uint nrestraints,
                 float weight,
                 uint size)
{
    uint lid = get_local_id(0);

    for (uint i = 0; i < nrestraints; i++)
        local_hist[lid + i * get_local_size(0)] = 0;

    for (uint pos = get_global_id(0); pos < size; pos += get_global_size(0))
        local_hist[lid + data[pos] * get_local_size(0)] += weight;

    barrier(CLK_LOCAL_MEM_FENCE);

    if (lid < nrestraints) {
        for (uint j =  lid; j < nrestraints; j += get_local_size(0)) {

            float sum = 0;
            int pos = get_local_size(0) * lid;

            for (uint i = 0; i < get_local_size(0); i++)
                sum += local_hist[pos + i];

            subhists[get_group_id(0)*nrestraints + j] += sum;
        }
    }
}

__kernel
void rotate_image3d(sampler_t sampler,
                    read_only image3d_t image,
                    float16 rotmat,
                   __global float *out,
                   float4 center,
                   int4 shape)
{
    int id = get_global_id(0);
    int stride = get_global_size(0);

    int x, y, z, slice;
    float xrot, yrot, zrot;
    float4 coordinate, weight;

    slice = shape.s2*shape.s1;
    float OFFSET = 0.5f;

    int i;
    for (i = id; i < shape.s3; i += stride) {

        z = i/slice;
        y = (i - z*slice)/shape.s2;
        x = i - z*slice - y*shape.s2;

        if (x >= 0.5*shape.s2)
            x -= shape.s2;
        if (y >= 0.5*shape.s1)
            y -= shape.s1;
        if (z >= 0.5*shape.s0)
            z -= shape.s0;

        xrot = rotmat.s0*x + rotmat.s1*y + rotmat.s2*z;
        yrot = rotmat.s3*x + rotmat.s4*y + rotmat.s5*z;
        zrot = rotmat.s6*x + rotmat.s7*y + rotmat.s8*z;

        xrot += OFFSET + center.s0;
        yrot += OFFSET + center.s1;
        zrot += OFFSET + center.s2;

        coordinate = (float4) (xrot, yrot, zrot, 0);
        weight = read_imagef(image, sampler, coordinate);

        out[i] = weight.s0;
    }
}

__kernel
void distance_restraint(__global float8 *restraints,
                       float16 rotmat,
                       __global int *restspace,
                       int4 shape, int nrestraints)
{

    uint zid = get_global_id(0);
    uint yid = get_global_id(1);
    uint xid = get_global_id(2);

    uint zstride = get_global_size(0);
    uint ystride = get_global_size(1);
    uint xstride = get_global_size(2);

    uint i, ix, iy, iz;
    uint z_ind, yz_ind, xyz_ind;
    float xligand, yligand, zligand;
    float xcenter, ycenter, zcenter, mindis2, maxdis2, z_dis2, yz_dis2, xyz_dis2;

    uint slice = shape.s2 * shape.s1;

    for (i = 0; i < nrestraints; i++){

         // determine the center of the point that will be dilated
         xligand = rotmat.s0 * restraints[i].s3 +
                   rotmat.s1 * restraints[i].s4 +
                   rotmat.s2 * restraints[i].s5;
         yligand = rotmat.s3 * restraints[i].s3 +
                   rotmat.s4 * restraints[i].s4 +
                   rotmat.s5 * restraints[i].s5;
         zligand = rotmat.s6 * restraints[i].s3 +
                   rotmat.s7 * restraints[i].s4 +
                   rotmat.s8 * restraints[i].s5;

         xcenter = restraints[i].s0 - xligand;
         ycenter = restraints[i].s1 - yligand;
         zcenter = restraints[i].s2 - zligand;

         mindis2 = SQUARE(restraints[i].s6);
         maxdis2 = SQUARE(restraints[i].s7);

         // calculate the distance of every voxel to the determined center
         for (iz = zid; iz < shape.s0; iz += zstride){

             z_dis2 = SQUARE(iz - zcenter);

             z_ind = iz * slice;

             for (iy = yid; iy < shape.s1; iy += ystride){
                 yz_dis2 = SQUARE(iy - ycenter) + z_dis2;

                 yz_ind = z_ind + iy*shape.s2;

                 for (ix = xid; ix < shape.s2; ix += xstride){

                     xyz_dis2 = SQUARE(ix - xcenter) + yz_dis2;

                     if ((xyz_dis2 <= maxdis2) && (xyz_dis2 >= mindis2)){
                         xyz_ind = ix + yz_ind;
                         restspace[xyz_ind] += 1;
                     }
                 }
             }
         }
    }
}

#define UMUL(a, b) ((a) * (b))
#define UMAD(a, b, c) (UMUL((a), (b)) + (c))
#define SQUARE(a) ((a) * (a))

__kernel
void count_violations(__global float8 *restraints,
                      float16 rotmat,
                      __global int *access_interspace,
                      __global float *viol_counter,
                      __local float *loc_viol,
                      __local float4 *restraints_center,
                      __local float *mindist2,
                      __local float *maxdist2,
                      uint nrestraints, uint4 shape, float weight)
{
    uint id = get_global_id(0);
    uint stride = get_global_size(0);
    uint lid = get_local_id(0);
    uint slice = shape.s2 * shape.s1;

    //set loc_viol to 0
    uint nrestraints2 = nrestraints*nrestraints;
    uint ind_z = lid*nrestraints2;
    for (uint i = 0; i < nrestraints2; i++)
        loc_viol[i + ind_z] = 0;

    // calculate the center of the restraints
    if (lid < nrestraints) {
        restraints_center[lid].s0 = restraints[lid].s0 -
                                   (rotmat.s0*restraints[lid].s3 +
                                    rotmat.s1*restraints[lid].s4 +
                                    rotmat.s2*restraints[lid].s5);
        restraints_center[lid].s1 = restraints[lid].s1 -
                                   (rotmat.s3*restraints[lid].s3 +
                                    rotmat.s4*restraints[lid].s4 +
                                    rotmat.s5*restraints[lid].s5);
        restraints_center[lid].s2 = restraints[lid].s2 -
                                   (rotmat.s6*restraints[lid].s3 +
                                    rotmat.s7*restraints[lid].s4 +
                                    rotmat.s8*restraints[lid].s5);
        mindist2[lid] = SQUARE(restraints[lid].s6);
        maxdist2[lid] = SQUARE(restraints[lid].s7);
    }
    barrier(CLK_LOCAL_MEM_FENCE);

    // loop over the accessable interaction space
    for (uint i = id; i < shape.s3; i += stride) {
        int consistent = access_interspace[i];
        if ((consistent == 0) || (consistent == nrestraints))
            continue;
        consistent--;

        // get x, y, z map coordinate
        uint z = i/slice;
        uint y = (i - slice*z)/shape.s2;
        uint x = i - slice*z - y*shape.s2;

        // check which restraints are violated for a certain number
        // of consistent restraints
        for (uint j = 0; j < nrestraints; j++){
            float dist2 = SQUARE(x - restraints_center[j].s0) +
                          SQUARE(y - restraints_center[j].s1) +
                          SQUARE(z - restraints_center[j].s2);
            if ((dist2 < mindist2[j]) || (dist2 > maxdist2[j])){
                uint ind = ind_z + nrestraints*consistent + j;
                loc_viol[ind] += weight;
            }
        }
    }
    barrier(CLK_LOCAL_MEM_FENCE);

    // merge the work item subhistograms into work group histogram
    if (lid < nrestraints2) {
        uint gind_z = get_group_id(0)*nrestraints2;
        for (uint i = lid; i < nrestraints2; i += get_local_size(0)){

            float sum = 0;
            for (uint j = 0; j < get_local_size(0); j++)
                sum += loc_viol[nrestraints2*j + i];

            viol_counter[gind_z + i] += sum;
        }
    }
}

__kernel
void copy_partial(__global float *part, __global float *full, int4 part_size, int4 full_size)
{
    uint z, y, x, full_ind;
    uint part_slice = part_size.s2*part_size.s1;
    uint full_slice = full_size.s2*full_size.s1;

    for (uint i = get_global_id(0); i < part_size.s3; i += get_global_size(0)) {
        z = i/part_slice;
        y = (i - z*part_slice)/part_size.s2;
        x = i - z*part_slice - y*part_size.s2;

        full_ind = z * full_slice + y * full_size.s2 + x;
        full[full_ind] = part[i];
    }
}

__kernel
void histogram(__global int *data,
                 __global float *subhists,
                 __local float *local_hist,
                 uint nrestraints,
                 float weight,
                 uint size)
{
    uint lid = get_local_id(0);

    for (uint i = 0; i < nrestraints; i++)
        local_hist[lid + i * get_local_size(0)] = 0;

    for (uint pos = get_global_id(0); pos < size; pos += get_global_size(0))
        local_hist[lid + data[pos] * get_local_size(0)] += weight;

    barrier(CLK_LOCAL_MEM_FENCE);

    if (lid < nrestraints) {
        for (uint j =  lid; j < nrestraints; j += get_local_size(0)) {

            float sum = 0;
            int pos = get_local_size(0) * lid;

            for (uint i = 0; i < get_local_size(0); i++)
                sum += local_hist[pos + i];

            subhists[get_group_id(0)*nrestraints + j] += sum;
        }
    }
}

__kernel
void rotate_image3d(sampler_t sampler,
                    read_only image3d_t image,
                    float16 rotmat,
                   __global float *out,
                   float4 center,
                   int4 shape)
{
    int id = get_global_id(0);
    int stride = get_global_size(0);

    int x, y, z, slice;
    float xrot, yrot, zrot;
    float4 coordinate, weight;

    slice = shape.s2*shape.s1;
    float OFFSET = 0.5f;

    int i;
    for (i = id; i < shape.s3; i += stride) {

        z = i/slice;
        y = (i - z*slice)/shape.s2;
        x = i - z*slice - y*shape.s2;

        if (x >= 0.5*shape.s2)
            x -= shape.s2;
        if (y >= 0.5*shape.s1)
            y -= shape.s1;
        if (z >= 0.5*shape.s0)
            z -= shape.s0;

        xrot = rotmat.s0*x + rotmat.s1*y + rotmat.s2*z;
        yrot = rotmat.s3*x + rotmat.s4*y + rotmat.s5*z;
        zrot = rotmat.s6*x + rotmat.s7*y + rotmat.s8*z;

        xrot += OFFSET + center.s0;
        yrot += OFFSET + center.s1;
        zrot += OFFSET + center.s2;

        coordinate = (float4) (xrot, yrot, zrot, 0);
        weight = read_imagef(image, sampler, coordinate);

        out[i] = weight.s0;
    }
}

__kernel
void distance_restraint(__global float8 *restraints,
                       float16 rotmat,
                       __global int *restspace,
                       int4 shape, int nrestraints)
{

    uint zid = get_global_id(0);
    uint yid = get_global_id(1);
    uint xid = get_global_id(2);

    uint zstride = get_global_size(0);
    uint ystride = get_global_size(1);
    uint xstride = get_global_size(2);

    uint i, ix, iy, iz;
    uint z_ind, yz_ind, xyz_ind;
    float xligand, yligand, zligand;
    float xcenter, ycenter, zcenter, mindis2, maxdis2, z_dis2, yz_dis2, xyz_dis2;

    uint slice = shape.s2 * shape.s1;

    for (i = 0; i < nrestraints; i++){

         // determine the center of the point that will be dilated
         xligand = rotmat.s0 * restraints[i].s3 + rotmat.s1 * restraints[i].s4 + rotmat.s2 * restraints[i].s5;
         yligand = rotmat.s3 * restraints[i].s3 + rotmat.s4 * restraints[i].s4 + rotmat.s5 * restraints[i].s5;
         zligand = rotmat.s6 * restraints[i].s3 + rotmat.s7 * restraints[i].s4 + rotmat.s8 * restraints[i].s5;

         xcenter = restraints[i].s0 - xligand;
         ycenter = restraints[i].s1 - yligand;
         zcenter = restraints[i].s2 - zligand;

         mindis2 = SQUARE(restraints[i].s6);
         maxdis2 = SQUARE(restraints[i].s7);

         // calculate the distance of every voxel to the determined center
         for (iz = zid; iz < shape.s0; iz += zstride){

             z_dis2 = SQUARE(iz - zcenter);

             z_ind = iz * slice;

             for (iy = yid; iy < shape.s1; iy += ystride){
                 yz_dis2 = SQUARE(iy - ycenter) + z_dis2;

                 yz_ind = z_ind + iy*shape.s2;

                 for (ix = xid; ix < shape.s2; ix += xstride){

                     xyz_dis2 = SQUARE(ix - xcenter) + yz_dis2;

                     if ((xyz_dis2 <= maxdis2) && (xyz_dis2 >= mindis2)){
                         xyz_ind = ix + yz_ind;
                         restspace[xyz_ind] += 1;
                     }
                 }
             }
         }
    }
}