anchor.diff:          let reducer = getMapReducer map add
           reducer.Run(input)
   
  +    member x.MapReduce(map : Expr<V3i -> V4f -> 'b>, add : Expr<'b -> 'b -> 'b>, input : ITextureSubResource) =
  +        let reducer = getImageMapReducer map add
  +        reducer.Run(input)
  +        
  +    member x.Map(map : Expr<int -> 'a -> 'b>, input : IBufferVector<'a>, output : IBufferVector<'b>) =
  +        let mapper = getMapper map
  +        mapper.Run(input, output)
  +
  
positive.code: 
  type ParallelPrimitives(runtime : IComputeRuntime) =
      
      let scanCache = ExpressionCache()
      let reduceCache = ExpressionCache()
      let mapReduceCache = ExpressionCache()
  
      let getScanner (add : Expr<'a -> 'a -> 'a>) = scanCache.GetOrCreate(add, fun add -> new Scan<'a>(runtime, add))
      let getReducer (add : Expr<'a -> 'a -> 'a>) = reduceCache.GetOrCreate(add, fun add -> new Reduce<'a>(runtime, add))
      let getMapReducer (map : Expr<int -> 'a -> 'b>) (add : Expr<'b -> 'b -> 'b>) =
  
negative.code: 
          }
  
        
      let inputImage2d =
          sampler2d {
              texture uniform?InputTexture
              filter Filter.MinMagMipPoint
              addressU WrapMode.Clamp
              addressV WrapMode.Clamp
          }
  
      [<LocalSize(X = 8, Y = 8)>]
      let mapReduceImageKernel2d (map : Expr<V3i -> V4f -> 'b>) (add : Expr<'b -> 'b -> 'b>) (numGroups : V2i) (inputSize : V2i) (inputLevel : int) (outputData : 'b[]) =
          compute {
              let s = inputSize
              let ggc = numGroups
              let lc = getLocalId().XY
              let gc = getWorkGroupId().XY
              let tid = lc.Y * 8 + lc.X
              
              let group = gc.Y * ggc.X + gc.X
  
              let ai = gc * V2i(16,8) + lc * V2i(2,1)
              let bi = ai + V2i(1,0)
              let lai = lc.Y * 16 + lc.X * 2
              let lbi = lai + 1
              
              let mem : 'b[] = allocateShared 128
  
              if ai.X < s.X && ai.Y < s.Y then mem.[lai] <- (%map) (V3i(ai, 0)) (V4f inputImage2d.[ai, inputLevel])
              else mem.[lai] <- (%map) (V3i(ai, 0)) V4f.Zero
  
              if bi.X < s.X && bi.Y < s.Y then mem.[lbi] <- (%map) (V3i(bi, 0)) (V4f inputImage2d.[bi, inputLevel])
              else mem.[lbi] <- (%map) (V3i(bi, 0)) V4f.Zero
  
              
              barrier()
              let mutable s = 64
              while s > 0 do
                  if tid < s then
                      mem.[tid] <- (%add) mem.[tid] mem.[tid + s]
  
                  s <- s >>> 1
                  barrier()
  
              if tid = 0 then
                  outputData.[group] <- mem.[0]
  
          }
  
  
        
      let inputImage3d =
          sampler3d {
              texture uniform?InputTexture
              filter Filter.MinMagMipPoint
              addressU WrapMode.Clamp
              addressV WrapMode.Clamp
              addressW WrapMode.Clamp
          }
  
      [<LocalSize(X = 4, Y = 4, Z = 2)>]
      let mapReduceImageKernel3d (map : Expr<V3i -> V4f -> 'b>) (add : Expr<'b -> 'b -> 'b>) (numGroups : V3i) (inputSize : V3i) (inputLevel : int) (outputData : 'b[]) =
          compute {
              let s = inputSize
              let ggc = numGroups
              let lc = getLocalId()
              let gc = getWorkGroupId()
              let tid = lc.Z * 16 + lc.Y * 4 + lc.X
              
              let group = gc.Z * (ggc.X * ggc.Y) + gc.Y * ggc.X + gc.X
  
              let ai = gc * V3i(8,4,2) + lc * V3i(2,1,1)
              let bi = ai + V3i(1,0,0)
              let lai = lc.Z * 32 + lc.Y * 8 + lc.X * 2
              let lbi = lai + 1
              
              let mem : 'b[] = allocateShared 128
  
              if ai.X < s.X && ai.Y < s.Y then mem.[lai] <- (%map) ai (V4f inputImage3d.[ai, inputLevel])
              else mem.[lai] <- (%map) ai V4f.Zero
  
              if bi.X < s.X && bi.Y < s.Y then mem.[lbi] <- (%map) bi (V4f inputImage3d.[bi, inputLevel])
              else mem.[lbi] <- (%map) bi V4f.Zero
  
              
              barrier()
              let mutable s = 64
              while s > 0 do
                  if tid < s then
                      mem.[tid] <- (%add) mem.[tid] mem.[tid + s]
  
                  s <- s >>> 1
                  barrier()
  
              if tid = 0 then
                  outputData.[group] <- mem.[0]
  
          }
  
  
      [<LocalSize(X = 64)>]
      let map (map : Expr<int -> 'a -> 'b>) (src : 'a[]) (dst : 'b[]) =
          compute {
              let id = getGlobalId().X
              let srcOffset : int = uniform?SrcOffset
              let srcDelta : int = uniform?SrcDelta
              let srcCnt : int = uniform?SrcCount
              
              let dstOffset : int = uniform?DstOffset
              let dstDelta : int = uniform?DstDelta
  
              if id < srcCnt then
                  dst.[dstOffset + id * dstDelta] <- (%map) id src.[srcOffset + id * srcDelta]
          }
  
  
  type private Map<'a, 'b when 'a : unmanaged and 'b : unmanaged>(runtime : IComputeRuntime, map : Expr<int -> 'a -> 'b>) =
      
      static let ceilDiv (v : int) (d : int) =
          if v % d = 0 then v / d
          else 1 + v / d
  
      let map    = runtime.CreateComputeShader (Kernels.map map)
  
      let build (src : IBufferVector<'a>) (dst : IBufferVector<'b>) =
          let args = runtime.NewInputBinding(map)
          
          args.["src"] <- src.Buffer
          args.["SrcOffset"] <- src.Origin
          args.["SrcDelta"] <- src.Delta
          args.["SrcCount"] <- src.Count
          
          args.["dst"] <- dst.Buffer
          args.["DstOffset"] <- dst.Origin
          args.["DstDelta"] <- dst.Delta
          args.Flush()
          
          args, [
              ComputeCommand.Bind map
              ComputeCommand.SetInput args
              ComputeCommand.Dispatch(ceilDiv (int src.Count) 64)
              ComputeCommand.Sync(dst.Buffer)
          ]
  
  
  
      member x.Compile(input : IBufferVector<'a>, output : IBufferVector<'b>) =
          let args, cmd = build input output
          
          let prog = runtime.Compile cmd
          { new ComputeProgram<unit>() with
              member x.Dispose() =
                  prog.Dispose()
                  args.Dispose()
              member x.RunUnit() =
                  prog.Run()
          }
  
      member x.Run(input : IBufferVector<'a>, output : IBufferVector<'b>) =
          let args, cmd = build input output
          runtime.Run(cmd)
          args.Dispose()
  
          
  
  type private Scan<'a when 'a : unmanaged>(runtime : IComputeRuntime, add : Expr<'a -> 'a -> 'a>) =
  
  