context: namespace OpenCl {
      using System.Collections.Generic;
      using System.Linq.Expressions;
      using System.Linq;
      using System.Runtime.InteropServices;
      using System.Text;
      using System;
  
      public delegate void ProgramNotify (Program program, object userData);
  
      internal delegate void ProgramNotifyInternal (IntPtr program, IntPtr userData);
  
      internal class ProgramNotifyData {
          private ProgramNotify callback;
          private object data;
          private GCHandle handle;
          public ProgramNotifyData (ProgramNotify callback, object data) {
              this.callback = callback;
              this.data = data;
              this.handle = GCHandle.Alloc (this);
          }
          internal GCHandle Handle {
              get {
                  if (!this.handle.IsAllocated) {
                      throw new InvalidOperationException ();
                  }
                  return this.handle;
              }
          }
          public static void Callback (IntPtr program, IntPtr userData) {
              var h = GCHandle.FromIntPtr (userData);
              var d = h.Target as ProgramNotifyData;
              d.callback (new Program (program), d.data);
              h.Free ();
          }
      }
  
      public enum BuildStatus : int {
          Success = 0,
          None = -1,
          Error = -2,
          InProgress = -3,
      }
  
      public enum BinaryType : uint {
          None = 0x0,
          CompiledObject = 0x1,
          Library = 0x2,
          Executable = 0x4,
      }
  
      public sealed class BuildInfo {
          private const uint CL_PROGRAM_BUILD_STATUS = 0x1181;
          private const uint CL_PROGRAM_BUILD_OPTIONS = 0x1182;
          private const uint CL_PROGRAM_BUILD_LOG = 0x1183;
          private const uint CL_PROGRAM_BINARY_TYPE = 0x1184;
          private const uint CL_PROGRAM_BUILD_GLOBAL_VARIABLE_TOTAL_SIZE = 0x1185;
  
          private readonly Program owner;
  
          internal BuildInfo (Program owner) {
              this.owner = owner;
          }
  
          public BuildStatus GetStatus (Device device) {
              return Cl.GetBuildInfoEnum<BuildStatus> (NativeMethods.clGetProgramBuildInfo, this.owner.handle, device.handle, CL_PROGRAM_BUILD_STATUS);
          }
  
          public string GetOptions (Device device) {
              return Cl.GetBuildInfoString (NativeMethods.clGetProgramBuildInfo, this.owner.handle, device.handle, CL_PROGRAM_BUILD_OPTIONS);
          }
  
          public string GetLog (Device device) {
              return Cl.GetBuildInfoString (NativeMethods.clGetProgramBuildInfo, this.owner.handle, device.handle, CL_PROGRAM_BUILD_LOG);
          }
  
          public BinaryType GetBinaryType (Device device) {
              return Cl.GetBuildInfoEnum<BinaryType> (NativeMethods.clGetProgramBuildInfo, this.owner.handle, device.handle, CL_PROGRAM_BINARY_TYPE);
          }
      }
  
      public sealed class Program : RefCountedObject {
          private const uint CL_PROGRAM_REFERENCE_COUNT = 0x1160;
          private const uint CL_PROGRAM_CONTEXT = 0x1161;
          private const uint CL_PROGRAM_NUM_DEVICES = 0x1162;
          private const uint CL_PROGRAM_DEVICES = 0x1163;
          private const uint CL_PROGRAM_SOURCE = 0x1164;
          private const uint CL_PROGRAM_BINARY_SIZES = 0x1165;
          private const uint CL_PROGRAM_BINARIES = 0x1166;
          private const uint CL_PROGRAM_NUM_KERNELS = 0x1167;
          private const uint CL_PROGRAM_KERNEL_NAMES = 0x1168;
          private const uint CL_PROGRAM_IL = 0x1169;
  
          internal Program (IntPtr handle) : base (handle) { }
  
          // Program attributes
  
          public uint ReferenceCount {
              get { return Cl.GetInfo<uint> (NativeMethods.clGetProgramInfo, this.handle, CL_PROGRAM_REFERENCE_COUNT); }
          }
  
          public Context Context {
              get {
                  var ctx = Cl.GetInfo<IntPtr> (NativeMethods.clGetProgramInfo, this.handle, CL_PROGRAM_CONTEXT);
                  return new Context (ctx);
              }
          }
  
          public uint NumDevices {
              get { return Cl.GetInfo<uint> (NativeMethods.clGetProgramInfo, this.handle, CL_PROGRAM_NUM_DEVICES); }
          }
  
          public Device[] Devices {
              get { return Device.FromIntPtr (Cl.GetInfoArray<IntPtr> (NativeMethods.clGetProgramInfo, this.handle, CL_PROGRAM_DEVICES)); }
          }
  
          public string Source {
              get { return Cl.GetInfoString (NativeMethods.clGetProgramInfo, this.handle, CL_PROGRAM_SOURCE); }
          }
  
          public IntPtr[] BinarySizes {
              get { return Cl.GetInfoArray<IntPtr> (NativeMethods.clGetProgramInfo, this.handle, CL_PROGRAM_BINARY_SIZES); }
          }
  
          public byte[][] Binaries {
              get { throw new NotImplementedException (); }
          }
  
          // Program build attributes
  
          private BuildInfo bi;
  
          public BuildInfo BuildInfo {
              get {
                  if (this.bi == null) {
                      this.bi = new BuildInfo (this);
                  }
                  return this.bi;
              }
          }
  
          // Program methods
  
          public void BuildProgram (Device device) {
              BuildProgram (device, null, null, null);
          }
  
          public void BuildProgram (Device device, string options) {
              BuildProgram (device, options, null, null);
          }
  
          public void BuildProgram (Device device, string options, ProgramNotify callback, object userData) {
              var dev = Device.ToIntPtr (device);
              var pfn = (ProgramNotifyData) null;
              var pcb = (ProgramNotifyInternal) null;
              var ptr = IntPtr.Zero;
              if (callback != null) {
                  pfn = new ProgramNotifyData (callback, userData);
                  pcb = ProgramNotifyData.Callback;
                  ptr = GCHandle.ToIntPtr (pfn.Handle);
              }
              var err = NativeMethods.clBuildProgram (this.handle, (uint) dev.Length, dev, options, pcb, ptr);
              if (err != ErrorCode.Success) {
                  throw new OpenClException (err);
              }
          }
  
          public void BuildProgram (Device[] deviceList) {
              BuildProgram (deviceList, null, null, null);
          }
  
          public void BuildProgram (Device[] deviceList, string options) {
              BuildProgram (deviceList, options, null, null);
          }
  
          public void BuildProgram (Device[] deviceList, string options, ProgramNotify callback, object userData) {
              var dev = Device.ToIntPtr (deviceList);
              var pfn = (ProgramNotifyData) null;
              var pcb = (ProgramNotifyInternal) null;
              var ptr = IntPtr.Zero;
              if (callback != null) {
                  pfn = new ProgramNotifyData (callback, userData);
                  pcb = ProgramNotifyData.Callback;
                  ptr = GCHandle.ToIntPtr (pfn.Handle);
              }
              var err = NativeMethods.clBuildProgram (this.handle, (uint) dev.Length, dev, options, pcb, ptr);
              if (err != ErrorCode.Success) {
                  throw new OpenClException (err);
              }
          }
  
          // RefCountedObject
  
          protected override void Retain () {
              NativeMethods.clRetainProgram (this.handle);
          }
  
          protected override void Release () {
              NativeMethods.clReleaseProgram (this.handle);
          }
  
          // static factory methods
  
          public static Program CreateProgramWithSource (Context context, string[] sources) {
              ErrorCode error;
  
gt:             IntPtr[] buffers = new IntPtr[sources.Length];