1 /*
  2  * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
  3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4  *
  5  * This code is free software; you can redistribute it and/or modify it
  6  * under the terms of the GNU General Public License version 2 only, as
  7  * published by the Free Software Foundation.  Oracle designates this
  8  * particular file as subject to the "Classpath" exception as provided
  9  * by Oracle in the LICENSE file that accompanied this code.
 10  *
 11  * This code is distributed in the hope that it will be useful, but WITHOUT
 12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 14  * version 2 for more details (a copy is included in the LICENSE file that
 15  * accompanied this code).
 16  *
 17  * You should have received a copy of the GNU General Public License version
 18  * 2 along with this work; if not, write to the Free Software Foundation,
 19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 20  *
 21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 22  * or visit www.oracle.com if you need additional information or have any
 23  * questions.
 24  */
 25 package hat.test;
 26 
 27 import hat.Accelerator;
 28 import hat.ComputeContext;
 29 import hat.KernelContext;
 30 import hat.backend.Backend;
 31 import hat.buffer.*;
 32 import hat.ifacemapper.MappableIface.*;
 33 import hat.ifacemapper.Schema;
 34 import jdk.incubator.code.CodeReflection;
 35 import hat.test.annotation.HatTest;
 36 import hat.test.engine.HatAsserts;
 37 
 38 import java.lang.foreign.ValueLayout;
 39 import java.lang.invoke.MethodHandles;
 40 import java.util.Random;
 41 
 42 import static java.lang.foreign.ValueLayout.JAVA_BYTE;
 43 
 44 public class TestArrayView {
 45 
 46     // simple square kernel example using S32Array's ArrayView
 47     @CodeReflection
 48     public static void squareKernel(@RO  KernelContext kc, @RW S32Array s32Array) {
 49         if (kc.x<kc.maxX){
 50             int[] arr = s32Array.arrayView();
 51             arr[kc.x] *= arr[kc.x];
 52         }
 53     }
 54 
 55     @CodeReflection
 56     public static void square(@RO ComputeContext cc, @RW S32Array s32Array) {
 57         cc.dispatchKernel(s32Array.length(),
 58                 kc -> squareKernel(kc, s32Array)
 59         );
 60     }
 61 
 62     @HatTest
 63     public static void testSquare() {
 64 
 65         var accelerator = new Accelerator(MethodHandles.lookup(), Backend.FIRST);//new JavaMultiThreadedBackend());
 66         var arr = S32Array.create(accelerator, 32);
 67         for (int i = 0; i < arr.length(); i++) {
 68             arr.array(i, i);
 69         }
 70         accelerator.compute(
 71                 cc -> square(cc, arr)  //QuotableComputeContextConsumer
 72         );                                     //   extends Quotable, Consumer<ComputeContext>
 73         for (int i = 0; i < arr.length(); i++) {
 74             HatAsserts.assertEquals(i * i, arr.array(i));
 75         }
 76     }
 77 
 78     @CodeReflection
 79     public static void square2DKernel(@RO  KernelContext kc, @RW S32Array2D s32Array2D) {
 80         if (kc.x<kc.maxX){
 81             int[][] arr = s32Array2D.arrayView();
 82             arr[kc.x][kc.y] *= arr[kc.x][kc.y];
 83         }
 84     }
 85 
 86     @CodeReflection
 87     public static void square2D(@RO ComputeContext cc, @RW S32Array2D s32Array2D) {
 88         cc.dispatchKernel(s32Array2D.width() * s32Array2D.height(),
 89                 kc -> square2DKernel(kc, s32Array2D)
 90         );
 91     }
 92 
 93     @HatTest
 94     public static void testSquare2D() {
 95 
 96         var accelerator = new Accelerator(MethodHandles.lookup(), Backend.FIRST);//new JavaMultiThreadedBackend());
 97         var arr = S32Array2D.create(accelerator, 5, 5);
 98         for (int i = 0; i < arr.height(); i++) {
 99             for (int j = 0; j < arr.width(); j++) {
100                 arr.set(i, j, i * 5 + j);
101             }
102         }
103         accelerator.compute(
104                 cc -> square2D(cc, arr)  //QuotableComputeContextConsumer
105         );                                     //   extends Quotable, Consumer<ComputeContext>
106         for (int i = 0; i < arr.height(); i++) {
107             for (int j = 0; j < arr.width(); j++) {
108                 HatAsserts.assertEquals((i * 5 + j) * (i * 5 + j), arr.get(i, j));
109             }
110         }
111     }
112 
113     // simplified version of Game of Life using ArrayView
114     public final static byte ALIVE = (byte) 0xff;
115     public final static byte DEAD = 0x00;
116 
117     public interface CellGrid extends Buffer {
118         /*
119          * struct CellGrid{
120          *     int width;
121          *     int height;
122          *     byte[width*height*2] cellArray;
123          *  }
124          */
125         int width();
126 
127         int height();
128 
129         byte array(long idx);
130 
131         void array(long idx, byte b);
132 
133         Schema<CellGrid> schema = Schema.of(CellGrid.class, lifeData -> lifeData
134                 .arrayLen("width", "height").stride(2).array("array")
135         );
136 
137         static CellGrid create(Accelerator accelerator, int width, int height) {
138             return schema.allocate(accelerator, width, height);
139         }
140 
141         ValueLayout valueLayout = JAVA_BYTE;
142 
143         default byte[] arrayView() {
144             int size = this.width() * this.height();
145             byte[] arr = new byte[size];
146             for (int i = 0; i < size; i++) {
147                 arr[i] = this.array(i);
148             }
149             return arr;
150         }
151     }
152 
153     public interface Control extends Buffer {
154         /*
155          * struct Control{
156          *     int from;
157          *     int to;
158          *  }
159          */
160         int from();
161 
162         void from(int from);
163 
164         int to();
165 
166         void to(int to);
167 
168         Schema<Control> schema = Schema.of(
169                 Control.class, control ->
170                         control.fields("from", "to"));//, "generation", "requiredFrameRate", "maxGenerations"));
171 
172         static Control create(Accelerator accelerator, CellGrid cellGrid) {
173             var instance = schema.allocate(accelerator);
174             instance.from(cellGrid.width() * cellGrid.height());
175             instance.to(0);
176             return instance;
177         }
178     }
179 
180     public static class Compute {
181         @CodeReflection
182         public static void lifePerIdx(int idx, @RO Control control, @RW CellGrid cellGrid) {
183             int w = cellGrid.width();
184             int h = cellGrid.height();
185             int from = control.from();
186             int to = control.to();
187             int x = idx % w;
188             int y = idx / w;
189 
190             // byte[] bytes = cellGrid.arrayView();
191             // byte cell = bytes[idx + from];
192             // byte[] lookup = new byte[]{};
193             // if (x > 0 && x < (w - 1) && y > 0 && y < (h - 1)) { // passports please
194             //     int lookupIdx =
195             //             (bytes[(y - 1) * w + x - 1 + from]&1 <<0)
196             //                     |(bytes[(y + 0) * w + x - 1 + from]&1 <<1)
197             //                     |(bytes[(y + 1) * w + x - 1 + from]&1 <<2)
198             //                     |(bytes[(y - 1) * w + x + 0 + from]&1 <<3)
199             //                     |(bytes[(y - 0) * w + x + 0 + from]&1 <<4) // current cell added
200             //                     |(bytes[(y + 1) * w + x + 0 + from]&1 <<5)
201             //                     |(bytes[(y + 0) * w + x + 1 + from]&1 <<6)
202             //                     |(bytes[(y - 1) * w + x + 1 + from]&1 <<7)
203             //                     |(bytes[(y + 1) * w + x + 1 + from]&1 <<8) ;
204             //     // conditional removed!
205             //     bytes[idx + to] = lookup[lookupIdx];
206             // }
207 
208             byte[] bytes = cellGrid.arrayView();
209             byte cell = bytes[idx];
210             if (x > 0 && x < (w - 1) && y > 0 && y < (h - 1)) { // passports please
211                 int count =
212                         (bytes[(y - 1) * w + (x - 1)] & 1)
213                                 + (bytes[(y + 0) * w + (x - 1)] & 1)
214                                 + (bytes[(y + 1) * w + (x - 1)] & 1)
215                                 + (bytes[(y - 1) * w + (x + 0)] & 1)
216                                 + (bytes[(y + 1) * w + (x + 0)] & 1)
217                                 + (bytes[(y - 1) * w + (x + 1)] & 1)
218                                 + (bytes[(y + 0) * w + (x + 1)] & 1)
219                                 + (bytes[(y + 1) * w + (x + 1)] & 1);
220                 cell = ((count == 3) || ((count == 2) && (cell == ALIVE))) ? ALIVE : DEAD;// B3/S23.
221             }
222             bytes[idx] = cell;
223         }
224 
225         @CodeReflection
226         public static void life(@RO KernelContext kc, @RO Control control, @RW CellGrid cellGrid) {
227             if (kc.x < kc.maxX) {
228                 Compute.lifePerIdx(kc.x, control, cellGrid);
229             }
230         }
231 
232         @CodeReflection
233         static public void compute(final @RO ComputeContext cc, @RO Control ctrl, @RW CellGrid grid) {
234             int range = grid.width() * grid.height();
235             cc.dispatchKernel(range, kc -> Compute.life(kc, ctrl, grid));
236         }
237     }
238 
239     @HatTest
240     public static void testLife() {
241         Accelerator accelerator = new Accelerator(MethodHandles.lookup());//,new OpenCLBackend("INFO,MINIMIZE_COPIES,SHOW_COMPUTE_MODEL"));
242 
243         // We oversize the grid by adding 1 to n,e,w and s
244         CellGrid cellGrid = CellGrid.create(accelerator,
245                 17,
246                 17);
247 
248         byte[][] actualGrid = new byte[][]{
249                 {DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD},
250                 {DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD},
251                 {DEAD,  DEAD,  DEAD,  DEAD,  ALIVE, ALIVE, ALIVE, DEAD,  DEAD,  DEAD,  ALIVE, ALIVE, ALIVE, DEAD,  DEAD,  DEAD,  DEAD},
252                 {DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD},
253                 {DEAD,  DEAD,  ALIVE, DEAD,  DEAD,  DEAD,  DEAD,  ALIVE, DEAD,  ALIVE, DEAD,  DEAD,  DEAD,  DEAD,  ALIVE, DEAD,  DEAD},
254                 {DEAD,  DEAD,  ALIVE, DEAD,  DEAD,  DEAD,  DEAD,  ALIVE, DEAD,  ALIVE, DEAD,  DEAD,  DEAD,  DEAD,  ALIVE, DEAD,  DEAD},
255                 {DEAD,  DEAD,  ALIVE, DEAD,  DEAD,  DEAD,  DEAD,  ALIVE, DEAD,  ALIVE, DEAD,  DEAD,  DEAD,  DEAD,  ALIVE, DEAD,  DEAD},
256                 {DEAD,  DEAD,  DEAD,  DEAD,  ALIVE, ALIVE, ALIVE, DEAD,  DEAD,  DEAD,  ALIVE, ALIVE, ALIVE, DEAD,  DEAD,  DEAD,  DEAD},
257                 {DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD},
258                 {DEAD,  DEAD,  DEAD,  DEAD,  ALIVE, ALIVE, ALIVE, DEAD,  DEAD,  DEAD,  ALIVE, ALIVE, ALIVE, DEAD,  DEAD,  DEAD,  DEAD},
259                 {DEAD,  DEAD,  ALIVE, DEAD,  DEAD,  DEAD,  DEAD,  ALIVE, DEAD,  ALIVE, DEAD,  DEAD,  DEAD,  DEAD,  ALIVE, DEAD,  DEAD},
260                 {DEAD,  DEAD,  ALIVE, DEAD,  DEAD,  DEAD,  DEAD,  ALIVE, DEAD,  ALIVE, DEAD,  DEAD,  DEAD,  DEAD,  ALIVE, DEAD,  DEAD},
261                 {DEAD,  DEAD,  ALIVE, DEAD,  DEAD,  DEAD,  DEAD,  ALIVE, DEAD,  ALIVE, DEAD,  DEAD,  DEAD,  DEAD,  ALIVE, DEAD,  DEAD},
262                 {DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD},
263                 {DEAD,  DEAD,  DEAD,  DEAD,  ALIVE, ALIVE, ALIVE, DEAD,  DEAD,  DEAD,  ALIVE, ALIVE, ALIVE, DEAD,  DEAD,  DEAD,  DEAD},
264                 {DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD},
265                 {DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD},
266         };
267 
268         // By shifting all cells +1,+1 so we only need to scan 1..width-1, 1..height-1
269         // we don't worry about possibly finding cells in 0,n width,n or n,0 height,n
270         for (int i = 0; i < cellGrid.height(); i++) {
271             for (int j = 0; j < cellGrid.width(); j++) {
272                 cellGrid.array(((long) i * cellGrid.width()) + j, actualGrid[i][j]);
273             }
274         }
275 
276         Control control = Control.create(accelerator, cellGrid);
277 
278         accelerator.compute(cc -> Compute.compute(cc, control, cellGrid));
279 
280         byte[][] resultGrid = new byte[][]{
281                 {DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD},
282                 {DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  ALIVE, DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  ALIVE, DEAD,  DEAD,  DEAD,  DEAD,  DEAD},
283                 {DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  ALIVE, DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  ALIVE, DEAD,  DEAD,  DEAD,  DEAD,  DEAD},
284                 {DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  ALIVE, ALIVE, DEAD,  DEAD,  DEAD,  ALIVE, ALIVE, DEAD,  DEAD,  DEAD,  DEAD,  DEAD},
285                 {DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD},
286                 {DEAD,  ALIVE, ALIVE, ALIVE, DEAD,  DEAD,  ALIVE, ALIVE, DEAD,  ALIVE, ALIVE, DEAD,  DEAD,  ALIVE, ALIVE, ALIVE, DEAD},
287                 {DEAD,  DEAD,  DEAD,  ALIVE, DEAD,  ALIVE, DEAD,  ALIVE, DEAD,  ALIVE, DEAD,  ALIVE, DEAD,  ALIVE, DEAD,  DEAD,  DEAD},
288                 {DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  ALIVE, ALIVE, DEAD,  DEAD,  DEAD,  ALIVE, ALIVE, DEAD,  DEAD,  DEAD,  DEAD,  DEAD},
289                 {DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD},
290                 {DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  ALIVE, ALIVE, DEAD,  DEAD,  DEAD,  ALIVE, ALIVE, DEAD,  DEAD,  DEAD,  DEAD,  DEAD},
291                 {DEAD,  DEAD,  DEAD,  ALIVE, DEAD,  ALIVE, DEAD,  ALIVE, DEAD,  ALIVE, DEAD,  ALIVE, DEAD,  ALIVE, DEAD,  DEAD,  DEAD},
292                 {DEAD,  ALIVE, ALIVE, ALIVE, DEAD,  DEAD,  ALIVE, ALIVE, DEAD,  ALIVE, ALIVE, DEAD,  DEAD,  ALIVE, ALIVE, ALIVE, DEAD},
293                 {DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD},
294                 {DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  ALIVE, ALIVE, DEAD,  DEAD,  DEAD,  ALIVE, ALIVE, DEAD,  DEAD,  DEAD,  DEAD,  DEAD},
295                 {DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  ALIVE, DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  ALIVE, DEAD,  DEAD,  DEAD,  DEAD,  DEAD},
296                 {DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  ALIVE, DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  ALIVE, DEAD,  DEAD,  DEAD,  DEAD,  DEAD},
297                 {DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD,  DEAD},
298         };
299 
300         for (int i = 0; i < cellGrid.height(); i++) {
301             for (int j = 0; j < cellGrid.width(); j++) {
302                 HatAsserts.assertEquals(resultGrid[i][j], cellGrid.array(((long) i * cellGrid.width()) + j));
303             }
304         }
305     }
306 
307     // simplified version of mandel using ArrayView
308     @CodeReflection
309     public static int mandelCheck(int i, int j, float width, float height, int[] pallette, float offsetx, float offsety, float scale) {
310         float x = (i * scale - (scale / 2f * width)) / width + offsetx;
311         float y = (j * scale - (scale / 2f * height)) / height + offsety;
312         float zx = x;
313         float zy = y;
314         float new_zx;
315         int colorIdx = 0;
316         while ((colorIdx < pallette.length) && (((zx * zx) + (zy * zy)) < 4f)) {
317             new_zx = ((zx * zx) - (zy * zy)) + x;
318             zy = (2f * zx * zy) + y;
319             zx = new_zx;
320             colorIdx++;
321         }
322         return colorIdx < pallette.length ? pallette[colorIdx] : 0;
323     }
324 
325     @CodeReflection
326     public static void mandel(@RO KernelContext kc, @RW S32Array2D s32Array2D, @RO S32Array pallette, float offsetx, float offsety, float scale) {
327         if (kc.x < kc.maxX) {
328             int[] pal = pallette.arrayView();
329             int[][] s32 = s32Array2D.arrayView();
330             float width = s32Array2D.width();
331             float height = s32Array2D.height();
332             float x = ((kc.x % s32Array2D.width()) * scale - (scale / 2f * width)) / width + offsetx;
333             float y = ((kc.x / s32Array2D.width()) * scale - (scale / 2f * height)) / height + offsety;
334             float zx = x;
335             float zy = y;
336             float new_zx;
337             int colorIdx = 0;
338             while ((colorIdx < pal.length) && (((zx * zx) + (zy * zy)) < 4f)) {
339                 new_zx = ((zx * zx) - (zy * zy)) + x;
340                 zy = (2f * zx * zy) + y;
341                 zx = new_zx;
342                 colorIdx++;
343             }
344             int color = colorIdx < pal.length ? pal[colorIdx] : 0;
345             s32[kc.x % s32Array2D.width()][kc.x / s32Array2D.width()] = color;
346         }
347     }
348 
349 
350     @CodeReflection
351     static public void compute(final ComputeContext computeContext, S32Array pallete, S32Array2D s32Array2D, float x, float y, float scale) {
352 
353         computeContext.dispatchKernel(
354                 s32Array2D.width()*s32Array2D.height(), //0..S32Array2D.size()
355                 kc -> mandel(kc, s32Array2D, pallete, x, y, scale));
356     }
357 
358     @HatTest
359     public static void testMandel() {
360         final int width = 1024;
361         final int height = 1024;
362         final float defaultScale = 3f;
363         final float originX = -1f;
364         final float originY = 0;
365         final int maxIterations = 64;
366 
367         Accelerator accelerator = new Accelerator(MethodHandles.lookup(), Backend.FIRST);
368 
369         S32Array2D s32Array2D = S32Array2D.create(accelerator, width, height);
370 
371         int[] palletteArray = new int[maxIterations];
372 
373         for (int i = 1; i < maxIterations; i++) {
374             palletteArray[i]=(i/8+1);// 0-7?
375         }
376         palletteArray[0]=0;
377         S32Array pallette = S32Array.createFrom(accelerator, palletteArray);
378 
379         accelerator.compute(cc -> compute(cc, pallette, s32Array2D, originX, originY, defaultScale));
380 
381         // Well take 1 in 4 samples (so 1024 -> 128 grid) of the pallette.
382         int subsample = 16;
383         char[] charPallette9 = new char []{' ', '.', ',',':', '-', '+','*', '#', '@', '%'};
384         for (int y = 0; y<height/subsample; y++) {
385             for (int x = 0; x<width/subsample; x++) {
386                 int palletteValue = s32Array2D.get(x*subsample,y*subsample); // so 0->8
387                 int paletteCheck = mandelCheck(x*subsample, y*subsample, width, height, palletteArray, originX, originY, defaultScale);
388                 // System.out.print(charPallette9[palletteValue]);
389                 HatAsserts.assertEquals(paletteCheck, palletteValue);
390             }
391             // System.out.println();
392         }
393     }
394 
395     // simplified version of BlackScholes using ArrayView
396     @CodeReflection
397     public static float[] blackScholesCheck(float s, float x, float t, float r, float v) {
398         float expNegRt = (float) Math.exp(-r * t);
399         float d1 = (float) ((Math.log(s / x) + (r + v * v * .5f) * t) / (v * Math.sqrt(t)));
400         float d2 = (float) (d1 - v * Math.sqrt(t));
401         float cnd1 = CND(d1);
402         float cnd2 = CND(d2);
403         float call = s * cnd1 - expNegRt * x * cnd2;
404         float put = expNegRt * x * (1 - cnd2) - s * (1 - cnd1);
405         return new float[]{call, put};
406     }
407 
408     @CodeReflection
409     public static void blackScholesKernel(@RO KernelContext kc,
410                                           @WO F32Array call,
411                                           @WO F32Array put,
412                                           @RO F32Array sArray,
413                                           @RO F32Array xArray,
414                                           @RO F32Array tArray,
415                                           float r,
416                                           float v) {
417         if (kc.x<kc.maxX){
418             float[] callArr = call.arrayView();
419             float[] putArr = put.arrayView();
420             float[] sArr = sArray.arrayView();
421             float[] xArr = xArray.arrayView();
422             float[] tArr = tArray.arrayView();
423 
424             float expNegRt = (float) Math.exp(-r * tArr[kc.x]);
425             float d1 = (float) ((Math.log(sArr[kc.x] / xArr[kc.x]) + (r + v * v * .5f) * tArr[kc.x]) / (v * Math.sqrt(tArr[kc.x])));
426             float d2 = (float) (d1 - v * Math.sqrt(tArr[kc.x]));
427             float cnd1 = CND(d1);
428             float cnd2 = CND(d2);
429             float value = sArr[kc.x] * cnd1 - expNegRt * xArr[kc.x] * cnd2;
430             callArr[kc.x] = value;
431             putArr[kc.x] = expNegRt * xArr[kc.x] * (1 - cnd2) - sArr[kc.x] * (1 - cnd1);
432         }
433     }
434 
435     @CodeReflection
436     public static float CND(float input) {
437         float x = input;
438         if (input < 0f) { // input = Math.abs(input)?
439             x = -input;
440         }
441 
442         float term = 1f / (1f + (0.2316419f * x));
443         float term_pow2 = term * term;
444         float term_pow3 = term_pow2 * term;
445         float term_pow4 = term_pow2 * term_pow2;
446         float term_pow5 = term_pow2 * term_pow3;
447 
448         float part1 = (1f / (float)Math.sqrt(2f * 3.1415926535f)) * (float)Math.exp((-x * x) * 0.5f);
449 
450         float part2 = (0.31938153f * term) +
451                 (-0.356563782f * term_pow2) +
452                 (1.781477937f * term_pow3) +
453                 (-1.821255978f * term_pow4) +
454                 (1.330274429f * term_pow5);
455 
456         if (input >= 0f) {
457             return 1f - part1 * part2;
458         }
459         return part1 * part2;
460 
461     }
462 
463     @CodeReflection
464     public static void blackScholes(@RO ComputeContext cc, @WO F32Array call, @WO F32Array put, @RO F32Array S, @RO F32Array X, @RO F32Array T, float r, float v) {
465         cc.dispatchKernel(call.length(),
466                 kc -> blackScholesKernel(kc, call, put, S, X, T, r, v)
467         );
468     }
469 
470     static F32Array floatArray(Accelerator accelerator, int size, float low, float high, Random rand) {
471         F32Array array = F32Array.create(accelerator, size);
472         for (int i = 0; i <size; i++) {
473             array.array(i, rand.nextFloat() * (high - low) + low);
474         }
475         return array;
476     }
477 
478     @HatTest
479     public static void testBlackScholes() {
480         int size = 50;
481         Random rand = new Random();
482         var accelerator = new Accelerator(java.lang.invoke.MethodHandles.lookup(), Backend.FIRST);//new JavaMultiThreadedBackend());
483         var call = F32Array.create(accelerator, size);
484         for (int i = 0; i < call.length(); i++) {
485             call.array(i, i);
486         }
487 
488         var put = F32Array.create(accelerator, size);
489         for (int i = 0; i < put.length(); i++) {
490             put.array(i, i);
491         }
492 
493         var S = floatArray(accelerator, size,1f, 100f, rand);
494         var X = floatArray(accelerator, size,1f, 100f, rand);
495         var T = floatArray(accelerator,size, 0.25f, 10f, rand);
496         float r = 0.02f;
497         float v = 0.30f;
498 
499         accelerator.compute(cc -> blackScholes(cc, call, put, S, X, T, r, v));
500         float[] res;
501         for (int i = 0; i < call.length(); i++) {
502             res = blackScholesCheck(S.array(i), X.array(i), T.array(i), r, v);
503             HatAsserts.assertEquals(res[0], call.array(i), 0.0001);
504             HatAsserts.assertEquals(res[1], put.array(i), 0.0001);
505         }
506     }
507 
508     // basic test of local and private buffer ArrayViews
509     private interface SharedMemory extends Buffer {
510         void array(long index, int value);
511         int array(long index);
512         Schema<SharedMemory> schema = Schema.of(SharedMemory.class,
513                 arr -> arr.array("array", 1024));
514         static SharedMemory create(Accelerator accelerator) {
515             return schema.allocate(accelerator);
516         }
517         static SharedMemory createLocal() {
518             return schema.allocate(new Accelerator(MethodHandles.lookup(), Backend.FIRST));
519         }
520 
521         default int[] localArrayView() {
522             int[] view = new int[1024];
523             for (int i = 0; i < 1024; i++) {
524                 view[i] = this.array(i);
525             }
526             return view;
527         }
528     }
529 
530     public interface PrivateArray extends Buffer {
531         void array(long index, int value);
532         int array(long index);
533         Schema<PrivateArray> schema = Schema.of(PrivateArray.class,
534                 arr -> arr.array("array", 16));
535         static PrivateArray create(Accelerator accelerator) {
536             return schema.allocate(accelerator);
537         }
538         static PrivateArray createPrivate() {
539             return schema.allocate(new Accelerator(MethodHandles.lookup(), Backend.FIRST));
540         }
541 
542         default int[] privateArrayView() {
543             int[] view = new int[16];
544             for (int i = 0; i < 16; i++) {
545                 view[i] = this.array(i);
546             }
547             return view;
548         }
549     }
550 
551     @CodeReflection
552     public static void squareKernelWithPrivateAndLocal(@RO  KernelContext kc, @RW S32Array s32Array) {
553         SharedMemory shared = SharedMemory.createLocal();
554         if (kc.x<kc.maxX){
555             int[] arr = s32Array.arrayView();
556             arr[kc.x] += arr[kc.x];
557             // int[] a = new int[4];
558             // a[1] = 4;
559 
560             PrivateArray priv = PrivateArray.createPrivate();
561             int[] privView = priv.privateArrayView();
562             privView[0] = 1;
563             arr[kc.x] += privView[0];
564 
565             int[] sharedView = shared.localArrayView();
566             sharedView[0] = 16;
567             arr[kc.x] += sharedView[0];
568         }
569     }
570 
571     @CodeReflection
572     public static void privateAndLocal(@RO ComputeContext cc, @RW S32Array s32Array) {
573         cc.dispatchKernel(s32Array.length(),
574                 kc -> squareKernelWithPrivateAndLocal(kc, s32Array)
575         );
576     }
577 
578     @HatTest
579     public static void testPrivateAndLocal() {
580 
581         var accelerator = new Accelerator(MethodHandles.lookup(), Backend.FIRST);//new JavaMultiThreadedBackend());
582         var arr = S32Array.create(accelerator, 32);
583         for (int i = 0; i < arr.length(); i++) {
584             arr.array(i, i);
585         }
586         accelerator.compute(
587                 cc -> privateAndLocal(cc, arr)  //QuotableComputeContextConsumer
588         );                                     //   extends Quotable, Consumer<ComputeContext>
589         for (int i = 0; i < arr.length(); i++) {
590             HatAsserts.assertEquals(2 * i + 17, arr.array(i));
591         }
592     }
593 }