Fix CMJ

Fix CMJ

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

//#define SOBOL
#define MULTISCATTER

#include <../App/CL/ray.cl>
#include <../App/CL/isect.cl>
#include <../App/CL/utils.cl>
#include <../App/CL/random.cl>
#include <../App/CL/payload.cl>
#include <../App/CL/texture.cl>
#include <../App/CL/sampling.cl>
#include <../App/CL/normalmap.cl>
#include <../App/CL/bxdf.cl>
#include <../App/CL/light.cl>
#include <../App/CL/camera.cl>
#include <../App/CL/scene.cl>
#include <../App/CL/material.cl>
#include <../App/CL/volumetrics.cl>
#include <../App/CL/path.cl>

#define CRAZY_LOW_THROUGHPUT 0.0f
#define CRAZY_HIGH_RADIANCE 3.f
#define CRAZY_HIGH_DISTANCE 1000000.f
#define CRAZY_LOW_DISTANCE 0.01f
#define REASONABLE_RADIANCE(x) (clamp((x), 0.f, CRAZY_HIGH_RADIANCE))
#define NON_BLACK(x) (length(x) > 0.f)

#define CMJ 1

// This kernel only handles scattered paths.
// It applies direct illumination and generates
// path continuation if multiscattering is enabled.
__kernel void ShadeVolume(
    // Ray batch
    __global ray const* rays,
    // Intersection data
    __global Intersection const* isects,
    // Hit indices
    __global int const* hitindices,
    // Pixel indices
    __global int const* pixelindices,
    // Number of rays
    __global int const* numhits,
    // Vertices
    __global float3 const* vertices,
    // Normals
    __global float3 const* normals,
    // UVs
    __global float2 const* uvs,
    // Indices
    __global int const* indices,
    // Shapes
    __global Shape const* shapes,
    // Material IDs
    __global int const* materialids,
    // Materials
    __global Material const* materials,
    // Textures
    TEXTURE_ARG_LIST,
    // Environment texture index
    int envmapidx,
    // Envmap multiplier
    float envmapmul,
    // Emissives
    __global Light const* lights,
    // Number of emissive objects
    int num_lights,
    // RNG seed
    int rngseed,
    // Sampler state
    __global SobolSampler* samplers,
    // Sobol matrices
    __global uint const* sobolmat,
    // Current bounce
    int bounce,
    // Volume data
    __global Volume const* volumes,
    // Shadow rays
    __global ray* shadowrays,
    // Light samples
    __global float3* lightsamples,
    // Path throughput
    __global Path* paths,
    // Indirect rays (next path segment)
    __global ray* indirectrays,
    // Radiance
    __global float3* output
)
{
    int globalid = get_global_id(0);

    Scene scene =
    {
        vertices,
        normals,
        uvs,
        indices,
        shapes,
        materialids,
        materials,
        lights,
        envmapidx,
        envmapmul,
        num_lights
    };

    if (globalid < *numhits)
    {
        // Fetch index
        int hitidx = hitindices[globalid];
        int pixelidx = pixelindices[globalid];
        Intersection isect = isects[hitidx];

        __global Path* path = paths + pixelidx;

        // Only apply to scattered paths
        if (!Path_IsScattered(path))
        {
            return;
        }

        // Fetch incoming ray
        float3 o = rays[hitidx].o.xyz;
        float3 wi = rays[hitidx].d.xyz;

#ifdef SOBOL
        __global SobolSampler* sampler = samplers + pixelidx;
        float sample0 = SobolSampler_Sample1D(sampler->seq, GetSampleDim(bounce, kVolumeLight), sampler->s0, sobolmat);
        float2 sample1;
        sample1.x = SobolSampler_Sample1D(sampler->seq, GetSampleDim(bounce, kVolumeLightU), sampler->s0, sobolmat);
        sample1.y = SobolSampler_Sample1D(sampler->seq, GetSampleDim(bounce, kVolumeLightV), sampler->s0, sobolmat);
#ifdef MULTISCATTER
        float2 sample2;
        sample2.x = SobolSampler_Sample1D(sampler->seq, GetSampleDim(bounce, kVolumeIndirectU), sampler->s0, sobolmat);
        sample2.y = SobolSampler_Sample1D(sampler->seq, GetSampleDim(bounce, kVolumeIndirectV), sampler->s0, sobolmat);
#endif
#else
        // Prepare RNG for sampling
        Rng rng;
        InitRng(rngseed + (globalid << 2) * 157 + 13, &rng);
        float sample0 = UniformSampler_Sample2D(&rng).x;
        float2 sample1 = UniformSampler_Sample2D(&rng);
#ifdef MULTISCATTER
        float2 sample2 = UniformSampler_Sample2D(&rng);
#endif
#endif
        // Here we know that volidx != -1 since this is a precondition
        // for scattering event
        int volidx = Path_GetVolumeIdx(path);

        // Sample light source
        float pdf = 0.f;
        float selection_pdf = 0.f;
        float3 wo;

        int light_idx = Scene_SampleLight(&scene, sample0, &selection_pdf);

        // Here we need fake differential geometry for light sampling procedure
        DifferentialGeometry dg;
        // put scattering position in there (it is along the current ray at isect.distance
        // since EvaluateVolume has put it there
        dg.p = o + wi * Intersection_GetDistance(isects + hitidx);
        // Get light sample intencity
        float3 le = Light_Sample(light_idx, &scene, &dg, TEXTURE_ARGS, sample1, &wo, &pdf);

        // Generate shadow ray
        float shadow_ray_length = 0.999f * length(wo);
        Ray_Init(shadowrays + globalid, dg.p, normalize(wo), shadow_ray_length, 0.f, 0xFFFFFFFF);

        // Evaluate volume transmittion along the shadow ray (it is incorrect if the light source is outside of the
        // current volume, but in this case it will be discarded anyway since the intersection at the outer bound
        // of a current volume), so the result is fully correct.
        float3 tr = Volume_Transmittance(&volumes[volidx], &shadowrays[globalid], shadow_ray_length);

        // Volume emission is applied only if the light source is in the current volume(this is incorrect since the light source might be
        // outside of a volume and we have to compute fraction of ray in this case, but need to figure out how)
        float3 r = Volume_Emission(&volumes[volidx], &shadowrays[globalid], shadow_ray_length);

        // This is the estimate coming from a light source
        // TODO: remove hardcoded phase func and sigma
        r += tr * le * volumes[volidx].sigma_s * PhaseFunction_Uniform(wi, normalize(wo)) / pdf / selection_pdf;

        // Only if we have some radiance compute the visibility ray
        if (NON_BLACK(tr) && NON_BLACK(r) && pdf > 0.f)
        {
            // Put lightsample result
            lightsamples[globalid] = REASONABLE_RADIANCE(r * Path_GetThroughput(path));
        }
        else
        {
            // Nothing to compute
            lightsamples[globalid] = 0.f;
            // Otherwise make it incative to save intersector cycles (hopefully)
            Ray_SetInactive(shadowrays + globalid);
        }

#ifdef MULTISCATTER
        // This is highly brute-force
        // TODO: investigate importance sampling techniques here
        wo = Sample_MapToSphere(sample2);
        pdf = 1.f / (4.f * PI);

        // Generate new path segment
        Ray_Init(indirectrays + globalid, dg.p, normalize(wo), CRAZY_HIGH_DISTANCE, 0.f, 0xFFFFFFFF);

        // Update path throughput multiplying by phase function.
        Path_MulThroughput(path, volumes[volidx].sigma_s * PhaseFunction_Uniform(wi, normalize(wo)) / pdf);
#else
        // Single-scattering mode only,
        // kill the path and compact away on next iteration
        Path_Kill(path);
        Ray_SetInactive(indirectrays + globalid);
#endif
    }
}

#define CMJ_DIM 16

// Handle ray-surface interaction possibly generating path continuation.
// This is only applied to non-scattered paths.
__kernel void ShadeSurface(
    // Ray batch
    __global ray const* rays,
    // Intersection data
    __global Intersection const* isects,
    // Hit indices
    __global int const* hitindices,
    // Pixel indices
    __global int const* pixelindices,
    // Number of rays
    __global int const* numhits,
    // Vertices
    __global float3 const* vertices,
    // Normals
    __global float3 const* normals,
    // UVs
    __global float2 const* uvs,
    // Indices
    __global int const* indices,
    // Shapes
    __global Shape const* shapes,
    // Material IDs
    __global int const* materialids,
    // Materials
    __global Material const* materials,
    // Textures
    TEXTURE_ARG_LIST,
    // Environment texture index
    int envmapidx,
    // Envmap multiplier
    float envmapmul,
    // Emissives
    __global Light const* lights,
    // Number of emissive objects
    int num_lights,
    // RNG seed
    int rngseed,
    // Sampler states
    __global SobolSampler* samplers,
    // Sobol matrices
    __global uint const* sobolmat,
    // Current bounce
    int bounce,
    // Frame
    int frame,
    // Volume data
    __global Volume const* volumes,
    // Shadow rays
    __global ray* shadowrays,
    // Light samples
    __global float3* lightsamples,
    // Path throughput
    __global Path* paths,
    // Indirect rays
    __global ray* indirectrays,
    // Radiance
    __global float3* output
)
{
    int globalid = get_global_id(0);

    Scene scene =
    {
        vertices,
        normals,
        uvs,
        indices,
        shapes,
        materialids,
        materials,
        lights,
        envmapidx,
        envmapmul,
        num_lights
    };

    // Only applied to active rays after compaction
    if (globalid < *numhits)
    {
        // Fetch index
        int hitidx = hitindices[globalid];
        int pixelidx = pixelindices[globalid];
        Intersection isect = isects[hitidx];

        __global Path* path = paths + pixelidx;

        // Early exit for scattered paths
        if (Path_IsScattered(path))
        {
            return;
        }

        // Fetch incoming ray direction
        float3 wi = -normalize(rays[hitidx].d.xyz);
#if (SOBOL == 1)
        // Sample light
        __global SobolSampler* sampler = samplers + pixelidx;

        float2 sample0;
        sample0.x = SobolSampler_Sample1D(sampler->seq, GetSampleDim(bounce, kBrdf), sampler->s0, sobolmat);
        sample0.y = SobolSampler_Sample1D(sampler->seq, GetSampleDim(bounce, kLight), sampler->s0, sobolmat);

        float2 sample1;
        sample1.x = SobolSampler_Sample1D(sampler->seq, GetSampleDim(bounce, kLightU), sampler->s0, sobolmat);
        sample1.y = SobolSampler_Sample1D(sampler->seq, GetSampleDim(bounce, kLightV), sampler->s0, sobolmat);

        float2 sample2;
        sample2.x = SobolSampler_Sample1D(sampler->seq, GetSampleDim(bounce, kBrdfU), sampler->s0, sobolmat);
        sample2.y = SobolSampler_Sample1D(sampler->seq, GetSampleDim(bounce, kBrdfV), sampler->s0, sobolmat);

        float2 sample3;
        sample3.x = SobolSampler_Sample1D(sampler->seq, GetSampleDim(bounce, kIndirectU), sampler->s0, sobolmat);
        sample3.y = SobolSampler_Sample1D(sampler->seq, GetSampleDim(bounce, kIndirectV), sampler->s0, sobolmat);

        float sample4 = SobolSampler_Sample1D(sampler->seq, GetSampleDim(bounce, kRR), sampler->s0, sobolmat);
#elif (RANDOM == 1)
        // Prepare RNG
        Rng rng;
        InitRng(rngseed + (globalid << 2) * 157 + 13, &rng);
        float2 sample0 = UniformSampler_Sample2D(&rng);
        float2 sample1 = UniformSampler_Sample2D(&rng);
        float2 sample2 = UniformSampler_Sample2D(&rng);
        float2 sample3 = UniformSampler_Sample2D(&rng);
        float  sample4 = UniformSampler_Sample2D(&rng).x;
#elif (CMJ == 1)
        Rng rng;
        InitRng(rngseed + (globalid << 2) * 157 + 13, &rng);

        int pass = frame / (CMJ_DIM * CMJ_DIM);
        int subsample0 = permute(frame % (CMJ_DIM * CMJ_DIM), CMJ_DIM * CMJ_DIM, (pass + pixelidx + bounce) * 0xc517e953);
        int subsample1 = permute(frame % (CMJ_DIM * CMJ_DIM), CMJ_DIM * CMJ_DIM, (pass + pixelidx + bounce + 1) * 0xc517e953);
        int subsample2 = permute(frame % (CMJ_DIM * CMJ_DIM), CMJ_DIM * CMJ_DIM, (pass + pixelidx + bounce + 2) * 0xc517e953);
        int subsample3 = permute(frame % (CMJ_DIM * CMJ_DIM), CMJ_DIM * CMJ_DIM, (pass + pixelidx + bounce + 3) * 0xc517e953);
        int subsample4 = permute(frame % (CMJ_DIM * CMJ_DIM), CMJ_DIM * CMJ_DIM, (pass + pixelidx + bounce + 4) * 0xc517e953);

        int pattern0 = (pass + bounce + pixelidx) % 13331;
        int pattern1 = (pass + bounce + pixelidx + 1) % 13331;
        int pattern2 = (pass + bounce + pixelidx + 2) % 13331;
        int pattern3 = (pass + bounce + pixelidx + 3) % 13331;
        int pattern4 = (pass + bounce + pixelidx + 4) % 13331;

        float2 sample0 = cmj(subsample0, CMJ_DIM, pattern0);
        float2 sample1 = cmj(subsample1, CMJ_DIM, pattern1);
        float2 sample2 = cmj(subsample2, CMJ_DIM, pattern2);
        float2 sample3 = cmj(subsample3, CMJ_DIM, pattern3);
        float sample4 = cmj(subsample4, CMJ_DIM, pattern4).x;
#else

#endif

        // Fill surface data
        DifferentialGeometry diffgeo;
        DifferentialGeometry_Fill(&scene, &isect, &diffgeo);

        // Check if we are hitting from the inside

        float backfacing = dot(diffgeo.ng, wi) < 0.f;
        int twosided = diffgeo.mat.twosided;
        if (twosided && backfacing)
        {
            // Reverse normal and tangents in this case
            // but not for BTDFs, since BTDFs rely
            // on normal direction in order to arrange
            // indices of refraction
            diffgeo.n = -diffgeo.n;
            diffgeo.dpdu = -diffgeo.dpdu;
            diffgeo.dpdv = -diffgeo.dpdv;
        }

        float ndotwi = dot(diffgeo.n, wi);

        // Select BxDF
        Material_Select(
            &scene, wi, TEXTURE_ARGS,
#ifdef SOBOL
            sampler, sobolmat, bounce,
#else
            &rng,
#endif
            &diffgeo
        );

        // Terminate if emissive
        if (Bxdf_IsEmissive(&diffgeo))
        {
            if (!backfacing)
            {
                float weight = 1.f;

                if (bounce > 0 && !Path_IsSpecular(path))
                {
                    float2 extra = Ray_GetExtra(&rays[hitidx]);
                    float ld = isect.uvwt.w;
                    float denom = extra.y * diffgeo.area;
                    // TODO: num_lights should be num_emissies instead, presence of analytical lights breaks this code
                    float bxdflightpdf = denom > 0.f ? (ld * ld / denom / num_lights) : 0.f;
                    weight = BalanceHeuristic(1, extra.x, 1, bxdflightpdf);
                }

                {
                    // In this case we hit after an application of MIS process at previous step.
                    // That means BRDF weight has been already applied.
                    output[pixelidx] += Path_GetThroughput(path) * Emissive_GetLe(&diffgeo, TEXTURE_ARGS) * weight;
                }
            }

            Path_Kill(path);
            Ray_SetInactive(shadowrays + globalid);
            Ray_SetInactive(indirectrays + globalid);

            lightsamples[globalid] = 0.f;
            return;
        }

        float s = Bxdf_IsBtdf(&diffgeo) ? (-sign(ndotwi)) : 1.f;
        if (!twosided && backfacing && !Bxdf_IsBtdf(&diffgeo))
        {
             //Reverse normal and tangents in this case
             //but not for BTDFs, since BTDFs rely
             //on normal direction in order to arrange
             //indices of refraction
            diffgeo.n = -diffgeo.n;
            diffgeo.dpdu = -diffgeo.dpdu;
            diffgeo.dpdv = -diffgeo.dpdv;
        }

        // TODO: this is test code, need to
        // maintain proper volume stack here
        //if (Bxdf_IsBtdf(&diffgeo))
        //{
        //    // If we entering set the volume
        //    path->volume = !backfacing ? 0 : -1;
        //}

        // Check if we need to apply normal map
        //ApplyNormalMap(&diffgeo, TEXTURE_ARGS);
        DifferentialGeometry_ApplyBumpMap(&diffgeo, TEXTURE_ARGS);
        DifferentialGeometry_CalculateTangentTransforms(&diffgeo);

        float lightpdf = 0.f;
        float bxdflightpdf = 0.f;
        float bxdfpdf = 0.f;
        float lightbxdfpdf = 0.f;
        float selection_pdf = 0.f;
        float3 radiance = 0.f;
        float3 lightwo;
        float3 bxdfwo;
        float3 wo;
        float bxdfweight = 1.f;
        float lightweight = 1.f;

        int light_idx = num_lights > 0 ? Scene_SampleLight(&scene, sample0.y, &selection_pdf) : -1;

        float3 throughput = Path_GetThroughput(path);

        // Sample bxdf
        float3 bxdf = Bxdf_Sample(&diffgeo, wi, TEXTURE_ARGS, sample2, &bxdfwo, &bxdfpdf);

        // If we have light to sample we can hopefully do mis
        if (light_idx > -1)
        {
            // Sample light
            float3 le = Light_Sample(light_idx, &scene, &diffgeo, TEXTURE_ARGS, sample1, &lightwo, &lightpdf);
            lightbxdfpdf = Bxdf_GetPdf(&diffgeo, wi, normalize(lightwo), TEXTURE_ARGS);
            lightweight = Light_IsSingular(&scene.lights[light_idx]) ? 1.f : BalanceHeuristic(1, lightpdf, 1, lightbxdfpdf);

            // Apply MIS to account for both
            if (NON_BLACK(le) && lightpdf > 0.0f && !Bxdf_IsSingular(&diffgeo))
            {
                wo = lightwo;
                float ndotwo = fabs(dot(diffgeo.n, normalize(wo)));
                radiance = le * Bxdf_Evaluate(&diffgeo, wi, normalize(wo), TEXTURE_ARGS) * throughput * ndotwo * lightweight / lightpdf / selection_pdf;
            }
        }

        // If we have some light here generate a shadow ray
        if (NON_BLACK(radiance))
        {
            // Generate shadow ray
            float shadow_ray_length = (1.f - 2.f * CRAZY_LOW_DISTANCE) * length(wo);
            float3 shadow_ray_dir = normalize(wo);
            float3 shadow_ray_o = diffgeo.p + CRAZY_LOW_DISTANCE * s * diffgeo.n;
            int shadow_ray_mask = Bxdf_IsSingular(&diffgeo) ? 0xFFFFFFFF : 0x0000FFFF;

            Ray_Init(shadowrays + globalid, shadow_ray_o, shadow_ray_dir, shadow_ray_length, 0.f, shadow_ray_mask);

            // Apply the volume to shadow ray if needed
            int volidx = Path_GetVolumeIdx(path);
            if (volidx != -1)
            {
                radiance *= Volume_Transmittance(&volumes[volidx], &shadowrays[globalid], shadow_ray_length);
                radiance += Volume_Emission(&volumes[volidx], &shadowrays[globalid], shadow_ray_length) * throughput;
            }

            // And write the light sample
            lightsamples[globalid] = REASONABLE_RADIANCE(radiance);
        }
        else
        {
            // Otherwise save some intersector cycles
            Ray_SetInactive(shadowrays + globalid);
            lightsamples[globalid] = 0;
        }

        // Apply Russian roulette
        float q = max(min(0.5f,
            // Luminance
            0.2126f * throughput.x + 0.7152f * throughput.y + 0.0722f * throughput.z), 0.01f);
        // Only if it is 3+ bounce
        bool rr_apply = bounce > 3;
        bool rr_stop = sample4 > q && rr_apply;

        if (rr_apply)
        {
            Path_MulThroughput(path, 1.f / q);
        }

        if (Bxdf_IsSingular(&diffgeo))
        {
            Path_SetSpecularFlag(path);
        }

        bxdfwo = normalize(bxdfwo);
        float3 t = bxdf * fabs(dot(diffgeo.n, bxdfwo));

        // Only continue if we have non-zero throughput & pdf
        if (NON_BLACK(t) && bxdfpdf > 0.f && !rr_stop)
        {
            // Update the throughput
            Path_MulThroughput(path, t / bxdfpdf);

            // Generate ray
            float3 indirect_ray_dir = bxdfwo;
            float3 indirect_ray_o = diffgeo.p + CRAZY_LOW_DISTANCE * s * diffgeo.n;

            Ray_Init(indirectrays + globalid, indirect_ray_o, indirect_ray_dir, CRAZY_HIGH_DISTANCE, 0.f, 0xFFFFFFFF);
            Ray_SetExtra(indirectrays + globalid, make_float2(bxdfpdf, fabs(dot(diffgeo.n, bxdfwo))));
        }
        else
        {
            // Otherwise kill the path
            Path_Kill(path);
            Ray_SetInactive(indirectrays + globalid);
        }
    }
}

///< Illuminate missing rays
__kernel void ShadeMiss(
    // Ray batch
    __global ray const* rays,
    // Intersection data
    __global Intersection const* isects,
    // Pixel indices
    __global int const* pixelindices,
    // Number of rays
    int numrays,
    // Textures
    TEXTURE_ARG_LIST,
    // Environment texture index
    int envmapidx,
    //
    __global Path const* paths,
    __global Volume const* volumes,
    // Output values
    __global float4* output
)
{
    int globalid = get_global_id(0);

    if (globalid < numrays)
    {
        int pixelidx = pixelindices[globalid];

        // In case of a miss
        if (isects[globalid].shapeid < 0)
        {
            // Multiply by throughput
            int volidx = paths[pixelidx].volume;

            if (volidx == -1)
                output[pixelidx].xyz += Texture_SampleEnvMap(rays[globalid].d.xyz, TEXTURE_ARGS_IDX(envmapidx));
            else
            {
                output[pixelidx].xyz += Texture_SampleEnvMap(rays[globalid].d.xyz, TEXTURE_ARGS_IDX(envmapidx)) *
                    Volume_Transmittance(&volumes[volidx], &rays[globalid], rays[globalid].o.w);

                output[pixelidx].xyz += Volume_Emission(&volumes[volidx], &rays[globalid], rays[globalid].o.w);
            }
        }

        if (isnan(output[pixelidx].x) || isnan(output[pixelidx].y) || isnan(output[pixelidx].z))
        {
            output[pixelidx] = make_float4(100.f, 0.f, 0.f, 1.f);
        }

        output[pixelidx].w += 1.f;
    }
}

///< Handle light samples and visibility info and add contribution to final buffer
__kernel void GatherLightSamples(
    // Pixel indices
    __global int const* pixelindices,
    // Number of rays
    __global int* numrays,
    // Shadow rays hits
    __global int const* shadowhits,
    // Light samples
    __global float3 const* lightsamples,
    // throughput
    __global Path const* paths,
    // Radiance sample buffer
    __global float4* output
)
{
    int globalid = get_global_id(0);

    if (globalid < *numrays)
    {
        // Get pixel id for this sample set
        int pixelidx = pixelindices[globalid];

        // Prepare accumulator variable
        float3 radiance = make_float3(0.f, 0.f, 0.f);

        // Start collecting samples
        {
            // If shadow ray didn't hit anything and reached skydome
            if (shadowhits[globalid] == -1)
            {
                // Add its contribution to radiance accumulator
                radiance += lightsamples[globalid];
            }
        }

        // Divide by number of light samples (samples already have built-in throughput)
        output[pixelidx].xyz += radiance;
    }
}

///< Handle light samples and visibility info and add contribution to final buffer
__kernel void GatherOcclusion(
    // Pixel indices
    __global int const* pixelindices,
    // Number of rays
    __global int const* numrays,
    // Shadow rays hits
    __global int const* shadowhits,
    // Light samples
    __global float3 const* lightsamples,
    // throughput
    __global float3 const* throughput,
    // Radiance sample buffer
    __global float4* output
)
{
    int globalid = get_global_id(0);

    if (globalid < *numrays)
    {
        // Get pixel id for this sample set
        int pixelidx = pixelindices[globalid];

        float visibility = (shadowhits[globalid] == 1) ? 0.f : 1.f;

        output[pixelidx].xyz += visibility;
        output[pixelidx].w += 1.f;
    }
}

///< Restore pixel indices after compaction
__kernel void RestorePixelIndices(
    // Compacted indices
    __global int const* compacted_indices,
    // Number of compacted indices
    __global int* numitems,
    // Previous pixel indices
    __global int const* previndices,
    // New pixel indices
    __global int* newindices
)
{
    int globalid = get_global_id(0);

    // Handle only working subset
    if (globalid < *numitems)
    {
        newindices[globalid] = previndices[compacted_indices[globalid]];
    }
}

///< Restore pixel indices after compaction
__kernel void FilterPathStream(
    // Intersections
    __global Intersection const* isects,
    // Number of compacted indices
    __global int const* numitems,
    // Pixel indices
    __global int const* pixelindices,
    // Paths
    __global Path* paths,
    // Predicate
    __global int* predicate
)
{
    int globalid = get_global_id(0);

    // Handle only working subset
    if (globalid < *numitems)
    {
        int pixelidx = pixelindices[globalid];

        __global Path* path = paths + pixelidx;

        if (Path_IsAlive(path))
        {
            bool kill = (length(Path_GetThroughput(path)) < CRAZY_LOW_THROUGHPUT);

            if (!kill)
            {
                predicate[globalid] = isects[globalid].shapeid >= 0 ? 1 : 0;
            }
            else
            {
                Path_Kill(path);
                predicate[globalid] = 0;
            }
        }
        else
        {
            predicate[globalid] = 0;
        }
    }
}

///< Illuminate missing rays
__kernel void ShadeBackground(
    // Ray batch
    __global ray const* rays,
    // Intersection data
    __global Intersection const* isects,
    // Pixel indices
    __global int const* pixelindices,
    // Number of rays
    __global int const* numrays,
    // Textures
    TEXTURE_ARG_LIST,
    // Environment texture index
    int envmapidx,
    float envmapmul,
    //
    int num_lights,
    __global Path const* paths,
    __global Volume const* volumes,
    // Output values
    __global float4* output
)
{
    int globalid = get_global_id(0);

    if (globalid < *numrays)
    {
        int pixelidx = pixelindices[globalid];

        __global Path const* path = paths + pixelidx;

        // In case of a miss
        if (isects[globalid].shapeid < 0 && Path_IsAlive(path))
        {
            float3 t = Path_GetThroughput(path);
            output[pixelidx].xyz += REASONABLE_RADIANCE(envmapmul * Texture_SampleEnvMap(rays[globalid].d.xyz, TEXTURE_ARGS_IDX(envmapidx)) * t);
        }
    }
}

// Copy data to interop texture if supported
__kernel void AccumulateData(
    __global float4 const* srcdata,
    int numelems,
    __global float4* dstdata
)
{
    int gid = get_global_id(0);

    if (gid < numelems)
    {
        float4 v = srcdata[gid];
        dstdata[gid] += v;
    }
}

// Copy data to interop texture if supported
__kernel void ApplyGammaAndCopyData(
    __global float4 const* data,
    int imgwidth,
    int imgheight,
    float gamma,
    write_only image2d_t img
)
{
    int gid = get_global_id(0);

    int gidx = gid % imgwidth;
    int gidy = gid / imgwidth;

    if (gidx < imgwidth && gidy < imgheight)
    {
        float4 v = data[gid];
        float4 val = clamp(native_powr(v / v.w, 1.f / gamma), 0.f, 1.f);
        write_imagef(img, make_int2(gidx, gidy), val);
    }
}

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

//#define SOBOL
#define MULTISCATTER

#include <../App/CL/ray.cl>
#include <../App/CL/isect.cl>
#include <../App/CL/utils.cl>
#include <../App/CL/random.cl>
#include <../App/CL/payload.cl>
#include <../App/CL/texture.cl>
#include <../App/CL/sampling.cl>
#include <../App/CL/normalmap.cl>
#include <../App/CL/bxdf.cl>
#include <../App/CL/light.cl>
#include <../App/CL/camera.cl>
#include <../App/CL/scene.cl>
#include <../App/CL/material.cl>
#include <../App/CL/volumetrics.cl>
#include <../App/CL/path.cl>

#define CRAZY_LOW_THROUGHPUT 0.0f
#define CRAZY_HIGH_RADIANCE 3.f
#define CRAZY_HIGH_DISTANCE 1000000.f
#define CRAZY_LOW_DISTANCE 0.01f
#define REASONABLE_RADIANCE(x) (clamp((x), 0.f, CRAZY_HIGH_RADIANCE))
#define NON_BLACK(x) (length(x) > 0.f)

#define CMJ 1

// This kernel only handles scattered paths.
// It applies direct illumination and generates
// path continuation if multiscattering is enabled.
__kernel void ShadeVolume(
    // Ray batch
    __global ray const* rays,
    // Intersection data
    __global Intersection const* isects,
    // Hit indices
    __global int const* hitindices,
    // Pixel indices
    __global int const* pixelindices,
    // Number of rays
    __global int const* numhits,
    // Vertices
    __global float3 const* vertices,
    // Normals
    __global float3 const* normals,
    // UVs
    __global float2 const* uvs,
    // Indices
    __global int const* indices,
    // Shapes
    __global Shape const* shapes,
    // Material IDs
    __global int const* materialids,
    // Materials
    __global Material const* materials,
    // Textures
    TEXTURE_ARG_LIST,
    // Environment texture index
    int envmapidx,
    // Envmap multiplier
    float envmapmul,
    // Emissives
    __global Light const* lights,
    // Number of emissive objects
    int num_lights,
    // RNG seed
    int rngseed,
    // Sampler state
    __global SobolSampler* samplers,
    // Sobol matrices
    __global uint const* sobolmat,
    // Current bounce
    int bounce,
    // Volume data
    __global Volume const* volumes,
    // Shadow rays
    __global ray* shadowrays,
    // Light samples
    __global float3* lightsamples,
    // Path throughput
    __global Path* paths,
    // Indirect rays (next path segment)
    __global ray* indirectrays,
    // Radiance
    __global float3* output
)
{
    int globalid = get_global_id(0);

    Scene scene =
    {
        vertices,
        normals,
        uvs,
        indices,
        shapes,
        materialids,
        materials,
        lights,
        envmapidx,
        envmapmul,
        num_lights
    };

    if (globalid < *numhits)
    {
        // Fetch index
        int hitidx = hitindices[globalid];
        int pixelidx = pixelindices[globalid];
        Intersection isect = isects[hitidx];

        __global Path* path = paths + pixelidx;

        // Only apply to scattered paths
        if (!Path_IsScattered(path))
        {
            return;
        }

        // Fetch incoming ray
        float3 o = rays[hitidx].o.xyz;
        float3 wi = rays[hitidx].d.xyz;

#ifdef SOBOL
        __global SobolSampler* sampler = samplers + pixelidx;
        float sample0 = SobolSampler_Sample1D(sampler->seq, GetSampleDim(bounce, kVolumeLight), sampler->s0, sobolmat);
        float2 sample1;
        sample1.x = SobolSampler_Sample1D(sampler->seq, GetSampleDim(bounce, kVolumeLightU), sampler->s0, sobolmat);
        sample1.y = SobolSampler_Sample1D(sampler->seq, GetSampleDim(bounce, kVolumeLightV), sampler->s0, sobolmat);
#ifdef MULTISCATTER
        float2 sample2;
        sample2.x = SobolSampler_Sample1D(sampler->seq, GetSampleDim(bounce, kVolumeIndirectU), sampler->s0, sobolmat);
        sample2.y = SobolSampler_Sample1D(sampler->seq, GetSampleDim(bounce, kVolumeIndirectV), sampler->s0, sobolmat);
#endif
#else
        // Prepare RNG for sampling
        Rng rng;
        InitRng(rngseed + (globalid << 2) * 157 + 13, &rng);
        float sample0 = UniformSampler_Sample2D(&rng).x;
        float2 sample1 = UniformSampler_Sample2D(&rng);
#ifdef MULTISCATTER
        float2 sample2 = UniformSampler_Sample2D(&rng);
#endif
#endif
        // Here we know that volidx != -1 since this is a precondition
        // for scattering event
        int volidx = Path_GetVolumeIdx(path);

        // Sample light source
        float pdf = 0.f;
        float selection_pdf = 0.f;
        float3 wo;

        int light_idx = Scene_SampleLight(&scene, sample0, &selection_pdf);

        // Here we need fake differential geometry for light sampling procedure
        DifferentialGeometry dg;
        // put scattering position in there (it is along the current ray at isect.distance
        // since EvaluateVolume has put it there
        dg.p = o + wi * Intersection_GetDistance(isects + hitidx);
        // Get light sample intencity
        float3 le = Light_Sample(light_idx, &scene, &dg, TEXTURE_ARGS, sample1, &wo, &pdf);

        // Generate shadow ray
        float shadow_ray_length = 0.999f * length(wo);
        Ray_Init(shadowrays + globalid, dg.p, normalize(wo), shadow_ray_length, 0.f, 0xFFFFFFFF);

        // Evaluate volume transmittion along the shadow ray (it is incorrect if the light source is outside of the
        // current volume, but in this case it will be discarded anyway since the intersection at the outer bound
        // of a current volume), so the result is fully correct.
        float3 tr = Volume_Transmittance(&volumes[volidx], &shadowrays[globalid], shadow_ray_length);

        // Volume emission is applied only if the light source is in the current volume(this is incorrect since the light source might be
        // outside of a volume and we have to compute fraction of ray in this case, but need to figure out how)
        float3 r = Volume_Emission(&volumes[volidx], &shadowrays[globalid], shadow_ray_length);

        // This is the estimate coming from a light source
        // TODO: remove hardcoded phase func and sigma
        r += tr * le * volumes[volidx].sigma_s * PhaseFunction_Uniform(wi, normalize(wo)) / pdf / selection_pdf;

        // Only if we have some radiance compute the visibility ray
        if (NON_BLACK(tr) && NON_BLACK(r) && pdf > 0.f)
        {
            // Put lightsample result
            lightsamples[globalid] = REASONABLE_RADIANCE(r * Path_GetThroughput(path));
        }
        else
        {
            // Nothing to compute
            lightsamples[globalid] = 0.f;
            // Otherwise make it incative to save intersector cycles (hopefully)
            Ray_SetInactive(shadowrays + globalid);
        }

#ifdef MULTISCATTER
        // This is highly brute-force
        // TODO: investigate importance sampling techniques here
        wo = Sample_MapToSphere(sample2);
        pdf = 1.f / (4.f * PI);

        // Generate new path segment
        Ray_Init(indirectrays + globalid, dg.p, normalize(wo), CRAZY_HIGH_DISTANCE, 0.f, 0xFFFFFFFF);

        // Update path throughput multiplying by phase function.
        Path_MulThroughput(path, volumes[volidx].sigma_s * PhaseFunction_Uniform(wi, normalize(wo)) / pdf);
#else
        // Single-scattering mode only,
        // kill the path and compact away on next iteration
        Path_Kill(path);
        Ray_SetInactive(indirectrays + globalid);
#endif
    }
}

#define CMJ_DIM 16

// Handle ray-surface interaction possibly generating path continuation.
// This is only applied to non-scattered paths.
__kernel void ShadeSurface(
    // Ray batch
    __global ray const* rays,
    // Intersection data
    __global Intersection const* isects,
    // Hit indices
    __global int const* hitindices,
    // Pixel indices
    __global int const* pixelindices,
    // Number of rays
    __global int const* numhits,
    // Vertices
    __global float3 const* vertices,
    // Normals
    __global float3 const* normals,
    // UVs
    __global float2 const* uvs,
    // Indices
    __global int const* indices,
    // Shapes
    __global Shape const* shapes,
    // Material IDs
    __global int const* materialids,
    // Materials
    __global Material const* materials,
    // Textures
    TEXTURE_ARG_LIST,
    // Environment texture index
    int envmapidx,
    // Envmap multiplier
    float envmapmul,
    // Emissives
    __global Light const* lights,
    // Number of emissive objects
    int num_lights,
    // RNG seed
    int rngseed,
    // Sampler states
    __global SobolSampler* samplers,
    // Sobol matrices
    __global uint const* sobolmat,
    // Current bounce
    int bounce,
    // Frame
    int frame,
    // Volume data
    __global Volume const* volumes,
    // Shadow rays
    __global ray* shadowrays,
    // Light samples
    __global float3* lightsamples,
    // Path throughput
    __global Path* paths,
    // Indirect rays
    __global ray* indirectrays,
    // Radiance
    __global float3* output
)
{
    int globalid = get_global_id(0);

    Scene scene =
    {
        vertices,
        normals,
        uvs,
        indices,
        shapes,
        materialids,
        materials,
        lights,
        envmapidx,
        envmapmul,
        num_lights
    };

    // Only applied to active rays after compaction
    if (globalid < *numhits)
    {
        // Fetch index
        int hitidx = hitindices[globalid];
        int pixelidx = pixelindices[globalid];
        Intersection isect = isects[hitidx];

        __global Path* path = paths + pixelidx;

        // Early exit for scattered paths
        if (Path_IsScattered(path))
        {
            return;
        }

        // Fetch incoming ray direction
        float3 wi = -normalize(rays[hitidx].d.xyz);
#if (SOBOL == 1)
        // Sample light
        __global SobolSampler* sampler = samplers + pixelidx;

        float2 sample0;
        sample0.x = SobolSampler_Sample1D(sampler->seq, GetSampleDim(bounce, kBrdf), sampler->s0, sobolmat);
        sample0.y = SobolSampler_Sample1D(sampler->seq, GetSampleDim(bounce, kLight), sampler->s0, sobolmat);

        float2 sample1;
        sample1.x = SobolSampler_Sample1D(sampler->seq, GetSampleDim(bounce, kLightU), sampler->s0, sobolmat);
        sample1.y = SobolSampler_Sample1D(sampler->seq, GetSampleDim(bounce, kLightV), sampler->s0, sobolmat);

        float2 sample2;
        sample2.x = SobolSampler_Sample1D(sampler->seq, GetSampleDim(bounce, kBrdfU), sampler->s0, sobolmat);
        sample2.y = SobolSampler_Sample1D(sampler->seq, GetSampleDim(bounce, kBrdfV), sampler->s0, sobolmat);

        float2 sample3;
        sample3.x = SobolSampler_Sample1D(sampler->seq, GetSampleDim(bounce, kIndirectU), sampler->s0, sobolmat);
        sample3.y = SobolSampler_Sample1D(sampler->seq, GetSampleDim(bounce, kIndirectV), sampler->s0, sobolmat);

        float sample4 = SobolSampler_Sample1D(sampler->seq, GetSampleDim(bounce, kRR), sampler->s0, sobolmat);
#elif (RANDOM == 1)
        // Prepare RNG
        Rng rng;
        InitRng(rngseed + (globalid << 2) * 157 + 13, &rng);
        float2 sample0 = UniformSampler_Sample2D(&rng);
        float2 sample1 = UniformSampler_Sample2D(&rng);
        float2 sample2 = UniformSampler_Sample2D(&rng);
        float2 sample3 = UniformSampler_Sample2D(&rng);
        float  sample4 = UniformSampler_Sample2D(&rng).x;
#elif (CMJ == 1)
        Rng rng;
        InitRng(rngseed + (globalid << 2) * 157 + 13, &rng);
        float2 sample0 = UniformSampler_Sample2D(&rng);
        float2 sample1 = UniformSampler_Sample2D(&rng);
        float2 sample2 = UniformSampler_Sample2D(&rng);
        float2 sample3 = UniformSampler_Sample2D(&rng);
        float  sample4 = UniformSampler_Sample2D(&rng).x;

        int pass = frame / (CMJ_DIM * CMJ_DIM);
        int subsample0 = permute(frame % (CMJ_DIM * CMJ_DIM), CMJ_DIM * CMJ_DIM, (pixelidx + bounce) * 0xc517e953);
        int subsample1 = permute(frame % (CMJ_DIM * CMJ_DIM), CMJ_DIM * CMJ_DIM, (pixelidx + bounce + 1) * 0xc517e953);
        int subsample2 = permute(frame % (CMJ_DIM * CMJ_DIM), CMJ_DIM * CMJ_DIM, (pixelidx + bounce + 2) * 0xc517e953);
        int subsample3 = permute(frame % (CMJ_DIM * CMJ_DIM), CMJ_DIM * CMJ_DIM, (pixelidx + bounce + 3) * 0xc517e953);
        int subsample4 = permute(frame % (CMJ_DIM * CMJ_DIM), CMJ_DIM * CMJ_DIM, (pixelidx + bounce + 4) * 0xc517e953);

        int pattern0 = (pass + bounce + pixelidx) % 13331;
        int pattern1 = (pass + bounce + pixelidx + 1) % 13331;
        int pattern2 = (pass + bounce + pixelidx + 2) % 13331;
        int pattern3 = (pass + bounce + pixelidx + 3) % 13331;
        int pattern4 = (pass + bounce + pixelidx + 4) % 13331;

        sample0 = cmj(subsample0, CMJ_DIM, pattern0);
        sample1 = cmj(subsample1, CMJ_DIM, pattern1);
        sample2 = cmj(subsample2, CMJ_DIM, pattern2);
        sample3 = cmj(subsample3, CMJ_DIM, pattern2);
        sample4 = cmj(subsample4, CMJ_DIM, pattern2).x;
#else

#endif

        // Fill surface data
        DifferentialGeometry diffgeo;
        DifferentialGeometry_Fill(&scene, &isect, &diffgeo);

        // Check if we are hitting from the inside

        float backfacing = dot(diffgeo.ng, wi) < 0.f;
        int twosided = diffgeo.mat.twosided;
        if (twosided && backfacing)
        {
            // Reverse normal and tangents in this case
            // but not for BTDFs, since BTDFs rely
            // on normal direction in order to arrange
            // indices of refraction
            diffgeo.n = -diffgeo.n;
            diffgeo.dpdu = -diffgeo.dpdu;
            diffgeo.dpdv = -diffgeo.dpdv;
        }

        float ndotwi = dot(diffgeo.n, wi);

        // Select BxDF
        Material_Select(
            &scene, wi, TEXTURE_ARGS,
#ifdef SOBOL
            sampler, sobolmat, bounce,
#else
            &rng,
#endif
            &diffgeo
        );

        // Terminate if emissive
        if (Bxdf_IsEmissive(&diffgeo))
        {
            if (!backfacing)
            {
                float weight = 1.f;

                if (bounce > 0 && !Path_IsSpecular(path))
                {
                    float2 extra = Ray_GetExtra(&rays[hitidx]);
                    float ld = isect.uvwt.w;
                    float denom = extra.y * diffgeo.area;
                    // TODO: num_lights should be num_emissies instead, presence of analytical lights breaks this code
                    float bxdflightpdf = denom > 0.f ? (ld * ld / denom / num_lights) : 0.f;
                    weight = BalanceHeuristic(1, extra.x, 1, bxdflightpdf);
                }

                {
                    // In this case we hit after an application of MIS process at previous step.
                    // That means BRDF weight has been already applied.
                    output[pixelidx] += Path_GetThroughput(path) * Emissive_GetLe(&diffgeo, TEXTURE_ARGS) * weight;
                }
            }

            Path_Kill(path);
            Ray_SetInactive(shadowrays + globalid);
            Ray_SetInactive(indirectrays + globalid);

            lightsamples[globalid] = 0.f;
            return;
        }

        float s = Bxdf_IsBtdf(&diffgeo) ? (-sign(ndotwi)) : 1.f;
        if (!twosided && backfacing && !Bxdf_IsBtdf(&diffgeo))
        {
             //Reverse normal and tangents in this case
             //but not for BTDFs, since BTDFs rely
             //on normal direction in order to arrange
             //indices of refraction
            diffgeo.n = -diffgeo.n;
            diffgeo.dpdu = -diffgeo.dpdu;
            diffgeo.dpdv = -diffgeo.dpdv;
        }

        // TODO: this is test code, need to
        // maintain proper volume stack here
        //if (Bxdf_IsBtdf(&diffgeo))
        //{
        //    // If we entering set the volume
        //    path->volume = !backfacing ? 0 : -1;
        //}

        // Check if we need to apply normal map
        //ApplyNormalMap(&diffgeo, TEXTURE_ARGS);
        DifferentialGeometry_ApplyBumpMap(&diffgeo, TEXTURE_ARGS);
        DifferentialGeometry_CalculateTangentTransforms(&diffgeo);

        float lightpdf = 0.f;
        float bxdflightpdf = 0.f;
        float bxdfpdf = 0.f;
        float lightbxdfpdf = 0.f;
        float selection_pdf = 0.f;
        float3 radiance = 0.f;
        float3 lightwo;
        float3 bxdfwo;
        float3 wo;
        float bxdfweight = 1.f;
        float lightweight = 1.f;

        int light_idx = num_lights > 0 ? Scene_SampleLight(&scene, sample0.y, &selection_pdf) : -1;

        float3 throughput = Path_GetThroughput(path);

        // Sample bxdf
        float3 bxdf = Bxdf_Sample(&diffgeo, wi, TEXTURE_ARGS, sample2, &bxdfwo, &bxdfpdf);

        // If we have light to sample we can hopefully do mis
        if (light_idx > -1)
        {
            // Sample light
            float3 le = Light_Sample(light_idx, &scene, &diffgeo, TEXTURE_ARGS, sample1, &lightwo, &lightpdf);
            lightbxdfpdf = Bxdf_GetPdf(&diffgeo, wi, normalize(lightwo), TEXTURE_ARGS);
            lightweight = Light_IsSingular(&scene.lights[light_idx]) ? 1.f : BalanceHeuristic(1, lightpdf, 1, lightbxdfpdf);

            // Apply MIS to account for both
            if (NON_BLACK(le) && lightpdf > 0.0f && !Bxdf_IsSingular(&diffgeo))
            {
                wo = lightwo;
                float ndotwo = fabs(dot(diffgeo.n, normalize(wo)));
                radiance = le * Bxdf_Evaluate(&diffgeo, wi, normalize(wo), TEXTURE_ARGS) * throughput * ndotwo * lightweight / lightpdf / selection_pdf;
            }
        }

        // If we have some light here generate a shadow ray
        if (NON_BLACK(radiance))
        {
            // Generate shadow ray
            float shadow_ray_length = (1.f - 2.f * CRAZY_LOW_DISTANCE) * length(wo);
            float3 shadow_ray_dir = normalize(wo);
            float3 shadow_ray_o = diffgeo.p + CRAZY_LOW_DISTANCE * s * diffgeo.n;
            int shadow_ray_mask = Bxdf_IsSingular(&diffgeo) ? 0xFFFFFFFF : 0x0000FFFF;

            Ray_Init(shadowrays + globalid, shadow_ray_o, shadow_ray_dir, shadow_ray_length, 0.f, shadow_ray_mask);

            // Apply the volume to shadow ray if needed
            int volidx = Path_GetVolumeIdx(path);
            if (volidx != -1)
            {
                radiance *= Volume_Transmittance(&volumes[volidx], &shadowrays[globalid], shadow_ray_length);
                radiance += Volume_Emission(&volumes[volidx], &shadowrays[globalid], shadow_ray_length) * throughput;
            }

            // And write the light sample
            lightsamples[globalid] = REASONABLE_RADIANCE(radiance);
        }
        else
        {
            // Otherwise save some intersector cycles
            Ray_SetInactive(shadowrays + globalid);
            lightsamples[globalid] = 0;
        }

        // Apply Russian roulette
        float q = max(min(0.5f,
            // Luminance
            0.2126f * throughput.x + 0.7152f * throughput.y + 0.0722f * throughput.z), 0.01f);
        // Only if it is 3+ bounce
        bool rr_apply = bounce > 3;
        bool rr_stop = sample4 > q && rr_apply;

        if (rr_apply)
        {
            Path_MulThroughput(path, 1.f / q);
        }

        if (Bxdf_IsSingular(&diffgeo))
        {
            Path_SetSpecularFlag(path);
        }

        bxdfwo = normalize(bxdfwo);
        float3 t = bxdf * fabs(dot(diffgeo.n, bxdfwo));

        // Only continue if we have non-zero throughput & pdf
        if (NON_BLACK(t) && bxdfpdf > 0.f && !rr_stop)
        {
            // Update the throughput
            Path_MulThroughput(path, t / bxdfpdf);

            // Generate ray
            float3 indirect_ray_dir = bxdfwo;
            float3 indirect_ray_o = diffgeo.p + CRAZY_LOW_DISTANCE * s * diffgeo.n;

            Ray_Init(indirectrays + globalid, indirect_ray_o, indirect_ray_dir, CRAZY_HIGH_DISTANCE, 0.f, 0xFFFFFFFF);
            Ray_SetExtra(indirectrays + globalid, make_float2(bxdfpdf, fabs(dot(diffgeo.n, bxdfwo))));
        }
        else
        {
            // Otherwise kill the path
            Path_Kill(path);
            Ray_SetInactive(indirectrays + globalid);
        }
    }
}

///< Illuminate missing rays
__kernel void ShadeMiss(
    // Ray batch
    __global ray const* rays,
    // Intersection data
    __global Intersection const* isects,
    // Pixel indices
    __global int const* pixelindices,
    // Number of rays
    int numrays,
    // Textures
    TEXTURE_ARG_LIST,
    // Environment texture index
    int envmapidx,
    //
    __global Path const* paths,
    __global Volume const* volumes,
    // Output values
    __global float4* output
)
{
    int globalid = get_global_id(0);

    if (globalid < numrays)
    {
        int pixelidx = pixelindices[globalid];

        // In case of a miss
        if (isects[globalid].shapeid < 0)
        {
            // Multiply by throughput
            int volidx = paths[pixelidx].volume;

            if (volidx == -1)
                output[pixelidx].xyz += Texture_SampleEnvMap(rays[globalid].d.xyz, TEXTURE_ARGS_IDX(envmapidx));
            else
            {
                output[pixelidx].xyz += Texture_SampleEnvMap(rays[globalid].d.xyz, TEXTURE_ARGS_IDX(envmapidx)) *
                    Volume_Transmittance(&volumes[volidx], &rays[globalid], rays[globalid].o.w);

                output[pixelidx].xyz += Volume_Emission(&volumes[volidx], &rays[globalid], rays[globalid].o.w);
            }
        }

        if (isnan(output[pixelidx].x) || isnan(output[pixelidx].y) || isnan(output[pixelidx].z))
        {
            output[pixelidx] = make_float4(100.f, 0.f, 0.f, 1.f);
        }

        output[pixelidx].w += 1.f;
    }
}

///< Handle light samples and visibility info and add contribution to final buffer
__kernel void GatherLightSamples(
    // Pixel indices
    __global int const* pixelindices,
    // Number of rays
    __global int* numrays,
    // Shadow rays hits
    __global int const* shadowhits,
    // Light samples
    __global float3 const* lightsamples,
    // throughput
    __global Path const* paths,
    // Radiance sample buffer
    __global float4* output
)
{
    int globalid = get_global_id(0);

    if (globalid < *numrays)
    {
        // Get pixel id for this sample set
        int pixelidx = pixelindices[globalid];

        // Prepare accumulator variable
        float3 radiance = make_float3(0.f, 0.f, 0.f);

        // Start collecting samples
        {
            // If shadow ray didn't hit anything and reached skydome
            if (shadowhits[globalid] == -1)
            {
                // Add its contribution to radiance accumulator
                radiance += lightsamples[globalid];
            }
        }

        // Divide by number of light samples (samples already have built-in throughput)
        output[pixelidx].xyz += radiance;
    }
}

///< Handle light samples and visibility info and add contribution to final buffer
__kernel void GatherOcclusion(
    // Pixel indices
    __global int const* pixelindices,
    // Number of rays
    __global int const* numrays,
    // Shadow rays hits
    __global int const* shadowhits,
    // Light samples
    __global float3 const* lightsamples,
    // throughput
    __global float3 const* throughput,
    // Radiance sample buffer
    __global float4* output
)
{
    int globalid = get_global_id(0);

    if (globalid < *numrays)
    {
        // Get pixel id for this sample set
        int pixelidx = pixelindices[globalid];

        float visibility = (shadowhits[globalid] == 1) ? 0.f : 1.f;

        output[pixelidx].xyz += visibility;
        output[pixelidx].w += 1.f;
    }
}

///< Restore pixel indices after compaction
__kernel void RestorePixelIndices(
    // Compacted indices
    __global int const* compacted_indices,
    // Number of compacted indices
    __global int* numitems,
    // Previous pixel indices
    __global int const* previndices,
    // New pixel indices
    __global int* newindices
)
{
    int globalid = get_global_id(0);

    // Handle only working subset
    if (globalid < *numitems)
    {
        newindices[globalid] = previndices[compacted_indices[globalid]];
    }
}

///< Restore pixel indices after compaction
__kernel void FilterPathStream(
    // Intersections
    __global Intersection const* isects,
    // Number of compacted indices
    __global int const* numitems,
    // Pixel indices
    __global int const* pixelindices,
    // Paths
    __global Path* paths,
    // Predicate
    __global int* predicate
)
{
    int globalid = get_global_id(0);

    // Handle only working subset
    if (globalid < *numitems)
    {
        int pixelidx = pixelindices[globalid];

        __global Path* path = paths + pixelidx;

        if (Path_IsAlive(path))
        {
            bool kill = (length(Path_GetThroughput(path)) < CRAZY_LOW_THROUGHPUT);

            if (!kill)
            {
                predicate[globalid] = isects[globalid].shapeid >= 0 ? 1 : 0;
            }
            else
            {
                Path_Kill(path);
                predicate[globalid] = 0;
            }
        }
        else
        {
            predicate[globalid] = 0;
        }
    }
}

///< Illuminate missing rays
__kernel void ShadeBackground(
    // Ray batch
    __global ray const* rays,
    // Intersection data
    __global Intersection const* isects,
    // Pixel indices
    __global int const* pixelindices,
    // Number of rays
    __global int const* numrays,
    // Textures
    TEXTURE_ARG_LIST,
    // Environment texture index
    int envmapidx,
    float envmapmul,
    //
    int num_lights,
    __global Path const* paths,
    __global Volume const* volumes,
    // Output values
    __global float4* output
)
{
    int globalid = get_global_id(0);

    if (globalid < *numrays)
    {
        int pixelidx = pixelindices[globalid];

        __global Path const* path = paths + pixelidx;

        // In case of a miss
        if (isects[globalid].shapeid < 0 && Path_IsAlive(path))
        {
            float3 t = Path_GetThroughput(path);
            output[pixelidx].xyz += REASONABLE_RADIANCE(envmapmul * Texture_SampleEnvMap(rays[globalid].d.xyz, TEXTURE_ARGS_IDX(envmapidx)) * t);
        }
    }
}

// Copy data to interop texture if supported
__kernel void AccumulateData(
    __global float4 const* srcdata,
    int numelems,
    __global float4* dstdata
)
{
    int gid = get_global_id(0);

    if (gid < numelems)
    {
        float4 v = srcdata[gid];
        dstdata[gid] += v;
    }
}

// Copy data to interop texture if supported
__kernel void ApplyGammaAndCopyData(
    __global float4 const* data,
    int imgwidth,
    int imgheight,
    float gamma,
    write_only image2d_t img
)
{
    int gid = get_global_id(0);

    int gidx = gid % imgwidth;
    int gidy = gid / imgwidth;

    if (gidx < imgwidth && gidy < imgheight)
    {
        float4 v = data[gid];
        float4 val = clamp(native_powr(v / v.w, 1.f / gamma), 0.f, 1.f);
        write_imagef(img, make_int2(gidx, gidy), val);
    }
}


Removing a function that is no longer in use.

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/kknox/clSPARSE.git/'


write_image.cl: fix argument address spaces

image2d_t are always global, so we need to cast them to global struct *

/* OpenCL built-in library: write_image()

   Copyright (c) 2013 Ville Korhonen

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

#include "templates.h"
#include "pocl_image_rw_utils.h"

#if (__clang_major__ == 3) && (__clang_minor__ >= 5)
// Clang 3.5 crashes in case trying to cast to the private pointer,
// adding the global qualifier fixes it. Clang 3.4 crashes if it's
// there. The issue is in SROA.
#define ADDRESS_SPACE global
#else
#define ADDRESS_SPACE
#endif

/* writes pixel to coord in image */
void pocl_write_pixel (void* color_, ADDRESS_SPACE dev_image_t* dev_image, int4 coord)
{
  uint4 *color = (uint4*)color_;
  int i, idx;
  int width = dev_image->width;
  int height = dev_image->height;
  int num_channels = dev_image->num_channels;
  int elem_size = dev_image->elem_size;

  if (dev_image->order == CL_A)
    {
      idx = (coord.x + coord.y*width + coord.z*height*width) * num_channels;
      if (elem_size == 1)
        ((uchar*)(dev_image->data))[idx] = (*color)[3];
      if (elem_size == 2)
        ((ushort*)(dev_image->data))[idx] = (*color)[3];
      if (elem_size == 4)
        ((uint*)(dev_image->data))[idx] = (*color)[3];
      return;
    }

  for (i = 0; i < num_channels; i++)
    {
      idx = i + (coord.x + coord.y*width + coord.z*height*width)*num_channels;
      if (elem_size == 1)
        {
          ((uchar*)dev_image->data)[idx] = (*color)[i];
        }
      if (elem_size == 2)
        {
          ((ushort*)dev_image->data)[idx] = (*color)[i];
        }
      if (elem_size == 4)
        {
          ((uint*)dev_image->data)[idx] = (*color)[i];
        }
    }
}

/* Implementation for write_image with any image data type and int coordinates
   __IMGTYPE__ = image type (image2d_t, ...)
   __DTYPE__  = data type to be read (int4 or uint4 float4)
   __POSTFIX__ = function name postfix (i, ui, f)
   __COORD__   = coordinate type (int, int2, int4)
*/
#define IMPLEMENT_WRITE_IMAGE_INT_COORD(__IMGTYPE__,__DTYPE__,__POSTFIX__, \
                                        __COORD__)                      \
  void _CL_OVERLOADABLE write_image##__POSTFIX__ (__IMGTYPE__ image,    \
                                                  __COORD__ coord,      \
                                                  __DTYPE__ color)      \
  {                                                                     \
  int4 coord4;                                                          \
  INITCOORD##__COORD__(coord4, coord);                                  \
  pocl_write_pixel (&color, *(ADDRESS_SPACE dev_image_t**)&image, coord4);                             \
  }                                                                     \

IMPLEMENT_WRITE_IMAGE_INT_COORD(image2d_t, uint4, ui, int2)
IMPLEMENT_WRITE_IMAGE_INT_COORD(image2d_t, float4, f, int2);

/* OpenCL built-in library: write_image()

   Copyright (c) 2013 Ville Korhonen

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

#include "templates.h"
#include "pocl_image_rw_utils.h"

#if (__clang_major__ == 3) && (__clang_minor__ >= 5)
// Clang 3.5 crashes in case trying to cast to the private pointer,
// adding the global qualifier fixes it. Clang 3.4 crashes if it's
// there. The issue is in SROA.
#define ADDRESS_SPACE global
#else
#define ADDRESS_SPACE
#endif

/* writes pixel to coord in image */
void pocl_write_pixel (void* color_, void* image, int4 coord)
{
  ADDRESS_SPACE dev_image_t* dev_image = *((ADDRESS_SPACE dev_image_t**)image);
  uint4 *color = (uint4*)color_;
  int i, idx;
  int width = dev_image->width;
  int height = dev_image->height;
  int num_channels = dev_image->num_channels;
  int elem_size = dev_image->elem_size;

  if (dev_image->order == CL_A)
    {
      idx = (coord.x + coord.y*width + coord.z*height*width) * num_channels;
      if (elem_size == 1)
        ((uchar*)(dev_image->data))[idx] = (*color)[3];
      if (elem_size == 2)
        ((ushort*)(dev_image->data))[idx] = (*color)[3];
      if (elem_size == 4)
        ((uint*)(dev_image->data))[idx] = (*color)[3];
      return;
    }

  for (i = 0; i < num_channels; i++)
    {
      idx = i + (coord.x + coord.y*width + coord.z*height*width)*num_channels;
      if (elem_size == 1)
        {
          ((uchar*)dev_image->data)[idx] = (*color)[i];
        }
      if (elem_size == 2)
        {
          ((ushort*)dev_image->data)[idx] = (*color)[i];
        }
      if (elem_size == 4)
        {
          ((uint*)dev_image->data)[idx] = (*color)[i];
        }
    }
}

/* Implementation for write_image with any image data type and int coordinates
   __IMGTYPE__ = image type (image2d_t, ...)
   __DTYPE__  = data type to be read (int4 or uint4 float4)
   __POSTFIX__ = function name postfix (i, ui, f)
   __COORD__   = coordinate type (int, int2, int4)
*/
#define IMPLEMENT_WRITE_IMAGE_INT_COORD(__IMGTYPE__,__DTYPE__,__POSTFIX__, \
                                        __COORD__)                      \
  void _CL_OVERLOADABLE write_image##__POSTFIX__ (__IMGTYPE__ image,    \
                                                  __COORD__ coord,      \
                                                  __DTYPE__ color)      \
  {                                                                     \
  int4 coord4;                                                          \
  INITCOORD##__COORD__(coord4, coord);                                  \
  pocl_write_pixel (&color, &image, coord4);                             \
  }                                                                     \

IMPLEMENT_WRITE_IMAGE_INT_COORD(image2d_t, uint4, ui, int2)
IMPLEMENT_WRITE_IMAGE_INT_COORD(image2d_t, float4, f, int2);


Fix some syntax errors

Fix some syntax errors

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/CLBplustree/CLBplustree.git/'


Create CLPhysics3D.cl

Create CLPhysics3D.cl

typedef struct{
    float4 force;
    float4 vel;
    float4 pos;
    float mass;
    float fixed;
    float locked;
    float dummy;
} Particle;

typedef struct{
    float springConstant;
    float damping;
    float restLength;
    float on;
} Spring;

__kernel void tick( __global Particle* particles,float time,__global float4* pos,__global Spring* springs, __global Particle* anchors,__global Particle* attractPoint,float range, float distanceMin, float drag,float density,float gravity,__global float4* linePos,__global float4* lineCol,__global int* lineCount,float lineBetweenDistance) {

    //printf("Tick\n\n");
    int i = get_global_id(0);
    if(i==0){
        *lineCount = 0;
    }
    __global Particle* a = &particles[i];

    __global Particle* b = attractPoint;// mouse
    __global Particle* p = a;

    //-------------Gravity----------------
    if(gravity != 0.0){
        a->force += (float4)(0,gravity,0,0);
    }

    //----------Drag----------------
    p = a;
    p->force -= p->vel*drag;

    //----------Spring----------------
    __global Spring *s = springs;
    b = &anchors[i];
    if(s->on != 0.0&&(a->fixed != 1.0|| b->fixed != 1.0) ){
        float4 a2b = a->pos - b->pos;
        float a2bDistance = fast_length(a2b);
        a2b = fast_normalize(a2b);
        float springForce = -( a2bDistance - s->restLength ) * s->springConstant;
        float4 Va2b = a->vel - b->vel;
        float dampingForce = -1*s->damping * ( a2b.x*Va2b.x + a2b.y*Va2b.y);
        float r = springForce + dampingForce;
        a2b *= r;
        if ( a->fixed != 1.0)
            a->force += a2b;
    }

    //-----Attraction--------
    b = attractPoint;
    if ( a->fixed != 1.0|| b->fixed != 1.0 ){
        float4 b2a = b->pos - a->pos;
        float distance = fast_length(b2a);
        if ( distance < distanceMin )
            distance = distanceMin;

        float force=range/ldexp(distance,density);

        b2a = fast_normalize(b2a);
        b2a = b2a*force;
        a->vel += b2a;
    }

    //-------ApplyForce--------

    if (p->fixed != 1.0f) {
        float4 acc = p->force /(p->mass*time);
        p->vel +=  acc;
        p->pos = p->pos + (p->vel/time);
    }
    pos[i] = p->pos;

    //------------Calculate lines-------------
    if(i!=0){
        float d = distance(pos[i].xy,pos[i-1].xy);
        //printf("%f\n",d);
        float pVel = fast_length(p->vel);
        if(d<lineBetweenDistance&&pVel>2){
            atomic_inc(lineCount);
            //----deal with linePos and lineCol, lineCount
            linePos[(i-1)*2]= pos[i];
            linePos[(i-1)*2+1] = pos[i-1];

//            lineCol[(i-1)*2] = (float4)(0.0f,0.0f,0.0f,0.0001+0.002*pVel);
//            lineCol[(i-1)*2+1] = (float4)(0.0f,0.0f,0.0f,0.0001+0.002*pVel);

            lineCol[(i-1)*2].w = 0.0001+0.002*pVel;
            lineCol[(i-1)*2+1].w = 0.0001+0.002*pVel;

        }
        else
        {
            linePos[(i-1)*2]= (float4)(0.0f,0.0f,0.0f,0.0f);
            linePos[(i-1)*2+1] = (float4)(0.0f,0.0f,0.0f,0.0f);

//            lineCol[(i-1)*2]= (float4)(0.0f,0.0f,0.0f,0.0f);
//            lineCol[(i-1)*2+1] = (float4)(0.0f,0.0f,0.0f,0.0f);
        }
        //printf("%f\t%f\n%f\t%f\n",linePos[(i-1)*2].x,linePos[(i-1)*2].y,linePos[(i-1)*2+1].x,linePos[(i-1)*2+1].y);

    }

    //---ClearForce------
    p->force = (float4)(0,0,0,0);
}

error: pathspec 'CLPhysics3D.cl' did not match any file(s) known to git


sixtracklib/opencl: added generic NS(ManagedBuffer) remapping kernel for OpenCL

#ifndef SIXTRACKLIB_OPENCL_IMPL_MANAGED_BUFFER_REMAP_KERNEL_OPENCL_CL__
#define SIXTRACKLIB_OPENCL_IMPL_MANAGED_BUFFER_REMAP_KERNEL_OPENCL_CL__

#if !defined( SIXTRL_NO_INCLUDES )
    #include "sixtracklib/_impl/definitions.h"
    #include "sixtracklib/common/impl/buffer_defines.h"
    #include "sixtracklib/common/impl/managed_buffer_minimal.h"
    #include "sixtracklib/common/impl/managed_buffer_remap.h"
#endif /* !defined( SIXTRL_NO_INCLUDES ) */

__kernel void NS(ManagedBuffer_remap_opencl)(
    SIXTRL_BUFFER_DATAPTR_DEC unsigned char* SIXTRL_RESTRICT buffer_begin,
    SIXTRL_UINT64_T const buffer_slot_size,
    SIXTRL_BUFFER_DATAPTR_DEC long int* SIXTRL_RESTRICT ptr_success_flag )
{
    typedef NS(buffer_size_t) buf_size_t;

    size_t const global_id           = get_global_id( 0 );
    size_t const gid_to_remap_buffer = ( size_t )0u;

    if( gid_to_remap_buffer == global_id )
    {
        long int success_flag = ( buffer_begin != SIXTRL_NULLPTR ) ? 0 : -1;

        buf_size_t const slot_size =
            ( buffer_slot_size > ( SIXTRL_UINT64_T )0u )
                ? ( buf_size_t )buffer_slot_size : ( buf_size_t )8u;

        if( 0 != NS(ManagedBuffer_remap)( buffer_begin, slot_size ) )
        {
            success_flag |= -2;
        }

        if( ( success_flag == 0 ) &&
            ( NS(ManagedBuffer_needs_remapping)( buffer_begin, slot_size ) ) )
        {
            success_flag |= -4;
        }

        if(  ptr_success_flag != SIXTRL_NULLPTR )
        {
            *ptr_success_flag  = success_flag;
        }
    }

    return;
}

#endif /* SIXTRACKLIB_OPENCL_IMPL_MANAGED_BUFFER_REMAP_KERNEL_OPENCL_CL__ */
/* end: sixtracklib/opencl/impl/managed_buffer_remap_kernel.cl */

error: pathspec 'sixtracklib/opencl/impl/managed_buffer_remap_kernel.cl' did not match any file(s) known to git


/******************************************************************************
 * @file     prngcl_common.cl
 * @author   Vadim Demchik <vadimdi@yahoo.com>
 * @version  1.1
 *
 * @brief    [PRNGCL library]
 *           common module for all PRNG implementations
 *           contains precision selection, etc.
 *
 *
 * @section  LICENSE
 *
 * Copyright (c) 2013, 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_COMMON_CL
#define PRNGCL_COMMON_CL

#define GID_SIZE    (get_global_size(0) * get_global_size(1) * get_global_size(2))
#define GID         (get_global_id(0) + get_global_id(1) * get_global_size(0) + get_global_id(2) * get_global_size(0) * get_global_size(1))

#if defined(cl_khr_fp64)

#define hgpu_double     double
#define hgpu_double2    double2
#define hgpu_double3    double3
#define hgpu_double4    double4

#if defined(cl_amd_fp64)    // AMD extension available?
	#pragma OPENCL EXTENSION cl_amd_fp64 : enable
#elif defined(cl_khr_fp64)  // Khronos extension available?
	#pragma OPENCL EXTENSION cl_khr_fp64 : enable
#endif

#ifndef PRNG_PRECISION
#define PRNG_PRECISION   0       // single precision by default
#endif

#if ((PRNG_PRECISION==0)||(PRNG_PRECISION==1))
#define PRECISION_SINGLE            // define single precision
#elif (PRNG_PRECISION==2)
#define PRECISION_DOUBLE            // define double precision
#endif

#ifdef PRECISION_DOUBLE
#define hgpu_float   hgpu_double
#define hgpu_float2  hgpu_double2
#define hgpu_float3  hgpu_double3
#define hgpu_float4  hgpu_double4
#else
#define hgpu_float   float
#define hgpu_float2  float2
#define hgpu_float3  float3
#define hgpu_float4  float4
#endif

#define hgpu_single  float
#define hgpu_single2 float2
#define hgpu_single3 float3
#define hgpu_single4 float4

inline hgpu_double4 hgpu_uint4_2_double4(uint4 x){
    hgpu_double4 result;
    result.x = (double) x.x;
    result.y = (double) x.y;
    result.z = (double) x.z;
    result.w = (double) x.w;

    return result;
}

inline hgpu_double hgpu_uint_to_double(uint rnd1, uint rnd2, uint rnd_min, uint rnd_max, double k){
    hgpu_double result = (rnd1 + k * rnd2 - (rnd_min + k * rnd_max)) / ((rnd_max - rnd_min) * (1.0 - k));
    return result;
}

inline hgpu_double4 hgpu_uint4_to_double4(uint4 rnd1, uint4 rnd2, uint rnd_min, uint rnd_max, double k){
    hgpu_double4 result;
    hgpu_double4 drnd1 = hgpu_uint4_2_double4(rnd1);
    hgpu_double4 drnd2 = hgpu_uint4_2_double4(rnd2);
    double a1 = (rnd_min + k * rnd_max);
    double a2 = ((rnd_max - rnd_min) * (1.0 - k));
        result.x = (drnd1.x + k * drnd2.x - a1) / a2;
        result.y = (drnd1.y + k * drnd2.y - a1) / a2;
        result.z = (drnd1.z + k * drnd2.z - a1) / a2;
        result.w = (drnd1.w + k * drnd2.w - a1) / a2;
        return result;
}

inline hgpu_double hgpu_float_to_double(float rnd1, float rnd2, double rnd_min, double rnd_max, double k){
    hgpu_double result;
        double a1 = (rnd_max - rnd_min);
        double a2 = k * a1;
        double a3 = a1 * (1.0 - k);
        double a4 = -(rnd_min / a1 + rnd_max) * k - rnd_min;
        result = (trunc((rnd1 - rnd_min) / a2) + rnd2 / a1) * k / a3 + a4 / a3;
        return result;
}

inline hgpu_double2 hgpu_float4_to_double2(float4 rnd, double rnd_min, double rnd_max, double k){
    hgpu_double2 result;
        double a1 = (rnd_max - rnd_min);
        double a2 = k * a1;
        double a3 = a1 * (1.0 - k);
        double a4 = -(rnd_min / a1 + rnd_max) * k - rnd_min;
        result.x = (trunc((rnd.x - rnd_min) / a2) + rnd.y / a1) * k / a3 + a4 / a3;
        result.y = (trunc((rnd.z - rnd_min) / a2) + rnd.w / a1) * k / a3 + a4 / a3;
        return result;
}

inline hgpu_double4 hgpu_float4_to_double4(float4 rnd1, float4 rnd2, double rnd_min, double rnd_max, double k){
    hgpu_double4 result;
        double a1 = (rnd_max - rnd_min);
        double a2 = k * a1;
        double a3 = a1 * (1.0 - k);
        double a4 = -(rnd_min / a1 + rnd_max) * k - rnd_min;
        result.x = (trunc((rnd1.x - rnd_min) / a2) + rnd2.x / a1) * k / a3 + a4 / a3;
        result.y = (trunc((rnd1.y - rnd_min) / a2) + rnd2.y / a1) * k / a3 + a4 / a3;
        result.z = (trunc((rnd1.z - rnd_min) / a2) + rnd2.z / a1) * k / a3 + a4 / a3;
        result.w = (trunc((rnd1.w - rnd_min) / a2) + rnd2.w / a1) * k / a3 + a4 / a3;
        return result;
}

#endif

inline float4 hgpu_uint4_to_float4(uint4 x){
    float4 result;
    result.x = (float) x.x;
    result.y = (float) x.y;
    result.z = (float) x.z;
    result.w = (float) x.w;

    return result;
}

typedef union _Uint_and_Float   // Uint <---> Float converter
{
    uint uint_value;
    float float_value;
} Uint_and_Float;

#endif

error: pathspec 'randomcl/prngcl_common.cl' did not match any file(s) known to git


clbptCompact Revise

clbptCompact Revise


OpenCL implementation of RANMAR PRNG

OpenCL implementation of RANMAR PRNG

/******************************************************************************
 * @file     prngcl_ranmar.cl
 * @author   Vadim Demchik <vadimdi@yahoo.com>
 * @version  1.1
 *
 * @brief    [PRNGCL library]
 *           contains OpenCL implementation of RANMAR pseudo-random number generator
 *
 *
 * @section  CREDITS
 *
 *   George Marsaglia, Arif Zaman and Wai Wan Tsang,
 *   "Toward a universal random number generator",
 *   Statistics & Probability Letters, 9 (1990), 35--39.
 *
 *   F. James,
 *   "A review of pseudorandom number generators",
 *   Computer Physics Communications, 60 (1990), 329--344.
 *
 *
 * @section  LICENSE
 *
 * Copyright (c) 2013, 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_RANMAR_CL
#define PRNGCL_RANMAR_CL

#include "prngcl_common.cl"

#define RM_min    (1.0/16777216.0)
#define RM_max    (16777215.0/16777216.0)
#define RM_k      (5.9604644775390625E-8) // 1/2^24

#define RM_left   (RM_min+RM_k*RM_max)
#define RM_right  (RM_max+RM_k*RM_min)

#define RM_CD (7654321.0f / 16777216.0f)
#define RM_CM (16777213.0f /16777216.0f)

//________________________________________________________________________________________________________ RANMAR PRNG
__kernel void
ranmar_init(const __global uint4* seeds, __global float4* seedtable)
{
    float4 seed;
    //
    uint4 RM_seed1 = seeds[GID];
    uint4 RM_seed2 = seeds[GID + GID_SIZE];
    //
    uint4 i = ((RM_seed1 / 177) % 177) + 2;
    uint4 j = (RM_seed1 % 177) + 2;
    uint4 k = ((RM_seed2 / 169) % 178) + 1;
    uint4 l = (RM_seed2 % 169);
    for (int n = 0; n < 97; n++)
    {
        uint4 s = (uint4) 0;
        uint  t = 8388608;
        for (int m = 0; m < 24;  m++)
        {
            uint4 u = (i * j) % 179;
            u = (u * k) % 179;
            i = j;
            j = k;
            k = u;
            l = (53 * l + 1) % 169;
            uint4 lus = (l * u) % 64;

            if ( lus.x >= 32) s.x += t;
            if ( lus.y >= 32) s.y += t;
            if ( lus.z >= 32) s.z += t;
            if ( lus.w >= 32) s.w += t;
            t = t >> 1;
        }
        seed.x = ((float) s.x);
        seed.y = ((float) s.y);
        seed.z = ((float) s.z);
        seed.w = ((float) s.w);
        seedtable[GID + GID_SIZE * n] = seed / ((float4) 16777216.0f);
	}
    Uint_and_Float indx_I97, indx_J97;
    indx_I97.uint_value = 96;
    indx_J97.uint_value = 32;
    float4 indx;
    indx.x = indx_I97.float_value;
    indx.y = indx_J97.float_value;
    indx.z = (362436.0f / 16777216.0f);
    indx.w = 0.0f;

    seedtable[GID + GID_SIZE * 97] = indx;   // (RM_I97,RM_J97,RM_C,0)
}

__attribute__((always_inline)) float4
rm_step(__global float4 * seedtable,uint* RM_I97,uint* RM_J97,float* uniz)
{
        float4 uni;
        uni = seedtable[GID + GID_SIZE * (*RM_I97)] - seedtable[GID + GID_SIZE * (*RM_J97)];
	    uni = select(uni, (uni + 1.0f), uni < (float4) 0.0f);
        seedtable[GID + GID_SIZE * (*RM_I97)] = uni;

	    if ((*RM_I97) == 0) (*RM_I97) = 97;
	    if ((*RM_J97) == 0) (*RM_J97) = 97;
	    (*RM_I97)--;
	    (*RM_J97)--;

        (*uniz) -= RM_CD;
	    if ((*uniz) < 0.0) {(*uniz) += RM_CM;}
        uni -= (*uniz);
	    uni = select(uni, (uni + 1.0f), uni < (float4) 0.0f);
        return uni;
}

#ifdef PRECISION_DOUBLE  // if double precision is defined
__attribute__((always_inline)) hgpu_double4
rm_step_double(__global float4 * seedtable,uint* RM_I97,uint* RM_J97,float* uniz)
{
    hgpu_double4 result;
    float4 rnd1, rnd2;
    bool flag = true;
    rnd1 = rm_step(seedtable,RM_I97,RM_J97,uniz);
    while (flag) {
        rnd2 = rm_step(seedtable,RM_I97,RM_J97,uniz);
        result = hgpu_float4_to_double4(rnd1,rnd2,RM_min,RM_max,RM_k);
        if ((result.x>=RM_left) && (result.x<RM_right) &&
            (result.y>=RM_left) && (result.y<RM_right) &&
            (result.z>=RM_left) && (result.z<RM_right) &&
            (result.w>=RM_left) && (result.w<RM_right))
            flag = false;
        else
            rnd1 = rnd2;
    }

    return result;
}
#endif

__kernel void
ranmar(__global float4 * seedtable,__global hgpu_float4 * prns, const uint samples)
{
    uint giddst = GID;
    float4 uni = (float4) 0.0;
    Uint_and_Float indx_I97, indx_J97;
    float4 indx = seedtable[GID + GID_SIZE * 97];
    indx_I97.float_value = indx.x;
    indx_J97.float_value = indx.y;
    uint RM_I97 = indx_I97.uint_value;
    uint RM_J97 = indx_J97.uint_value;
    float uniz = indx.z;
    for (uint i=0; i<samples; i++) {
#ifdef PRECISION_DOUBLE // if double precision is defined
        hgpu_double4 result = rm_step_double(seedtable,&RM_I97,&RM_J97,&uniz);
        prns[giddst] = result;
#else
        uni = rm_step(seedtable,&RM_I97,&RM_J97,&uniz);
        prns[giddst] = uni;
#endif
        giddst += GID_SIZE;
    }

    indx_I97.uint_value = RM_I97;
    indx_J97.uint_value = RM_J97;
    indx.z = uniz;
    indx.x = indx_I97.float_value;
    indx.y = indx_J97.float_value;
    seedtable[GID + GID_SIZE * 97] = indx;
}

#endif

error: pathspec 'randomcl/prngcl_ranmar.cl' did not match any file(s) known to git


Practice writting a simple unit testing framework with macro

;; @file unit-testing-framework.cl
;;
;; @breif Simple unit testing framework to show the usage of macro
;;

; Original test codes
(defun test+ ()
  (format t "~:[FAIL~;pass~] ... ~a~%" (= (+ 1 2) 3) '(= (+ 1 2) 3))
  (format t "~:[FAIL~;pass~] ... ~a~%" (= (+ 1 2 3) 6) '(= (+ 1 2 3) 6))
  (format t "~:[FAIL~;pass~] ... ~a~%" (= (+ -1 -2) -3) '(= (+ -1 -2) -3)))
; Factor the 'format ...' expression out
(defun report-result (result label)
  (format t "~:[FAIL~;pass~] ... ~a~%" result label))
; So now the 'test+' function is simplified a little
(defun test+1 ()
  (report-result (= (+ 1 2) 3) '(= (+ 1 2) 3))
  (report-result (= (+ 1 2 3) 6) '(= (+ 1 2 3) 6))
  (report-result (= (+ -1 -2) -3) '(= (+ -1 -2) -3)))
; To factor out the repeated contents in result and label, use macro
(defmacro check (form)
  `(report-result ,form ',form))
; Now the 'test' function is simplified further
(defun test+2 ()
  (check (= (+ 1 2) 3))
  (check (= (+ 1 2 3) 6))
  (check (= (+ -1 -2) -3)))
; Well, it seems natural to make check take an arbitrary number of forms
; @note It is a common macro idiom to wrap `progn` around a series of forms in
; order to turn them into a single form. Notice also how `,@` can be used to
; splice in the result of an expression that returns a list of expressions that
; are themselves generated with a backquote template.
(defmacro check1 (&body forms)
  `(progn
    ,@(loop for form in forms collect `(report-result ,form ',form))))
; And finally, the 'test+' function is as simple as follows:
(defun test+-final ()
  (check1
    (= (+ 1 2) 3)
    (= (+ 1 2 3) 6)
    (= (+ -1 -2) -3)))

error: pathspec 'unit-testing-framework.cl' did not match any file(s) known to git


Added files via upload

Added files via upload

;; author *this* => ("Dylan Ball" "Arathnim@gmail.com")
(proclaim '(optimize (speed 0) (safety 3) (debug 3) (space 0)))
(declaim #+sbcl(sb-ext:muffle-conditions style-warning))
(setf *print-case* :downcase)
(with-open-file
   (*standard-output* "/dev/null" :direction :output :if-exists :supersede)
      (ql:quickload '(alexandria iterate anaphora)))
(defpackage parmesan
   (:use cl alexandria iterate anaphora)
   (:export :parse-stack :match-stack :get-str :get-ind :ind :str-match :apply-pred :merge-str
      :parse :bounded-area :build-pointer :pretty-error :call-with-context :return-with-context
      :defparser :cat :fail :pass :seq :choice :one-of :none-of :many :sep-many :many+ :sep-many+
      :sep-num :num :par :ret :str :skip :consume-byte :restr :option :between :any-char
      :sym :letter :digit :hex-digit :whitespace))
(in-package parmesan)

(defvar parse-stack nil) ;; (str)
(defvar match-stack nil) ;; (new-ind success)/test-ind

(defun get-str ()
   (if parse-stack
       (first parse-stack)
       (error "Tried to access the parse-stack without any frames left")))

(defun get-ind ()
   (if match-stack
       (first match-stack)
       (error "Tried to access the match-stack without any frames left")))

(defun ind ()
   (let ((v (first match-stack)))
        (if (listp v) (first v) v)))

;; primitive parser for basic string matching
(defun str-match (src test ind)
   (if (>= (length src) (+ ind (length test)))
       (iter (for x from ind to (+ ind (length test)))
             (for y in-vector test)
             (if (not (equal (aref src x) y))
                 (leave nil))
             (finally (return t)))
       nil))

(defun apply-pred (pred list)
   (iter (for x in list)
         (if (not (funcall pred x)) (leave))
         (finally (return t))))

(defun merge-str (list)
   (if (apply-pred #'stringp list)
       (format nil "~{~a~}" list)
       list))

(defmacro parse (string exp)
  `(progn (push ,string parse-stack)
          (push 0 match-stack)
          (let ((res ,exp))
               (pop parse-stack)
               (print (list 'return-value (pop match-stack)))
               (pop match-stack)
               res)))

(defun bounded-area (str ind range)
   (let ((a (- ind range)) (b (+ ind range)) (len (length str)))
        (when (< a 0) (incf ind (- 0 a)) (setf a 0))
        (when (> b len) (setf b len))
        (list (subseq str a b) (- ind a))))

(defun build-pointer (ind)
   (format nil "~{~a~}^" (iter (repeat ind) (collect " "))))

(defun pretty-error (str)
   (let ((area (bounded-area (get-str) (ind) 12)))
        (error "~%~a~%~a~%~a" str (first area) (build-pointer (second area)))))

(defmacro call-with-context (exp context)
   (if (stringp exp) (setf exp `(str ,exp)))
   (if (symbolp exp) (setf exp `(,exp)))
   (if (numberp exp) (setf exp `(str (string (code-char ,exp)))))
   (if (characterp exp) (setf exp `(string ,exp)))
   (with-gensyms (res con)
    `(progn (push ,context match-stack)
            (let ((,res ,exp) (,con (pop match-stack)))
                 (pop match-stack)
                 (list ,res ,con)))))

(defun make-case-clauses (block-name test-var list)
   (iter (for x in list)
         (if (eq (car x) 'otherwise)
             (collect
               `(return-from ,block-name (progn ,@(cdr x))))
             (collect
               `(if (equalp ,(car x) ,test-var)
                    (return-from ,block-name (progn ,@(cdr x))))))))

(defmacro case! (exp &rest forms)
   (with-gensyms (v b)
     `(let ((,v ,exp))
         (block ,b ,v ,@(make-case-clauses b v forms)))))

(defmacro return-with-context (ret context)
  `(progn (push ,context match-stack)
          ,ret))

(defmacro defparser (name &rest forms)
   `(defun ,name () ,@forms))

(defun cat (list)
   (format nil "~{~a~}" list))

(defmacro fail ()
   `(return-with-context nil (list (get-ind) nil)))

(defmacro pass (form)
   `(return-with-context (first ,form) (second ,form)))

;; choice ~ in the matching operation, returns the first form that consumes input
;; many   ~ matches zero or more of the next form
;; many+  ~ matches one or more of the next form
;; one-of ~ matches one character from the given string
;; skip   ~ consumes input, doesn't return the value
;; count  ~ parses x occurances of y
;; option ~ tries x, if it fails, parse y
;; seq    ~ matches each form sequentially
;; str    ~ explicitly matches a string

(defmacro seq (form &rest aux)
   (if (first aux)
      (with-gensyms (ind res inner)
         `(let* ((,ind (get-ind)) (,res (call-with-context ,form ,ind)))
            (if (second (second ,res))
                (let ((,inner (call-with-context (seq ,(car aux) ,@(cdr aux)) (first (second ,res)))))
                   (if (second (second ,inner))
                       (return-with-context
                           (if (first ,inner)
                               (if (first ,res)
                                   (append (list (first ,res)) (first ,inner))
                                   (first ,inner))
                               (if (first ,res)
                                   (list (first ,res))
                                   nil))
                           (second ,inner))
                       (fail)))
                (fail))))
      (with-gensyms (str ind res)
         `(let* ((,ind (get-ind)) (,res (call-with-context ,form ,ind)))
            (if (second (second ,res))
                (return-with-context (if (first ,res) (list (first ,res))) (list (first (second ,res)) t))
                (fail))))))

(defmacro choice (form &rest aux)
   (if (first aux)
       (with-gensyms (ind res inner)
         `(let* ((,ind (get-ind)) (,res (call-with-context ,form ,ind)))
            (if (second (second ,res))
                (return-with-context (first ,res) (second ,res))
                (let ((,inner (call-with-context (choice ,(car aux) ,@(cdr aux)) (first (second ,res)))))
                   (if (second (second ,inner))
                       (return-with-context (first ,inner) (second ,inner))
                       (return-with-context nil (list ,ind nil)))))))
       (with-gensyms (str ind res)
         `(let* ((,ind (get-ind)) (,res (call-with-context ,form ,ind)))
            (if (second (second ,res))
                (return-with-context (if (first ,res) (first ,res)) (list (first (second ,res)) t))
                (return-with-context nil (list ,ind nil)))))))

(defmacro one-of (form)
   (with-gensyms (src ind i str)
      `(let ((,str ,form) (,ind (get-ind)) (,src (get-str)))
         (if (< ,ind (length ,src))
             (iter (for ,i in-string ,str)
                   (if (equalp ,i (aref ,src ,ind))
                       (leave (return-with-context (string ,i) (list (+ ,ind 1) t))))
                   (finally (return (return-with-context nil (list ,ind nil)))))
             (return-with-context nil (list ,ind nil))))))

(defmacro none-of (form)
   (with-gensyms (src ind i str)
      `(let ((,str ,form) (,ind (get-ind)) (,src (get-str)))
         (if (< ,ind (length ,src))
             (iter (for ,i in-string ,str)
                   (if (equalp ,i (aref ,src ,ind))
                       (leave (return-with-context nil (list ,ind nil))))
                   (finally (return (return-with-context (string (aref ,src ,ind)) (list (+ ,ind 1) t)))))
             (return-with-context nil (list ,ind nil))))))

(defmacro many (form)
  `(aif (sep-many ,form) (merge-str it)))

(defmacro sep-many (form)
   (with-gensyms (src ind v i s acc z)
      `(let ((,ind (get-ind)) (,src (get-str)))
         (iter (with ,z = ,ind)
               (for (,v (,i ,s)) = (call-with-context ,form ,z))
               (if ,v (collect ,v into ,acc))
               (if (not ,s)
                   (leave (return-with-context (if ,acc ,acc) (list ,z t)))
                   (setf ,z ,i))))))

(defmacro many+ (form)
  `(aif (sep-many+ ,form) (merge-str it)))

(defmacro sep-many+ (form)
   (with-gensyms (src ind v i s acc z c)
      `(let ((,ind (get-ind)) (,src (get-str)))
         (iter (with ,z = ,ind)
               (with ,c = 0)
               (for (,v (,i ,s)) = (call-with-context ,form ,z))
               (incf ,c)
               (if ,v (collect ,v into ,acc))
               (if (not ,s)
                   (if (> ,c 1)
                       (leave (return-with-context ,acc (list ,z t)))
                       (leave (return-with-context nil (list ,ind nil))))
                   (setf ,z ,i))))))

(defmacro sep-num (n form)
   (with-gensyms (src ind v i s acc z c)
      `(let ((,ind (get-ind)) (,src (get-str)))
         (iter (with ,z = ,ind)
               (with ,c = 0)
               (for (,v (,i ,s)) = (call-with-context ,form ,z))
               (incf ,c)
               (if ,v (collect ,v into ,acc))
               (if (not ,s)
                   (leave (fail))
                   (setf ,z ,i))
               (if (eql ,c ,n)
                   (leave (return-with-context (if ,acc ,acc) (list ,z t))))))))

(defmacro num (n form)
   `(aif (sep-num ,n ,form) (merge-str it)))

(defmacro par (form)
   (with-gensyms (res)
       `(let ((,res (call-with-context ,form (get-ind))))
         (if (second (second ,res))
             (return-with-context (first ,res) (second ,res))
             (return-with-context nil (list (get-ind) nil))))))

(defmacro ret (form)
   (with-gensyms (res ing)
       `(let ((,ing (pop match-stack)) (,res (call-with-context ,form (get-ind))))
         (if (second (second ,res))
             (return-with-context (first ,res) (second ,res))
             (return-with-context nil (list (get-ind) nil))))))

(defmacro str (form)
   (with-gensyms (str src ind)
       `(let ((,str ,form) (,ind (get-ind)) (,src (get-str)))
         (if (str-match ,src ,str ,ind)
             (return-with-context ,str (list (+ (length ,str) ,ind) t))
             (return-with-context nil (list ,ind nil))))))

(defmacro skip (form)
   (with-gensyms (res)
     `(let ((,res (call-with-context ,form (get-ind))))
           (if (second (second ,res))
               (return-with-context nil (second ,res))
               (return-with-context nil (list (get-ind) nil))))))

(defmacro consume-byte ()
   (with-gensyms (str ind)
       `(let ((,str (get-str)) (,ind (get-ind)))
         (if (< ,ind (length ,str))
             (return-with-context (string (aref ,str ,ind)) (list (+ 1 ,ind) t))
             (return-with-context nil (list ,ind nil))))))

(defmacro restr (form)
   (with-gensyms (res ind)
    `(let* ((,ind (get-ind)) (,res (call-with-context ,form ,ind)))
           (if (second (second ,res))
               (return-with-context (subseq (get-str) ,ind (first (second ,res))) (second ,res))
               (return-with-context nil (list ,ind nil))))))

(defmacro option (form aux)
   (with-gensyms (res ind)
    `(let* ((,ind (get-ind)) (,res (call-with-context ,form ,ind)))
           (if (second (second ,res))
               (return-with-context nil (list ,ind nil))
               (par ,aux)))))

(defmacro between (before form after)
   `(car (seq (skip ,before) ,form (skip ,after))))

(defparser any-char (consume-byte))
(defparser sym (one-of "~!@#$%^&*-_=+<>,./\\"))
(defparser letter (one-of "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"))
(defparser digit (one-of "0123456789"))
(defparser hex-digit (one-of "0123456789abcdef"))
(defparser whitespace (one-of (coerce '(#\Space #\Tab #\Newline) 'string)))

error: pathspec 'parmesan.cl' did not match any file(s) known to git