From 5389e66d754c288cd0413fdc19cb5b57dd018361 Mon Sep 17 00:00:00 2001 From: David Ellis Date: Thu, 24 Oct 2024 12:31:15 -0500 Subject: [PATCH 01/20] Implement GPGPU for the browser --- .github/workflows/node.yml | 14 + .gitignore | 1 + alan_std.js | 195 +++++++++++++- alan_std.test.html | 12 + alan_std.test.js | 331 +++++++++++------------ alan_std.test_gpgpu.js | 24 ++ package.json | 18 +- yarn.lock | 533 +++++++++++++++++++++++++++++++++++++ 8 files changed, 956 insertions(+), 172 deletions(-) create mode 100644 alan_std.test.html create mode 100644 alan_std.test_gpgpu.js create mode 100644 yarn.lock diff --git a/.github/workflows/node.yml b/.github/workflows/node.yml index 0fd1fa31..cb9209a4 100644 --- a/.github/workflows/node.yml +++ b/.github/workflows/node.yml @@ -13,3 +13,17 @@ jobs: - uses: actions/checkout@v4 - name: Run tests run: npm test + test-js-gpgpu: + runs-on: [self-hosted, macOS] + steps: + - uses: actions/checkout@v4 + - name: Install deps + run: yarn + - name: Bundle stdlib + run: yarn bundle + - name: Start webserver + run: yarn start-server + - name: Test GPGPU + run: yarn test-gpgpu + - name: Stop webserver + run: yarn stop-server diff --git a/.gitignore b/.gitignore index 37d81ba3..36059d03 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ target/ .vscode *.swp node_modules/ +alanStdBundle.js diff --git a/alan_std.js b/alan_std.js index faeceb14..d63c1007 100644 --- a/alan_std.js +++ b/alan_std.js @@ -1,3 +1,6 @@ +import { v4 as uuidv4 } from 'uuid'; +export { v4 as uuidv4 } from 'uuid'; + export class AlanError { constructor(message) { this.message = message; @@ -20,9 +23,22 @@ export function ifbool(b, t, f) { } } +// For those reading this binding support code, you might be wondering *why* all of the primitive +// types are now boxed in their own classes. The reason is that in Alan (and Rust), you can mark +// any input argument as mutable instead of the default being immutable, but in Javascript, all +// arguments are "immutable" but objects are actually pointers under-the-hood and anything you have +// pointer access to is mutable. Wrapping primitive types in objects makes it possible for the Alan +// compiler to give mutable access to them from a function (which is how all operators are defined +// in Alan). It would be *possible* to avoid this by inlining the function definition if any of the +// arguments are a mutable variant of a primitive type, but that would both make the compiler more +// complicated (and increase the maintenance burden) *and* increase the size of the generated code +// (as all of these functions would have their function bodies copied everywhere), which is a big +// problem for code that is read over the wire and re-loaded into a JIT every single page load. +// Further, that JIT is very well put together by a massive team of engineers over decades -- it'll +// be able to unbox the value and maintain the desired mutable behavior just fine, probably. ;) export class Int { constructor(val, bits, size, lower, upper) { - if (bits == 64) { + if (bits === 64) { let v = BigInt(val); if (v > upper) { this.val = upper; @@ -64,7 +80,7 @@ export class Int { } wrappingDiv(a) { - if (this.bits == 64) { + if (this.bits === 64) { return this.build(this.val / a.val); } else { return this.build(Math.floor(this.val / a.val)); @@ -76,7 +92,7 @@ export class Int { } wrappingPow(a) { - if (this.bits == 64) { + if (this.bits === 64) { return this.build(this.val ** a.val); } else { return this.build(Math.floor(this.val ** a.val)); @@ -285,7 +301,7 @@ export class Float { export class F32 extends Float { constructor(v) { - super(Number(v), 32); + super(Number(v), 32); } build(v) { @@ -295,7 +311,7 @@ export class F32 extends Float { export class F64 extends Float { constructor(v) { - super(Number(v), 64); + super(Number(v), 64); } build(v) { @@ -330,3 +346,172 @@ export class Str { return this.val.toString(); } } + +export class GPU { + constructor(adapter, device, queue) { + this.adapter = adapter; + this.device = device; + this.queue = queue; + } + + static async list() { + let out = []; + let hp = await navigator?.gpu?.requestAdapter({ powerPreference: "high-performance", }); + let lp = await navigator?.gpu?.requestAdapter({ powerPreference: 'low-power', }); + let np = await navigator?.gpu?.requestAdapter(); + if (hp) out.push(hp); + if (lp) out.push(lp); + if (np) out.push(np); + return out; + } + + static async init(adapters) { + let out = []; + for (let adapter of adapters) { + let features = adapter.features; + let limits = adapter.limits; + let info = adapter.info; + let device = await adapter.requestDevice({ + label: `${info.device} on ${info.architecture}`, + // If I don't pass these through, it defaults to a really small set of features and limits + requiredFeatures: features, + requiredLimits: limits, + }); + out.push(new GPU(adapter, device, device.queue)); + } + return out; + } +} + +let GPUS = null; + +export async function gpu() { + if (GPUS === null) { + GPUS = await GPU.init(await GPU.list()); + } + if (GPUS.length > 0) { + return GPUS[0]; + } else { + throw new AlanError("This program requires a GPU but there are no WebGPU-compliant GPUs on this machine"); + } +} + +export async function createBufferInit(usage, vals) { + let g = await gpu(); + let b = await g.device.createBuffer({ + mappedAtCreation: true, + size: vals.length * 4, + usage, + label: `buffer_${uuidv4().replaceAll('-', '_')}`, + }); + let ab = b.getMappedRange(); + let i32v = new Int32Array(ab); + for (let i = 0; i < vals.length; i++) { + i32v[i] = vals[i].valueOf(); + } + b.unmap(); + return b; +} + +export async function createEmptyBuffer(usage, size) { + let g = await gpu(); + let b = await g.device.createBuffer({ + size, + usage, + label: `buffer_${uuidv4().replaceAll('-', '_')}`, + }); + return b; +} + +export function mapReadBufferType() { + return GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST; +} + +export function mapWriteBufferType() { + return GPUBufferUsage.MAP_WRITE | GPUBufferUsage.COPY_SRC; +} + +export function storageBufferType() { + return GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST | GPUBufferUsage.COPY_SRC; +} + +export function bufferlen(b) { + return new I64(b.size / 4); +} + +export function bufferid(b) { + return b.label; +} + +export class GPGPU { + constructor(source, buffers, workgroupSizes, entrypoint) { + this.source = source; + this.entrypoint = entrypoint ?? "main"; + this.buffers = buffers; + this.workgroupSizes = workgroupSizes; + } +} + +export async function gpuRun(gg) { + let g = await gpu(); + let module = g.device.createShaderModule({ + code: gg.source, + }); + let computePipeline = g.device.createComputePipeline({ + layout: "auto", + compute: { + entryPoint: gg.entrypoint, + module, + }, + }); + let encoder = g.device.createCommandEncoder(); + let cpass = encoder.beginComputePass(); + cpass.setPipeline(computePipeline); + for (let i = 0; i < gg.buffers.length; i++) { + let bindGroupLayout = computePipeline.getBindGroupLayout(i); + let bindGroupBuffers = gg.buffers[i]; + let bindGroupEntries = []; + for (let j = 0; j < bindGroupBuffers.length; j++) { + bindGroupEntries.push({ + binding: j, + resource: { buffer: bindGroupBuffers[j] } + }); + } + let bindGroup = g.device.createBindGroup({ + layout: bindGroupLayout, + entries: bindGroupEntries, + }); + cpass.setBindGroup(i, bindGroup); + } + cpass.dispatchWorkgroups(...gg.workgroupSizes); + g.queue.submit([encoder.finish()]); +} + +export async function readBuffer(b) { + let g = await gpu(); + let tempBuffer = await createEmptyBuffer(mapReadBufferType(), b.size); + let encoder = g.device.createCommandEncoder(); + encoder.copyBufferToBuffer(b, 0, tempBuffer, 0, b.size); + g.queue.submit([encoder.finish()]); + await tempBuffer.mapAsync(GPUMapMode.READ); + let data = tempBuffer.slice(0); + tempBuffer.unmap(); + let vals = new Int32Array(data); + let out = []; + for (let i = 0; i < vals.length; i++) { + out[i] = new I32(vals[i]); + } + return out; +} + +export async function replaceBuffer(b, v) { + if (v.length != bufferlen(b)) { + return new AlanError("The input array is not the same size as the buffer"); + } + await b.mapAsync(GPUMapMode.WRITE); + let data = b.slice(0); + for (let i = 0; i < v.length; i++) { + data[i] = v[i].valueOf(); + } + b.unmap(); +} diff --git a/alan_std.test.html b/alan_std.test.html new file mode 100644 index 00000000..b0a677e3 --- /dev/null +++ b/alan_std.test.html @@ -0,0 +1,12 @@ + + + + + Alan Std Test + + + + diff --git a/alan_std.test.js b/alan_std.test.js index fef36d1c..5c9d2058 100644 --- a/alan_std.test.js +++ b/alan_std.test.js @@ -3,262 +3,263 @@ import assert from "node:assert"; import * as alanStd from "./alan_std.js"; assert(new alanStd.AlanError("foo") instanceof alanStd.AlanError, "AlanError constructor"); -assert(new alanStd.AlanError("foo").message === "foo", "AlanError message"); +assert.strictEqual(new alanStd.AlanError("foo").message, "foo", "AlanError message"); -assert(alanStd.nanToError(5) === 5, "nanToError normal number"); -assert(alanStd.nanToError(NaN).message === "Not a Number", "nanToError NaN"); +assert.strictEqual(alanStd.nanToError(5), 5, "nanToError normal number"); +assert.strictEqual(alanStd.nanToError(NaN).message, "Not a Number", "nanToError NaN"); -assert(new alanStd.I8(0).val === 0, "I8 0"); -assert(new alanStd.I8(150).val === 127, "I8 150"); -assert(new alanStd.I8(-150).val === -128, "I8 -150"); +assert.strictEqual(new alanStd.I8(0).val, 0, "I8 0"); +assert.strictEqual(new alanStd.I8(150).val, 127, "I8 150"); +assert.strictEqual(new alanStd.I8(-150).val, -128, "I8 -150"); -assert(new alanStd.I16(0).val === 0, "clampI16 0"); -assert(new alanStd.I16(40_000).val === 32_767, "clampI16 40k"); -assert(new alanStd.I16(-40_000).val === -32_768, "clampI16 -40k"); +assert.strictEqual(new alanStd.I16(0).val, 0, "clampI16 0"); +assert.strictEqual(new alanStd.I16(40_000).val, 32_767, "clampI16 40k"); +assert.strictEqual(new alanStd.I16(-40_000).val, -32_768, "clampI16 -40k"); -assert(new alanStd.I32(0).val === 0, "clampI32 0"); -assert(new alanStd.I32(3_000_000_000).val === 2_147_483_647, "clampI32 3B"); -assert(new alanStd.I32(-3_000_000_000).val === -2_147_483_648, "clampI32 -3B"); +assert.strictEqual(new alanStd.I32(0).val, 0, "clampI32 0"); +assert.strictEqual(new alanStd.I32(3_000_000_000).val, 2_147_483_647, "clampI32 3B"); +assert.strictEqual(new alanStd.I32(-3_000_000_000).val, -2_147_483_648, "clampI32 -3B"); -assert(new alanStd.I64(0n).val === 0n, "clampI64 0n"); -assert(new alanStd.I64(10_000_000_000_000_000_000n).val === 9_223_372_036_854_775_807n, "clampI64 10Q"); -assert(new alanStd.I64(-10_000_000_000_000_000_000n).val === -9_223_372_036_854_775_808n, "clampI64 -10Q"); +assert.strictEqual(new alanStd.I64(0n).val, 0n, "clampI64 0n"); +assert.strictEqual(new alanStd.I64(10_000_000_000_000_000_000n).val, 9_223_372_036_854_775_807n, "clampI64 10Q"); +assert.strictEqual(new alanStd.I64(-10_000_000_000_000_000_000n).val, -9_223_372_036_854_775_808n, "clampI64 -10Q"); -assert(new alanStd.U8(0).val === 0, "clampU8 0"); -assert(new alanStd.U8(350).val === 255, "clampU8 350"); -assert(new alanStd.U8(-150).val === 0, "clampU8 -150"); +assert.strictEqual(new alanStd.U8(0).val, 0, "clampU8 0"); +assert.strictEqual(new alanStd.U8(350).val, 255, "clampU8 350"); +assert.strictEqual(new alanStd.U8(-150).val, 0, "clampU8 -150"); -assert(new alanStd.U16(0).val === 0, "clampU16 0"); -assert(new alanStd.U16(80_000).val === 65_535, "clampU16 80k"); -assert(new alanStd.U16(-40_000).val === 0, "clampU16 -40k"); +assert.strictEqual(new alanStd.U16(0).val, 0, "clampU16 0"); +assert.strictEqual(new alanStd.U16(80_000).val, 65_535, "clampU16 80k"); +assert.strictEqual(new alanStd.U16(-40_000).val, 0, "clampU16 -40k"); -assert(new alanStd.U32(0).val === 0, "clampU32 0"); -assert(new alanStd.U32(6_000_000_000).val === 4_294_967_295, "clampU32 6B"); -assert(new alanStd.U32(-3_000_000_000).val === 0, "clampU32 -3B"); +assert.strictEqual(new alanStd.U32(0).val, 0, "clampU32 0"); +assert.strictEqual(new alanStd.U32(6_000_000_000).val, 4_294_967_295, "clampU32 6B"); +assert.strictEqual(new alanStd.U32(-3_000_000_000).val, 0, "clampU32 -3B"); -assert(new alanStd.U64(0n).val === 0n, "clampU64 0n"); -assert(new alanStd.U64(20_000_000_000_000_000_000n).val === 18_446_744_073_709_551_615n, "clampU64 20Q"); -assert(new alanStd.U64(-10_000_000_000_000_000_000n).val === 0n, "clampU64 -10Q"); +assert.strictEqual(new alanStd.U64(0n).val, 0n, "clampU64 0n"); +assert.strictEqual(new alanStd.U64(20_000_000_000_000_000_000n).val, 18_446_744_073_709_551_615n, "clampU64 20Q"); +assert.strictEqual(new alanStd.U64(-10_000_000_000_000_000_000n).val, 0n, "clampU64 -10Q"); -assert(alanStd.ifbool(true, () => true, () => false) === true, "ifbool true"); -assert(alanStd.ifbool(false, () => true, () => false) === false, "ifbool false"); +assert.strictEqual(alanStd.ifbool(true, () => true, () => false), true, "ifbool true"); +assert.strictEqual(alanStd.ifbool(false, () => true, () => false), false, "ifbool false"); -assert(new alanStd.I8(1).wrappingAdd(new alanStd.I8(2)).val === 3, "wrappingAddI8 1 + 2 = 3"); -assert(new alanStd.I8(127).wrappingAdd(new alanStd.I8(1)).val === -128, "wrappingAddI8 127 + 1 = -128"); +assert.strictEqual(new alanStd.I8(1).wrappingAdd(new alanStd.I8(2)).val, 3, "wrappingAddI8 1 + 2 = 3"); +assert.strictEqual(new alanStd.I8(127).wrappingAdd(new alanStd.I8(1)).val, -128, "wrappingAddI8 127 + 1 = -128"); -assert(new alanStd.I8(1).wrappingSub(new alanStd.I8(2)).val === -1, "wrappingSubI8 1 - 2 = -1"); -assert(new alanStd.I8(-128).wrappingSub(new alanStd.I8(1)).val === 127, "wrappingSubI8 -128 - 1 = 127"); +assert.strictEqual(new alanStd.I8(1).wrappingSub(new alanStd.I8(2)).val, -1, "wrappingSubI8 1 - 2 = -1"); +assert.strictEqual(new alanStd.I8(-128).wrappingSub(new alanStd.I8(1)).val, 127, "wrappingSubI8 -128 - 1 = 127"); -assert(new alanStd.I8(64).wrappingMul(new alanStd.I8(64)).val === 0, "wrappingMulI8 64 * 64 = 0"); +assert.strictEqual(new alanStd.I8(64).wrappingMul(new alanStd.I8(64)).val, 0, "wrappingMulI8 64 * 64 = 0"); -assert(new alanStd.I8(-128).wrappingDiv(new alanStd.I8(2)).val == -64, "wrappingDivI8 -128 / 2 = -64"); +assert.equal(new alanStd.I8(-128).wrappingDiv(new alanStd.I8(2)), -64, "wrappingDivI8 -128 / 2 = -64"); -assert(new alanStd.I8(5).wrappingMod(new alanStd.I8(2)).val == 1, "wrappingModI8 5 % 2 = 1"); +assert.equal(new alanStd.I8(5).wrappingMod(new alanStd.I8(2)), 1, "wrappingModI8 5 % 2 = 1"); -assert(new alanStd.I8(2).wrappingPow(new alanStd.I8(8)).val == 0, "wrappingPowI8 2 ^ 8 = 0"); +assert.equal(new alanStd.I8(2).wrappingPow(new alanStd.I8(8)), 0, "wrappingPowI8 2 ^ 8 = 0"); -assert(new alanStd.I8(-128).wrappingShl(new alanStd.I8(1)).val == 0, "wrappingShlI8 -128 << 1 = 0"); +assert.equal(new alanStd.I8(-128).wrappingShl(new alanStd.I8(1)), 0, "wrappingShlI8 -128 << 1 = 0"); -assert(new alanStd.I8(-128).wrappingShr(new alanStd.I8(1)).val == 64, "wrappingShrI8 -128 >> 1 = 64"); +assert.equal(new alanStd.I8(-128).wrappingShr(new alanStd.I8(1)), 64, "wrappingShrI8 -128 >> 1 = 64"); -assert(new alanStd.I8(-128).rotateLeft(new alanStd.I8(1)).val == 1, "rotateLeftI8 -128 <<< 1 = 1"); -assert(new alanStd.I8(0b01010101).rotateLeft(new alanStd.I8(0)).val == 0b01010101, "rotateLeftI8 0b01010101 <<< 0 = 0b01010101"); -assert(new alanStd.I8(0b01010101).rotateLeft(new alanStd.I8(1)).val == 0b10101010 - 256, "rotateLeftI8 0b01010101 <<< 1 = 0b10101010"); -assert(new alanStd.I8(0b01010101).rotateLeft(new alanStd.I8(2)).val == 0b01010101, "rotateLeftI8 0b01010101 <<< 2 = 0b01010101"); -assert(new alanStd.I8(0b01010101).rotateLeft(new alanStd.I8(3)).val == 0b10101010 - 256, "rotateLeftI8 0b01010101 <<< 3 = 0b10101010"); +assert.equal(new alanStd.I8(-128).rotateLeft(new alanStd.I8(1)), 1, "rotateLeftI8 -128 <<< 1 = 1"); +assert.equal(new alanStd.I8(0b01010101).rotateLeft(new alanStd.I8(0)), 0b01010101, "rotateLeftI8 0b01010101 <<< 0 = 0b01010101"); +assert.equal(new alanStd.I8(0b01010101).rotateLeft(new alanStd.I8(1)), 0b10101010 - 256, "rotateLeftI8 0b01010101 <<< 1 = 0b10101010"); +assert.equal(new alanStd.I8(0b01010101).rotateLeft(new alanStd.I8(2)), 0b01010101, "rotateLeftI8 0b01010101 <<< 2 = 0b01010101"); +assert.equal(new alanStd.I8(0b01010101).rotateLeft(new alanStd.I8(3)), 0b10101010 - 256, "rotateLeftI8 0b01010101 <<< 3 = 0b10101010"); -assert(new alanStd.I8(64).rotateRight(new alanStd.I8(1)).val == 32, "rotateRightI8 64 >>> 1 = 32"); -assert(new alanStd.I8(0b01010101).rotateRight(new alanStd.I8(0)).val == 0b01010101, "rotateRightI8 0b01010101 <<< 0 = 0b01010101"); -assert(new alanStd.I8(0b01010101).rotateRight(new alanStd.I8(1)).val == 0b10101010 - 256, "rotateRightI8 0b01010101 <<< 1 = 0b10101010"); -assert(new alanStd.I8(0b01010101).rotateRight(new alanStd.I8(2)).val == 0b01010101, "rotateRightI8 0b01010101 <<< 2 = 0b01010101"); -assert(new alanStd.I8(0b01010101).rotateRight(new alanStd.I8(3)).val == 0b10101010 - 256, "rotateRightI8 0b01010101 <<< 3 = 0b10101010"); +assert.equal(new alanStd.I8(64).rotateRight(new alanStd.I8(1)).val, 32, "rotateRightI8 64 >>> 1 = 32"); +assert.equal(new alanStd.I8(0b01010101).rotateRight(new alanStd.I8(0)).val, 0b01010101, "rotateRightI8 0b01010101 <<< 0 = 0b01010101"); +assert.equal(new alanStd.I8(0b01010101).rotateRight(new alanStd.I8(1)).val, 0b10101010 - 256, "rotateRightI8 0b01010101 <<< 1 = 0b10101010"); +assert.equal(new alanStd.I8(0b01010101).rotateRight(new alanStd.I8(2)).val, 0b01010101, "rotateRightI8 0b01010101 <<< 2 = 0b01010101"); +assert.equal(new alanStd.I8(0b01010101).rotateRight(new alanStd.I8(3)).val, 0b10101010 - 256, "rotateRightI8 0b01010101 <<< 3 = 0b10101010"); -assert(new alanStd.I16(1).wrappingAdd(new alanStd.I16(2)).val === 3, "wrappingAddI16 1 + 2 = 3"); -assert(new alanStd.I16(32_767).wrappingAdd(new alanStd.I16(1)).val === -32_768, "wrappingAddI16 32_767 + 1 = -32_768"); +assert.strictEqual(new alanStd.I16(1).wrappingAdd(new alanStd.I16(2)).val, 3, "wrappingAddI16 1 + 2 = 3"); +assert.strictEqual(new alanStd.I16(32_767).wrappingAdd(new alanStd.I16(1)).val, -32_768, "wrappingAddI16 32_767 + 1 = -32_768"); -assert(new alanStd.I16(1).wrappingSub(new alanStd.I16(2)).val === -1, "wrappingSubI16 1 - 2 = -1"); -assert(new alanStd.I16(-32_768).wrappingSub(new alanStd.I16(1)).val === 32_767, "wrappingSubI16 -32_768 - 1 = 32_767"); +assert.strictEqual(new alanStd.I16(1).wrappingSub(new alanStd.I16(2)).val, -1, "wrappingSubI16 1 - 2 = -1"); +assert.strictEqual(new alanStd.I16(-32_768).wrappingSub(new alanStd.I16(1)).val, 32_767, "wrappingSubI16 -32_768 - 1 = 32_767"); -assert(new alanStd.I16(256).wrappingMul(new alanStd.I16(256)).val === 0, "wrappingMulI16 256 * 256 = 0"); +assert.strictEqual(new alanStd.I16(256).wrappingMul(new alanStd.I16(256)).val, 0, "wrappingMulI16 256 * 256 = 0"); -assert(new alanStd.I16(-32_768).wrappingDiv(new alanStd.I16(2)) == -16_384, "wrappingDivI16 -32_768 / 2 = -16_384"); +assert.equal(new alanStd.I16(-32_768).wrappingDiv(new alanStd.I16(2)), -16_384, "wrappingDivI16 -32_768 / 2 = -16_384"); -assert(new alanStd.I16(5).wrappingMod(new alanStd.I16(2)) == 1, "wrappingModI16 5 % 2 = 1"); +assert.equal(new alanStd.I16(5).wrappingMod(new alanStd.I16(2)), 1, "wrappingModI16 5 % 2 = 1"); -assert(new alanStd.I16(2).wrappingPow(new alanStd.I16(16)) == 0, "wrappingPowI16 2 ^ 16 = 0"); +assert.equal(new alanStd.I16(2).wrappingPow(new alanStd.I16(16)), 0, "wrappingPowI16 2 ^ 16 = 0"); -assert(new alanStd.I16(-32_768).wrappingShl(new alanStd.I16(1)) == 0, "wrappingShlI16 -32_768 << 1 = 0"); +assert.equal(new alanStd.I16(-32_768).wrappingShl(new alanStd.I16(1)), 0, "wrappingShlI16 -32_768 << 1 = 0"); -assert(new alanStd.I16(-32_768).wrappingShr(new alanStd.I16(1)) == 16_384, "wrappingShrI16 -32_768 >> 1 = 16_384"); +assert.equal(new alanStd.I16(-32_768).wrappingShr(new alanStd.I16(1)), 16_384, "wrappingShrI16 -32_768 >> 1 = 16_384"); -assert(new alanStd.I16(-32_768).rotateLeft(new alanStd.I16(1)) == 1, "rotateLeftI16 -32_768 <<< 1 = 1"); +assert.equal(new alanStd.I16(-32_768).rotateLeft(new alanStd.I16(1)), 1, "rotateLeftI16 -32_768 <<< 1 = 1"); -assert(new alanStd.I16(16_384).rotateRight(new alanStd.I16(1)) == 8_192, "rotateRightI16 16_384 >>> 1 = -32_768"); +assert.equal(new alanStd.I16(16_384).rotateRight(new alanStd.I16(1)), 8_192, "rotateRightI16 16_384 >>> 1 = -32_768"); -assert(new alanStd.I32(1).wrappingAdd(new alanStd.I32(2)).val === 3, "wrappingAddI32 1 + 2 = 3"); -assert(new alanStd.I32(2_147_483_647).wrappingAdd(new alanStd.I32(1)).val === -2_147_483_648, "wrappingAddI32 2_147_483_647 + 1 = -2_147_483_648"); +assert.strictEqual(new alanStd.I32(1).wrappingAdd(new alanStd.I32(2)).val, 3, "wrappingAddI32 1 + 2 = 3"); +assert.strictEqual(new alanStd.I32(2_147_483_647).wrappingAdd(new alanStd.I32(1)).val, -2_147_483_648, "wrappingAddI32 2_147_483_647 + 1 = -2_147_483_648"); -assert(new alanStd.I32(1).wrappingSub(new alanStd.I32(2)).val === -1, "wrappingSubI32 1 - 2 = -1"); -assert(new alanStd.I32(-2_147_483_648).wrappingSub(new alanStd.I32(1)).val === 2_147_483_647, "wrappingSubI32 -2_147_483_648 - 1 = 2_147_483_647"); +assert.strictEqual(new alanStd.I32(1).wrappingSub(new alanStd.I32(2)).val, -1, "wrappingSubI32 1 - 2 = -1"); +assert.strictEqual(new alanStd.I32(-2_147_483_648).wrappingSub(new alanStd.I32(1)).val, 2_147_483_647, "wrappingSubI32 -2_147_483_648 - 1 = 2_147_483_647"); -assert(new alanStd.I32(65_536).wrappingMul(new alanStd.I32(65_536)).val === 0, "wrappingMulI32 65_536 * 65_536 = 0"); +assert.strictEqual(new alanStd.I32(65_536).wrappingMul(new alanStd.I32(65_536)).val, 0, "wrappingMulI32 65_536 * 65_536 = 0"); -assert(new alanStd.I32(-2_147_483_648).wrappingDiv(new alanStd.I32(2)) == -1_073_741_824, "wrappingDivI32 -2_147_483_648 / 2 = -1_073_741_824"); +assert.equal(new alanStd.I32(-2_147_483_648).wrappingDiv(new alanStd.I32(2)), -1_073_741_824, "wrappingDivI32 -2_147_483_648 / 2 = -1_073_741_824"); -assert(new alanStd.I32(5).wrappingMod(new alanStd.I32(2)) == 1, "wrappingModI32 5 % 2 = 1"); +assert.equal(new alanStd.I32(5).wrappingMod(new alanStd.I32(2)), 1, "wrappingModI32 5 % 2 = 1"); -assert(new alanStd.I32(2).wrappingPow(new alanStd.I32(32)) == 0, "wrappingPowI32 2 ^ 32 = 0"); +assert.equal(new alanStd.I32(2).wrappingPow(new alanStd.I32(32)), 0, "wrappingPowI32 2 ^ 32 = 0"); -assert(new alanStd.I32(-2_147_483_648).wrappingShl(new alanStd.I32(1)) == 0, "wrappingShlI32 -2_147_483_648 << 1 = 0"); +assert.equal(new alanStd.I32(-2_147_483_648).wrappingShl(new alanStd.I32(1)), 0, "wrappingShlI32 -2_147_483_648 << 1 = 0"); -assert(new alanStd.I32(-2_147_483_648).wrappingShr(new alanStd.I32(1)) == 1_073_741_824, "wrappingShrI32 -2_147_483_648 >> 1 = 1_073_741_824"); +assert.equal(new alanStd.I32(-2_147_483_648).wrappingShr(new alanStd.I32(1)), 1_073_741_824, "wrappingShrI32 -2_147_483_648 >> 1 = 1_073_741_824"); -assert(new alanStd.I32(-2_147_483_648).rotateLeft(new alanStd.I32(1)) == 1, "rotateLeftI32 -2_147_483_648 <<< 1 = 1"); +assert.equal(new alanStd.I32(-2_147_483_648).rotateLeft(new alanStd.I32(1)), 1, "rotateLeftI32 -2_147_483_648 <<< 1 = 1"); -assert(new alanStd.I32(1_073_741_824).rotateRight(new alanStd.I32(1)) == 536_870_912, "rotateRightI32 1_073_741_824 >>> 1 = 536_870_912"); +assert.equal(new alanStd.I32(1_073_741_824).rotateRight(new alanStd.I32(1)), 536_870_912, "rotateRightI32 1_073_741_824 >>> 1 = 536_870_912"); -assert(new alanStd.I64(1n).wrappingAdd(new alanStd.I64(2n)).val === 3n, "wrappingAddI64 1 + 2 = 3"); -assert(new alanStd.I64(9_223_372_036_854_775_807n).wrappingAdd(new alanStd.I64(1n)).val === -9_223_372_036_854_775_808n, "wrappingAddI64 9_223_372_036_854_775_807 + 1 = -9_223_372_036_854_775_808"); +assert.strictEqual(new alanStd.I64(1n).wrappingAdd(new alanStd.I64(2n)).val, 3n, "wrappingAddI64 1 + 2 = 3"); +assert.strictEqual(new alanStd.I64(9_223_372_036_854_775_807n).wrappingAdd(new alanStd.I64(1n)).val, -9_223_372_036_854_775_808n, "wrappingAddI64 9_223_372_036_854_775_807 + 1 = -9_223_372_036_854_775_808"); -assert(new alanStd.I64(1n).wrappingSub(new alanStd.I64(2n)).val === -1n, "wrappingSubI64 1 - 2 = -1"); -assert(new alanStd.I64(-9_223_372_036_854_775_808n).wrappingSub(new alanStd.I64(1n)).val === 9_223_372_036_854_775_807n, "wrappingSubI64 -9_223_372_036_854_775_808 - 1 = 9_223_372_036_854_775_807"); +assert.strictEqual(new alanStd.I64(1n).wrappingSub(new alanStd.I64(2n)).val, -1n, "wrappingSubI64 1 - 2 = -1"); +assert.strictEqual(new alanStd.I64(-9_223_372_036_854_775_808n).wrappingSub(new alanStd.I64(1n)).val, 9_223_372_036_854_775_807n, "wrappingSubI64 -9_223_372_036_854_775_808 - 1 = 9_223_372_036_854_775_807"); -assert(new alanStd.I64(4_294_967_296n).wrappingMul(new alanStd.I64(4_294_967_296n)).val === 0n, "wrappingMulI64 4_294_967_296 * 4_294_967_296 = 0"); +assert.strictEqual(new alanStd.I64(4_294_967_296n).wrappingMul(new alanStd.I64(4_294_967_296n)).val, 0n, "wrappingMulI64 4_294_967_296 * 4_294_967_296 = 0"); -assert(new alanStd.I64(-9_223_372_036_854_775_808n).wrappingDiv(new alanStd.I64(2n)) == -4_611_686_018_427_387_904n, "wrappingDivI64 -9_223_372_036_854_775_808 / 2 = −4_611_686_018_427_387_904"); +assert.equal(new alanStd.I64(-9_223_372_036_854_775_808n).wrappingDiv(new alanStd.I64(2n)), -4_611_686_018_427_387_904n, "wrappingDivI64 -9_223_372_036_854_775_808 / 2 = −4_611_686_018_427_387_904"); -assert(new alanStd.I64(5n).wrappingMod(new alanStd.I64(2n)) == 1n, "wrappingModI64 5 % 2 = 1"); +assert.equal(new alanStd.I64(5n).wrappingMod(new alanStd.I64(2n)), 1n, "wrappingModI64 5 % 2 = 1"); -assert(new alanStd.I64(2n).wrappingPow(new alanStd.I64(64n)) == 0n, "wrappingPowI64 2 ^ 64 = 0"); +assert.equal(new alanStd.I64(2n).wrappingPow(new alanStd.I64(64n)), 0n, "wrappingPowI64 2 ^ 64 = 0"); -assert(new alanStd.I64(-9_223_372_036_854_775_808n).wrappingShl(new alanStd.I64(1n)) == 0n, "wrappingShlI64 -9_223_372_036_854_775_808 << 1 = 0"); +assert.equal(new alanStd.I64(-9_223_372_036_854_775_808n).wrappingShl(new alanStd.I64(1n)), 0n, "wrappingShlI64 -9_223_372_036_854_775_808 << 1 = 0"); -assert(new alanStd.I64(-9_223_372_036_854_775_808n).wrappingShr(new alanStd.I64(1n)) == 4_611_686_018_427_387_904n, "wrappingShrI64 -9_223_372_036_854_775_808 >> 1 = 4_611_686_018_427_387_904"); +assert.equal(new alanStd.I64(-9_223_372_036_854_775_808n).wrappingShr(new alanStd.I64(1n)), 4_611_686_018_427_387_904n, "wrappingShrI64 -9_223_372_036_854_775_808 >> 1 = 4_611_686_018_427_387_904"); -assert(new alanStd.I64(-9_223_372_036_854_775_808n).rotateLeft(new alanStd.I64(1n)) == 1n, "rotateLeftI64 -9_223_372_036_854_775_808 <<< 1 = 1"); +assert.equal(new alanStd.I64(-9_223_372_036_854_775_808n).rotateLeft(new alanStd.I64(1n)), 1n, "rotateLeftI64 -9_223_372_036_854_775_808 <<< 1 = 1"); -assert(new alanStd.I64(4_611_686_018_427_387_904n).rotateRight(new alanStd.I64(1n)) == 2_305_843_009_213_693_952n, "rotateRightI64 4_611_686_018_427_387_904 >>> 1 = 2_305_843_009_213_693_952"); +assert.equal(new alanStd.I64(4_611_686_018_427_387_904n).rotateRight(new alanStd.I64(1n)), 2_305_843_009_213_693_952n, "rotateRightI64 4_611_686_018_427_387_904 >>> 1 = 2_305_843_009_213_693_952"); -assert(new alanStd.U8(1).wrappingAdd(new alanStd.U8(2)).val === 3, "wrappingAddU8 1 + 2 = 3"); -assert(new alanStd.U8(255).wrappingAdd(new alanStd.U8(1)).val === 0, "wrappingAddU8 255 + 1 = 0"); +assert.strictEqual(new alanStd.U8(1).wrappingAdd(new alanStd.U8(2)).val, 3, "wrappingAddU8 1 + 2 = 3"); +assert.strictEqual(new alanStd.U8(255).wrappingAdd(new alanStd.U8(1)).val, 0, "wrappingAddU8 255 + 1 = 0"); -assert(new alanStd.U8(1).wrappingSub(new alanStd.U8(2)).val === 255, "wrappingSubU8 1 - 2 = 255"); -assert(new alanStd.U8(255).wrappingSub(new alanStd.U8(1)).val === 254, "wrappingSubU8 255 - 1 = 254"); +assert.strictEqual(new alanStd.U8(1).wrappingSub(new alanStd.U8(2)).val, 255, "wrappingSubU8 1 - 2 = 255"); +assert.strictEqual(new alanStd.U8(255).wrappingSub(new alanStd.U8(1)).val, 254, "wrappingSubU8 255 - 1 = 254"); -assert(new alanStd.U8(64).wrappingMul(new alanStd.U8(64)).val === 0, "wrappingMulU8 64 * 64 = 0"); +assert.strictEqual(new alanStd.U8(64).wrappingMul(new alanStd.U8(64)).val, 0, "wrappingMulU8 64 * 64 = 0"); -assert(new alanStd.U8(128).wrappingDiv(new alanStd.U8(2)) == 64, "wrappingDivU8 128 / 2 = 64"); +assert.equal(new alanStd.U8(128).wrappingDiv(new alanStd.U8(2)), 64, "wrappingDivU8 128 / 2 = 64"); -assert(new alanStd.U8(5).wrappingMod(new alanStd.U8(2)) == 1, "wrappingModU8 5 % 2 = 1"); +assert.equal(new alanStd.U8(5).wrappingMod(new alanStd.U8(2)), 1, "wrappingModU8 5 % 2 = 1"); -assert(new alanStd.U8(2).wrappingPow(new alanStd.U8(8)) == 0, "wrappingPowU8 2 ^ 8 = 0"); +assert.equal(new alanStd.U8(2).wrappingPow(new alanStd.U8(8)), 0, "wrappingPowU8 2 ^ 8 = 0"); -assert(new alanStd.U8(0).not() == 255, "notU8 0 = 255"); +assert.equal(new alanStd.U8(0).not(), 255, "notU8 0 = 255"); -assert(new alanStd.U8(128).wrappingShl(new alanStd.U8(1)) == 0, "wrappingShlU8 128 << 1 = 0"); +assert.equal(new alanStd.U8(128).wrappingShl(new alanStd.U8(1)), 0, "wrappingShlU8 128 << 1 = 0"); -assert(new alanStd.U8(128).wrappingShr(new alanStd.U8(1)) == 64, "wrappingShrU8 128 >> 1 = 64"); +assert.equal(new alanStd.U8(128).wrappingShr(new alanStd.U8(1)), 64, "wrappingShrU8 128 >> 1 = 64"); -assert(new alanStd.U8(128).rotateLeft(new alanStd.U8(1)) == 1, "rotateLeftU8 128 <<< 1 = 1"); -assert(new alanStd.U8(0b01010101).rotateLeft(new alanStd.U8(0)) == 0b01010101, "rotateLeftU8 0b01010101 <<< 0 = 0b01010101"); -assert(new alanStd.U8(0b01010101).rotateLeft(new alanStd.U8(1)) == 0b10101010, "rotateLeftU8 0b01010101 <<< 1 = 0b10101010"); -assert(new alanStd.U8(0b01010101).rotateLeft(new alanStd.U8(2)) == 0b01010101, "rotateLeftU8 0b01010101 <<< 2 = 0b01010101"); -assert(new alanStd.U8(0b01010101).rotateLeft(new alanStd.U8(3)) == 0b10101010, "rotateLeftU8 0b01010101 <<< 3 = 0b10101010"); -assert(new alanStd.U8(0b00000001).rotateLeft(new alanStd.U8(0)) == 0b00000001, "rotateLeftU8 0b00000001 <<< 0 = 0b00000001"); -assert(new alanStd.U8(0b00000001).rotateLeft(new alanStd.U8(1)) == 0b00000010, "rotateLeftU8 0b00000001 <<< 1 = 0b00000010"); -assert(new alanStd.U8(0b00000001).rotateLeft(new alanStd.U8(2)) == 0b00000100, "rotateLeftU8 0b00000001 <<< 2 = 0b00000100"); -assert(new alanStd.U8(0b00000001).rotateLeft(new alanStd.U8(3)) == 0b00001000, "rotateLeftU8 0b00000001 <<< 3 = 0b00001000"); -assert(new alanStd.U8(0b00000001).rotateLeft(new alanStd.U8(4)) == 0b00010000, "rotateLeftU8 0b00000001 <<< 4 = 0b00010000"); -assert(new alanStd.U8(0b00000001).rotateLeft(new alanStd.U8(5)) == 0b00100000, "rotateLeftU8 0b00000001 <<< 5 = 0b00100000"); -assert(new alanStd.U8(0b00000001).rotateLeft(new alanStd.U8(6)) == 0b01000000, "rotateLeftU8 0b00000001 <<< 6 = 0b01000000"); -assert(new alanStd.U8(0b00000001).rotateLeft(new alanStd.U8(7)) == 0b10000000, "rotateLeftU8 0b00000001 <<< 7 = 0b10000000"); -assert(new alanStd.U8(0b00000001).rotateLeft(new alanStd.U8(8)) == 0b00000001, "rotateLeftU8 0b00000001 <<< 8 = 0b00000001"); +assert.equal(new alanStd.U8(128).rotateLeft(new alanStd.U8(1)), 1, "rotateLeftU8 128 <<< 1 = 1"); +assert.equal(new alanStd.U8(0b01010101).rotateLeft(new alanStd.U8(0)), 0b01010101, "rotateLeftU8 0b01010101 <<< 0 = 0b01010101"); +assert.equal(new alanStd.U8(0b01010101).rotateLeft(new alanStd.U8(1)), 0b10101010, "rotateLeftU8 0b01010101 <<< 1 = 0b10101010"); +assert.equal(new alanStd.U8(0b01010101).rotateLeft(new alanStd.U8(2)), 0b01010101, "rotateLeftU8 0b01010101 <<< 2 = 0b01010101"); +assert.equal(new alanStd.U8(0b01010101).rotateLeft(new alanStd.U8(3)), 0b10101010, "rotateLeftU8 0b01010101 <<< 3 = 0b10101010"); +assert.equal(new alanStd.U8(0b00000001).rotateLeft(new alanStd.U8(0)), 0b00000001, "rotateLeftU8 0b00000001 <<< 0 = 0b00000001"); +assert.equal(new alanStd.U8(0b00000001).rotateLeft(new alanStd.U8(1)), 0b00000010, "rotateLeftU8 0b00000001 <<< 1 = 0b00000010"); +assert.equal(new alanStd.U8(0b00000001).rotateLeft(new alanStd.U8(2)), 0b00000100, "rotateLeftU8 0b00000001 <<< 2 = 0b00000100"); +assert.equal(new alanStd.U8(0b00000001).rotateLeft(new alanStd.U8(3)), 0b00001000, "rotateLeftU8 0b00000001 <<< 3 = 0b00001000"); +assert.equal(new alanStd.U8(0b00000001).rotateLeft(new alanStd.U8(4)), 0b00010000, "rotateLeftU8 0b00000001 <<< 4 = 0b00010000"); +assert.equal(new alanStd.U8(0b00000001).rotateLeft(new alanStd.U8(5)), 0b00100000, "rotateLeftU8 0b00000001 <<< 5 = 0b00100000"); +assert.equal(new alanStd.U8(0b00000001).rotateLeft(new alanStd.U8(6)), 0b01000000, "rotateLeftU8 0b00000001 <<< 6 = 0b01000000"); +assert.equal(new alanStd.U8(0b00000001).rotateLeft(new alanStd.U8(7)), 0b10000000, "rotateLeftU8 0b00000001 <<< 7 = 0b10000000"); +assert.equal(new alanStd.U8(0b00000001).rotateLeft(new alanStd.U8(8)), 0b00000001, "rotateLeftU8 0b00000001 <<< 8 = 0b00000001"); -assert(new alanStd.U8(64).rotateRight(new alanStd.U8(1)) == 32, "rotateRightU8 64 >>> 1 = 32"); -assert(new alanStd.U8(0b01010101).rotateRight(new alanStd.U8(0)) == 0b01010101, "rotateRightU8 0b01010101 >>> 0 = 0b01010101"); -assert(new alanStd.U8(0b01010101).rotateRight(new alanStd.U8(1)) == 0b10101010, "rotateRightU8 0b01010101 >>> 1 = 0b10101010"); -assert(new alanStd.U8(0b01010101).rotateRight(new alanStd.U8(2)) == 0b01010101, "rotateRightU8 0b01010101 >>> 2 = 0b01010101"); -assert(new alanStd.U8(0b01010101).rotateRight(new alanStd.U8(3)) == 0b10101010, "rotateRightU8 0b01010101 >>> 3 = 0b10101010"); -assert(new alanStd.U8(0b00000001).rotateRight(new alanStd.U8(0)) == 0b00000001, "rotateRightU8 0b00000001 >>> 0 = 0b00000001"); -assert(new alanStd.U8(0b00000001).rotateRight(new alanStd.U8(1)) == 0b10000000, "rotateRightU8 0b00000001 >>> 1 = 0b10000000"); -assert(new alanStd.U8(0b00000001).rotateRight(new alanStd.U8(2)) == 0b01000000, "rotateRightU8 0b00000001 >>> 2 = 0b01000000"); -assert(new alanStd.U8(0b00000001).rotateRight(new alanStd.U8(3)) == 0b00100000, "rotateRightU8 0b00000001 >>> 3 = 0b00100000"); -assert(new alanStd.U8(0b00000001).rotateRight(new alanStd.U8(4)) == 0b00010000, "rotateRightU8 0b00000001 >>> 4 = 0b00010000"); -assert(new alanStd.U8(0b00000001).rotateRight(new alanStd.U8(5)) == 0b00001000, "rotateRightU8 0b00000001 >>> 5 = 0b00001000"); -assert(new alanStd.U8(0b00000001).rotateRight(new alanStd.U8(6)) == 0b00000100, "rotateRightU8 0b00000001 >>> 6 = 0b00000100"); -assert(new alanStd.U8(0b00000001).rotateRight(new alanStd.U8(7)) == 0b00000010, "rotateRightU8 0b00000001 >>> 7 = 0b00000010"); -assert(new alanStd.U8(0b00000001).rotateRight(new alanStd.U8(8)) == 0b00000001, "rotateRightU8 0b00000001 >>> 8 = 0b00000008"); -assert(new alanStd.U8(100).rotateRight(new alanStd.U8(2)) == 25, "rotateRightU8 100 >>> 2 = 25"); +assert.equal(new alanStd.U8(64).rotateRight(new alanStd.U8(1)), 32, "rotateRightU8 64 >>> 1 = 32"); +assert.equal(new alanStd.U8(0b01010101).rotateRight(new alanStd.U8(0)), 0b01010101, "rotateRightU8 0b01010101 >>> 0 = 0b01010101"); +assert.equal(new alanStd.U8(0b01010101).rotateRight(new alanStd.U8(1)), 0b10101010, "rotateRightU8 0b01010101 >>> 1 = 0b10101010"); +assert.equal(new alanStd.U8(0b01010101).rotateRight(new alanStd.U8(2)), 0b01010101, "rotateRightU8 0b01010101 >>> 2 = 0b01010101"); +assert.equal(new alanStd.U8(0b01010101).rotateRight(new alanStd.U8(3)), 0b10101010, "rotateRightU8 0b01010101 >>> 3 = 0b10101010"); +assert.equal(new alanStd.U8(0b00000001).rotateRight(new alanStd.U8(0)), 0b00000001, "rotateRightU8 0b00000001 >>> 0 = 0b00000001"); +assert.equal(new alanStd.U8(0b00000001).rotateRight(new alanStd.U8(1)), 0b10000000, "rotateRightU8 0b00000001 >>> 1 = 0b10000000"); +assert.equal(new alanStd.U8(0b00000001).rotateRight(new alanStd.U8(2)), 0b01000000, "rotateRightU8 0b00000001 >>> 2 = 0b01000000"); +assert.equal(new alanStd.U8(0b00000001).rotateRight(new alanStd.U8(3)), 0b00100000, "rotateRightU8 0b00000001 >>> 3 = 0b00100000"); +assert.equal(new alanStd.U8(0b00000001).rotateRight(new alanStd.U8(4)), 0b00010000, "rotateRightU8 0b00000001 >>> 4 = 0b00010000"); +assert.equal(new alanStd.U8(0b00000001).rotateRight(new alanStd.U8(5)), 0b00001000, "rotateRightU8 0b00000001 >>> 5 = 0b00001000"); +assert.equal(new alanStd.U8(0b00000001).rotateRight(new alanStd.U8(6)), 0b00000100, "rotateRightU8 0b00000001 >>> 6 = 0b00000100"); +assert.equal(new alanStd.U8(0b00000001).rotateRight(new alanStd.U8(7)), 0b00000010, "rotateRightU8 0b00000001 >>> 7 = 0b00000010"); +assert.equal(new alanStd.U8(0b00000001).rotateRight(new alanStd.U8(8)), 0b00000001, "rotateRightU8 0b00000001 >>> 8 = 0b00000008"); +assert.equal(new alanStd.U8(100).rotateRight(new alanStd.U8(2)), 25, "rotateRightU8 100 >>> 2 = 25"); -assert(new alanStd.U16(1).wrappingAdd(new alanStd.U16(2)).val === 3, "wrappingAddU16 1 + 2 = 3"); -assert(new alanStd.U16(65_535).wrappingAdd(new alanStd.U16(1)).val === 0, "wrappingAddU16 65_535 + 1 = 0"); +assert.strictEqual(new alanStd.U16(1).wrappingAdd(new alanStd.U16(2)).val, 3, "wrappingAddU16 1 + 2 = 3"); +assert.strictEqual(new alanStd.U16(65_535).wrappingAdd(new alanStd.U16(1)).val, 0, "wrappingAddU16 65_535 + 1 = 0"); -assert(new alanStd.U16(1).wrappingSub(new alanStd.U16(2)).val === 65_535, "wrappingSubU16 1 - 2 = 65_535"); -assert(new alanStd.U16(65_535).wrappingSub(new alanStd.U16(1)).val === 65_534, "wrappingSubU16 65_535 - 1 = 65_534"); +assert.strictEqual(new alanStd.U16(1).wrappingSub(new alanStd.U16(2)).val, 65_535, "wrappingSubU16 1 - 2 = 65_535"); +assert.strictEqual(new alanStd.U16(65_535).wrappingSub(new alanStd.U16(1)).val, 65_534, "wrappingSubU16 65_535 - 1 = 65_534"); -assert(new alanStd.U16(256).wrappingMul(new alanStd.U16(256)).val === 0, "wrappingMulU16 256 * 256 = 0"); +assert.strictEqual(new alanStd.U16(256).wrappingMul(new alanStd.U16(256)).val, 0, "wrappingMulU16 256 * 256 = 0"); -assert(new alanStd.U16(128).wrappingDiv(new alanStd.U16(2)) == 64, "wrappingDivU16 128 / 2 = 64"); +assert.equal(new alanStd.U16(128).wrappingDiv(new alanStd.U16(2)), 64, "wrappingDivU16 128 / 2 = 64"); -assert(new alanStd.U16(5).wrappingMod(new alanStd.U16(2)) == 1, "wrappingModU16 5 % 2 = 1"); +assert.equal(new alanStd.U16(5).wrappingMod(new alanStd.U16(2)), 1, "wrappingModU16 5 % 2 = 1"); -assert(new alanStd.U16(2).wrappingPow(new alanStd.U16(16)) == 0, "wrappingPowU16 2 ^ 16 = 0"); +assert.equal(new alanStd.U16(2).wrappingPow(new alanStd.U16(16)), 0, "wrappingPowU16 2 ^ 16 = 0"); -assert(new alanStd.U16(0).not() == 65_535, "notU16 0 = 65_535"); +assert.equal(new alanStd.U16(0).not(), 65_535, "notU16 0 = 65_535"); -assert(new alanStd.U16(32_768).wrappingShl(new alanStd.U16(1)) == 0, "wrappingShlU16 32_768 << 1 = 0"); +assert.equal(new alanStd.U16(32_768).wrappingShl(new alanStd.U16(1)), 0, "wrappingShlU16 32_768 << 1 = 0"); -assert(new alanStd.U16(128).wrappingShr(new alanStd.U16(1)) == 64, "wrappingShrU16 128 >> 1 = 64"); +assert.equal(new alanStd.U16(128).wrappingShr(new alanStd.U16(1)), 64, "wrappingShrU16 128 >> 1 = 64"); -assert(new alanStd.U16(32_768).rotateLeft(new alanStd.U16(1)) == 1, "rotateLeftU16 128 <<< 1 = 1"); +assert.equal(new alanStd.U16(32_768).rotateLeft(new alanStd.U16(1)), 1, "rotateLeftU16 128 <<< 1 = 1"); -assert(new alanStd.U16(64).rotateRight(new alanStd.U16(1)) == 32, "rotateRightU16 64 >>> 1 = 128"); +assert.equal(new alanStd.U16(64).rotateRight(new alanStd.U16(1)), 32, "rotateRightU16 64 >>> 1 = 128"); -assert(new alanStd.U32(1).wrappingAdd(new alanStd.U32(2)).val === 3, "wrappingAddU32 1 + 2 = 3"); -assert(new alanStd.U32(4_294_967_295).wrappingAdd(new alanStd.U32(1)).val === 0, "wrappingAddU32 4_294_967_295 + 1 = 0"); +assert.strictEqual(new alanStd.U32(1).wrappingAdd(new alanStd.U32(2)).val, 3, "wrappingAddU32 1 + 2 = 3"); +assert.strictEqual(new alanStd.U32(4_294_967_295).wrappingAdd(new alanStd.U32(1)).val, 0, "wrappingAddU32 4_294_967_295 + 1 = 0"); -assert(new alanStd.U32(1).wrappingSub(new alanStd.U32(2)).val === 4_294_967_295, "wrappingSubU32 1 - 2 = 4_294_967_295"); -assert(new alanStd.U32(4_294_967_295).wrappingSub(new alanStd.U32(1)).val === 4_294_967_294, "wrappingSubU32 4_294_967_295 - 1 = 4_294_967_294"); +assert.strictEqual(new alanStd.U32(1).wrappingSub(new alanStd.U32(2)).val, 4_294_967_295, "wrappingSubU32 1 - 2 = 4_294_967_295"); +assert.strictEqual(new alanStd.U32(4_294_967_295).wrappingSub(new alanStd.U32(1)).val, 4_294_967_294, "wrappingSubU32 4_294_967_295 - 1 = 4_294_967_294"); -assert(new alanStd.U32(65_536).wrappingMul(new alanStd.U32(65_536)).val === 0, "wrappingMulU32 65_536 * 65_536 = 0"); +assert.strictEqual(new alanStd.U32(65_536).wrappingMul(new alanStd.U32(65_536)).val, 0, "wrappingMulU32 65_536 * 65_536 = 0"); -assert(new alanStd.U32(128).wrappingDiv(new alanStd.U32(2)) == 64, "wrappingDivU32 128 / 2 = 64"); +assert.equal(new alanStd.U32(128).wrappingDiv(new alanStd.U32(2)), 64, "wrappingDivU32 128 / 2 = 64"); -assert(new alanStd.U32(5).wrappingMod(new alanStd.U32(2)) == 1, "wrappingModU32 5 % 2 = 1"); +assert.equal(new alanStd.U32(5).wrappingMod(new alanStd.U32(2)), 1, "wrappingModU32 5 % 2 = 1"); -assert(new alanStd.U32(2).wrappingPow(new alanStd.U32(32)) == 0, "wrappingPowU32 2 ^ 32 = 0"); +assert.equal(new alanStd.U32(2).wrappingPow(new alanStd.U32(32)), 0, "wrappingPowU32 2 ^ 32 = 0"); -assert(new alanStd.U32(0).not() == 4_294_967_295, "notU32 0 = 4_294_967_295"); +assert.equal(new alanStd.U32(0).not(), 4_294_967_295, "notU32 0 = 4_294_967_295"); -assert(new alanStd.U32(2_147_483_648).wrappingShl(new alanStd.U32(1)) == 0, "wrappingShlU32 2_147_483_648 << 1 = 0"); +assert.equal(new alanStd.U32(2_147_483_648).wrappingShl(new alanStd.U32(1)), 0, "wrappingShlU32 2_147_483_648 << 1 = 0"); -assert(new alanStd.U32(128).wrappingShr(new alanStd.U32(1)) == 64, "wrappingShrU32 128 >> 1 = 64"); +assert.equal(new alanStd.U32(128).wrappingShr(new alanStd.U32(1)), 64, "wrappingShrU32 128 >> 1 = 64"); -assert(new alanStd.U32(2_147_483_648).rotateLeft(new alanStd.U32(1)) == 1, "rotateLeftU32 2_147_483_648 <<< 1 = 1"); +assert.equal(new alanStd.U32(2_147_483_648).rotateLeft(new alanStd.U32(1)), 1, "rotateLeftU32 2_147_483_648 <<< 1 = 1"); -assert(new alanStd.U32(64).rotateRight(new alanStd.U32(1)) == 32, "rotateRightU32 64 >>> 1 = 32"); +assert.equal(new alanStd.U32(64).rotateRight(new alanStd.U32(1)), 32, "rotateRightU32 64 >>> 1 = 32"); -assert(new alanStd.U64(1n).wrappingAdd(new alanStd.U64(2n)).val === 3n, "wrappingAddU64 1 + 2 = 3"); -assert(new alanStd.U64(18_446_744_073_709_551_615n).wrappingAdd(new alanStd.U64(1n)).val === 0n, "wrappingAddU64 18_446_744_073_709_551_615 + 1 = 0"); +assert.strictEqual(new alanStd.U64(1n).wrappingAdd(new alanStd.U64(2n)).val, 3n, "wrappingAddU64 1 + 2 = 3"); +assert.strictEqual(new alanStd.U64(18_446_744_073_709_551_615n).wrappingAdd(new alanStd.U64(1n)).val, 0n, "wrappingAddU64 18_446_744_073_709_551_615 + 1 = 0"); -assert(new alanStd.U64(1n).wrappingSub(new alanStd.U64(2n)).val === 18_446_744_073_709_551_615n, "wrappingSubU64 1 - 2 = 18_446_744_073_709_551_615"); -assert(new alanStd.U64(18_446_744_073_709_551_615n).wrappingSub(new alanStd.U64(1n)).val === 18_446_744_073_709_551_614n, "wrappingSubU64 18_446_744_073_709_551_615 - 1 = 18_446_744_073_709_551_614"); +assert.strictEqual(new alanStd.U64(1n).wrappingSub(new alanStd.U64(2n)).val, 18_446_744_073_709_551_615n, "wrappingSubU64 1 - 2 = 18_446_744_073_709_551_615"); +assert.strictEqual(new alanStd.U64(18_446_744_073_709_551_615n).wrappingSub(new alanStd.U64(1n)).val, 18_446_744_073_709_551_614n, "wrappingSubU64 18_446_744_073_709_551_615 - 1 = 18_446_744_073_709_551_614"); -assert(new alanStd.U64(4_294_967_296n).wrappingMul(new alanStd.U64(4_294_967_296n)).val === 0n, "wrappingMulU64 4_294_967_296 * 4_294_967_296 = 0"); +assert.strictEqual(new alanStd.U64(4_294_967_296n).wrappingMul(new alanStd.U64(4_294_967_296n)).val, 0n, "wrappingMulU64 4_294_967_296 * 4_294_967_296 = 0"); -assert(new alanStd.U64(128n).wrappingDiv(new alanStd.U64(2n)) == 64n, "wrappingDivU64 128 / 2 = 64"); +assert.equal(new alanStd.U64(128n).wrappingDiv(new alanStd.U64(2n)), 64n, "wrappingDivU64 128 / 2 = 64"); -assert(new alanStd.U64(5n).wrappingMod(new alanStd.U64(2n)) == 1n, "wrappingModU64 5 % 2 = 1"); +assert.equal(new alanStd.U64(5n).wrappingMod(new alanStd.U64(2n)), 1n, "wrappingModU64 5 % 2 = 1"); -assert(new alanStd.U64(2n).wrappingPow(new alanStd.U64(64n)) == 0n, "wrappingPowU64 2 ^ 64 = 0"); +assert.equal(new alanStd.U64(2n).wrappingPow(new alanStd.U64(64n)), 0n, "wrappingPowU64 2 ^ 64 = 0"); -assert(new alanStd.U64(0n).not() == 18_446_744_073_709_551_615n, "notU64 0 = 18_446_744_073_709_551_615"); +assert.equal(new alanStd.U64(0n).not(), 18_446_744_073_709_551_615n, "notU64 0 = 18_446_744_073_709_551_615"); -assert(new alanStd.U64(9_223_372_036_854_775_808n).wrappingShl(new alanStd.U64(1n)) == 0n, "wrappingShlU64 9_223_372_036_854_775_808 << 1 = 0"); +assert.equal(new alanStd.U64(9_223_372_036_854_775_808n).wrappingShl(new alanStd.U64(1n)), 0n, "wrappingShlU64 9_223_372_036_854_775_808 << 1 = 0"); -assert(new alanStd.U64(128n).wrappingShr(new alanStd.U64(1n)) == 64n, "wrappingShrU64 128 >> 1 = 64"); +assert.equal(new alanStd.U64(128n).wrappingShr(new alanStd.U64(1n)), 64n, "wrappingShrU64 128 >> 1 = 64"); -assert(new alanStd.U64(9_223_372_036_854_775_808n).rotateLeft(new alanStd.U64(1n)) == 1n, "rotateLeftU64 9_223_372_036_854_775_808 <<< 1 = 1"); -assert(new alanStd.U64(100n).rotateLeft(new alanStd.U64(2n)) == 400n, "rotateLeftU64 100 <<< 2 = 400"); +assert.equal(new alanStd.U64(9_223_372_036_854_775_808n).rotateLeft(new alanStd.U64(1n)), 1n, "rotateLeftU64 9_223_372_036_854_775_808 <<< 1 = 1"); +assert.equal(new alanStd.U64(100n).rotateLeft(new alanStd.U64(2n)), 400n, "rotateLeftU64 100 <<< 2 = 400"); + +assert.equal(new alanStd.U64(64n).rotateRight(new alanStd.U64(1n)), 32n, "rotateRightU64 64 >>> 1 = 32"); -assert(new alanStd.U64(64n).rotateRight(new alanStd.U64(1n)) == 32n, "rotateRightU64 64 >>> 1 = 32"); diff --git a/alan_std.test_gpgpu.js b/alan_std.test_gpgpu.js new file mode 100644 index 00000000..e485f680 --- /dev/null +++ b/alan_std.test_gpgpu.js @@ -0,0 +1,24 @@ +import assert from "node:assert"; +import { chromium } from 'playwright'; + +// Currently can only run this on Windows and MacOS. Chromium has WebGPU behind flags on Linux that +// Playwright doesn't support setting +(async () => { + const browser = await chromium.launch(); + const context = await browser.newContext(); + const page = await context.newPage(); + await page.goto('http://localhost:8080/alan_std.test.html'); + + assert.strictEqual(await page.evaluate(async () => { + let error = ""; + try { + await alanStd.gpu(); + } catch (e) { + error = e.message; + } + return error; + }), ""); + + await context.close(); + await browser.close(); +})(); diff --git a/package.json b/package.json index b1b2f302..7b55f4d9 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,11 @@ "description": "The Alan Language standard library backing for Javascript", "main": "alan_std.js", "scripts": { - "test": "node alan_std.test.js" + "test": "node alan_std.test.js", + "test-gpgpu": "node alan_std.test_gpgpu.js", + "start-server": "http-server &", + "stop-server": "killall http-server", + "bundle": "npx rollup alan_std.js --name alanStdBundle -p @rollup/plugin-node-resolve --file alanStdBundle.js" }, "type": "module", "repository": { @@ -22,5 +26,15 @@ "bugs": { "url": "https://github.com/alantech/alan/issues" }, - "homepage": "https://github.com/alantech/alan#readme" + "homepage": "https://github.com/alantech/alan#readme", + "dependencies": { + "uuid": "^10.0.0" + }, + "devDependencies": { + "@rollup/plugin-node-resolve": "15.x", + "@types/uuid": "^10.0.0", + "http-server": "^14.1.1", + "playwright": "^1.48.1", + "rollup": "4.x" + } } diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 00000000..fd01cba5 --- /dev/null +++ b/yarn.lock @@ -0,0 +1,533 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@rollup/plugin-node-resolve@15.x": + version "15.3.0" + resolved "https://registry.yarnpkg.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.3.0.tgz#efbb35515c9672e541c08d59caba2eff492a55d5" + integrity sha512-9eO5McEICxMzJpDW9OnMYSv4Sta3hmt7VtBFz5zR9273suNOydOyq/FrGeGy+KsTRFm8w0SLVhzig2ILFT63Ag== + dependencies: + "@rollup/pluginutils" "^5.0.1" + "@types/resolve" "1.20.2" + deepmerge "^4.2.2" + is-module "^1.0.0" + resolve "^1.22.1" + +"@rollup/pluginutils@^5.0.1": + version "5.1.3" + resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-5.1.3.tgz#3001bf1a03f3ad24457591f2c259c8e514e0dbdf" + integrity sha512-Pnsb6f32CD2W3uCaLZIzDmeFyQ2b8UWMFI7xtwUezpcGBDVDW6y9XgAWIlARiGAo6eNF5FK5aQTr0LFyNyqq5A== + dependencies: + "@types/estree" "^1.0.0" + estree-walker "^2.0.2" + picomatch "^4.0.2" + +"@rollup/rollup-android-arm-eabi@4.24.0": + version "4.24.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.24.0.tgz#1661ff5ea9beb362795304cb916049aba7ac9c54" + integrity sha512-Q6HJd7Y6xdB48x8ZNVDOqsbh2uByBhgK8PiQgPhwkIw/HC/YX5Ghq2mQY5sRMZWHb3VsFkWooUVOZHKr7DmDIA== + +"@rollup/rollup-android-arm64@4.24.0": + version "4.24.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.24.0.tgz#2ffaa91f1b55a0082b8a722525741aadcbd3971e" + integrity sha512-ijLnS1qFId8xhKjT81uBHuuJp2lU4x2yxa4ctFPtG+MqEE6+C5f/+X/bStmxapgmwLwiL3ih122xv8kVARNAZA== + +"@rollup/rollup-darwin-arm64@4.24.0": + version "4.24.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.24.0.tgz#627007221b24b8cc3063703eee0b9177edf49c1f" + integrity sha512-bIv+X9xeSs1XCk6DVvkO+S/z8/2AMt/2lMqdQbMrmVpgFvXlmde9mLcbQpztXm1tajC3raFDqegsH18HQPMYtA== + +"@rollup/rollup-darwin-x64@4.24.0": + version "4.24.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.24.0.tgz#0605506142b9e796c370d59c5984ae95b9758724" + integrity sha512-X6/nOwoFN7RT2svEQWUsW/5C/fYMBe4fnLK9DQk4SX4mgVBiTA9h64kjUYPvGQ0F/9xwJ5U5UfTbl6BEjaQdBQ== + +"@rollup/rollup-linux-arm-gnueabihf@4.24.0": + version "4.24.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.24.0.tgz#62dfd196d4b10c0c2db833897164d2d319ee0cbb" + integrity sha512-0KXvIJQMOImLCVCz9uvvdPgfyWo93aHHp8ui3FrtOP57svqrF/roSSR5pjqL2hcMp0ljeGlU4q9o/rQaAQ3AYA== + +"@rollup/rollup-linux-arm-musleabihf@4.24.0": + version "4.24.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.24.0.tgz#53ce72aeb982f1f34b58b380baafaf6a240fddb3" + integrity sha512-it2BW6kKFVh8xk/BnHfakEeoLPv8STIISekpoF+nBgWM4d55CZKc7T4Dx1pEbTnYm/xEKMgy1MNtYuoA8RFIWw== + +"@rollup/rollup-linux-arm64-gnu@4.24.0": + version "4.24.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.24.0.tgz#1632990f62a75c74f43e4b14ab3597d7ed416496" + integrity sha512-i0xTLXjqap2eRfulFVlSnM5dEbTVque/3Pi4g2y7cxrs7+a9De42z4XxKLYJ7+OhE3IgxvfQM7vQc43bwTgPwA== + +"@rollup/rollup-linux-arm64-musl@4.24.0": + version "4.24.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.24.0.tgz#8c03a996efb41e257b414b2e0560b7a21f2d9065" + integrity sha512-9E6MKUJhDuDh604Qco5yP/3qn3y7SLXYuiC0Rpr89aMScS2UAmK1wHP2b7KAa1nSjWJc/f/Lc0Wl1L47qjiyQw== + +"@rollup/rollup-linux-powerpc64le-gnu@4.24.0": + version "4.24.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.24.0.tgz#5b98729628d5bcc8f7f37b58b04d6845f85c7b5d" + integrity sha512-2XFFPJ2XMEiF5Zi2EBf4h73oR1V/lycirxZxHZNc93SqDN/IWhYYSYj8I9381ikUFXZrz2v7r2tOVk2NBwxrWw== + +"@rollup/rollup-linux-riscv64-gnu@4.24.0": + version "4.24.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.24.0.tgz#48e42e41f4cabf3573cfefcb448599c512e22983" + integrity sha512-M3Dg4hlwuntUCdzU7KjYqbbd+BLq3JMAOhCKdBE3TcMGMZbKkDdJ5ivNdehOssMCIokNHFOsv7DO4rlEOfyKpg== + +"@rollup/rollup-linux-s390x-gnu@4.24.0": + version "4.24.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.24.0.tgz#e0b4f9a966872cb7d3e21b9e412a4b7efd7f0b58" + integrity sha512-mjBaoo4ocxJppTorZVKWFpy1bfFj9FeCMJqzlMQGjpNPY9JwQi7OuS1axzNIk0nMX6jSgy6ZURDZ2w0QW6D56g== + +"@rollup/rollup-linux-x64-gnu@4.24.0": + version "4.24.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.24.0.tgz#78144741993100f47bd3da72fce215e077ae036b" + integrity sha512-ZXFk7M72R0YYFN5q13niV0B7G8/5dcQ9JDp8keJSfr3GoZeXEoMHP/HlvqROA3OMbMdfr19IjCeNAnPUG93b6A== + +"@rollup/rollup-linux-x64-musl@4.24.0": + version "4.24.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.24.0.tgz#d9fe32971883cd1bd858336bd33a1c3ca6146127" + integrity sha512-w1i+L7kAXZNdYl+vFvzSZy8Y1arS7vMgIy8wusXJzRrPyof5LAb02KGr1PD2EkRcl73kHulIID0M501lN+vobQ== + +"@rollup/rollup-win32-arm64-msvc@4.24.0": + version "4.24.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.24.0.tgz#71fa3ea369316db703a909c790743972e98afae5" + integrity sha512-VXBrnPWgBpVDCVY6XF3LEW0pOU51KbaHhccHw6AS6vBWIC60eqsH19DAeeObl+g8nKAz04QFdl/Cefta0xQtUQ== + +"@rollup/rollup-win32-ia32-msvc@4.24.0": + version "4.24.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.24.0.tgz#653f5989a60658e17d7576a3996deb3902e342e2" + integrity sha512-xrNcGDU0OxVcPTH/8n/ShH4UevZxKIO6HJFK0e15XItZP2UcaiLFd5kiX7hJnqCbSztUF8Qot+JWBC/QXRPYWQ== + +"@rollup/rollup-win32-x64-msvc@4.24.0": + version "4.24.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.24.0.tgz#0574d7e87b44ee8511d08cc7f914bcb802b70818" + integrity sha512-fbMkAF7fufku0N2dE5TBXcNlg0pt0cJue4xBRE2Qc5Vqikxr4VCgKj/ht6SMdFcOacVA9rqF70APJ8RN/4vMJw== + +"@types/estree@1.0.6", "@types/estree@^1.0.0": + version "1.0.6" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.6.tgz#628effeeae2064a1b4e79f78e81d87b7e5fc7b50" + integrity sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw== + +"@types/resolve@1.20.2": + version "1.20.2" + resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-1.20.2.tgz#97d26e00cd4a0423b4af620abecf3e6f442b7975" + integrity sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q== + +"@types/uuid@^10.0.0": + version "10.0.0" + resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-10.0.0.tgz#e9c07fe50da0f53dc24970cca94d619ff03f6f6d" + integrity sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ== + +ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +async@^2.6.4: + version "2.6.4" + resolved "https://registry.yarnpkg.com/async/-/async-2.6.4.tgz#706b7ff6084664cd7eae713f6f965433b5504221" + integrity sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA== + dependencies: + lodash "^4.17.14" + +basic-auth@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/basic-auth/-/basic-auth-2.0.1.tgz#b998279bf47ce38344b4f3cf916d4679bbf51e3a" + integrity sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg== + dependencies: + safe-buffer "5.1.2" + +call-bind@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9" + integrity sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w== + dependencies: + es-define-property "^1.0.0" + es-errors "^1.3.0" + function-bind "^1.1.2" + get-intrinsic "^1.2.4" + set-function-length "^1.2.1" + +chalk@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +corser@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/corser/-/corser-2.0.1.tgz#8eda252ecaab5840dcd975ceb90d9370c819ff87" + integrity sha512-utCYNzRSQIZNPIcGZdQc92UVJYAhtGAteCFg0yRaFm8f0P+CPtyGyHXJcGXnffjCybUCEx3FQ2G7U3/o9eIkVQ== + +debug@^3.2.7: + version "3.2.7" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" + integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== + dependencies: + ms "^2.1.1" + +deepmerge@^4.2.2: + version "4.3.1" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a" + integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A== + +define-data-property@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e" + integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A== + dependencies: + es-define-property "^1.0.0" + es-errors "^1.3.0" + gopd "^1.0.1" + +es-define-property@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.0.tgz#c7faefbdff8b2696cf5f46921edfb77cc4ba3845" + integrity sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ== + dependencies: + get-intrinsic "^1.2.4" + +es-errors@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" + integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== + +estree-walker@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac" + integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w== + +eventemitter3@^4.0.0: + version "4.0.7" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" + integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== + +follow-redirects@^1.0.0: + version "1.15.9" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.9.tgz#a604fa10e443bf98ca94228d9eebcc2e8a2c8ee1" + integrity sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ== + +fsevents@2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" + integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== + +fsevents@~2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== + +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + +get-intrinsic@^1.1.3, get-intrinsic@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd" + integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ== + dependencies: + es-errors "^1.3.0" + function-bind "^1.1.2" + has-proto "^1.0.1" + has-symbols "^1.0.3" + hasown "^2.0.0" + +gopd@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" + integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== + dependencies: + get-intrinsic "^1.1.3" + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +has-property-descriptors@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854" + integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg== + dependencies: + es-define-property "^1.0.0" + +has-proto@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.3.tgz#b31ddfe9b0e6e9914536a6ab286426d0214f77fd" + integrity sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q== + +has-symbols@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" + integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== + +hasown@^2.0.0, hasown@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" + integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== + dependencies: + function-bind "^1.1.2" + +he@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" + integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== + +html-encoding-sniffer@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz#2cb1a8cf0db52414776e5b2a7a04d5dd98158de9" + integrity sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA== + dependencies: + whatwg-encoding "^2.0.0" + +http-proxy@^1.18.1: + version "1.18.1" + resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549" + integrity sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ== + dependencies: + eventemitter3 "^4.0.0" + follow-redirects "^1.0.0" + requires-port "^1.0.0" + +http-server@^14.1.1: + version "14.1.1" + resolved "https://registry.yarnpkg.com/http-server/-/http-server-14.1.1.tgz#d60fbb37d7c2fdff0f0fbff0d0ee6670bd285e2e" + integrity sha512-+cbxadF40UXd9T01zUHgA+rlo2Bg1Srer4+B4NwIHdaGxAGGv59nYRnGGDJ9LBk7alpS0US+J+bLLdQOOkJq4A== + dependencies: + basic-auth "^2.0.1" + chalk "^4.1.2" + corser "^2.0.1" + he "^1.2.0" + html-encoding-sniffer "^3.0.0" + http-proxy "^1.18.1" + mime "^1.6.0" + minimist "^1.2.6" + opener "^1.5.1" + portfinder "^1.0.28" + secure-compare "3.0.1" + union "~0.5.0" + url-join "^4.0.1" + +iconv-lite@0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" + integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== + dependencies: + safer-buffer ">= 2.1.2 < 3.0.0" + +is-core-module@^2.13.0: + version "2.15.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.15.1.tgz#a7363a25bee942fefab0de13bf6aa372c82dcc37" + integrity sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ== + dependencies: + hasown "^2.0.2" + +is-module@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591" + integrity sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g== + +lodash@^4.17.14: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + +mime@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== + +minimist@^1.2.6: + version "1.2.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== + +mkdirp@^0.5.6: + version "0.5.6" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" + integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== + dependencies: + minimist "^1.2.6" + +ms@^2.1.1: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +object-inspect@^1.13.1: + version "1.13.2" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.2.tgz#dea0088467fb991e67af4058147a24824a3043ff" + integrity sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g== + +opener@^1.5.1: + version "1.5.2" + resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.2.tgz#5d37e1f35077b9dcac4301372271afdeb2a13598" + integrity sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A== + +path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +picomatch@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.2.tgz#77c742931e8f3b8820946c76cd0c1f13730d1dab" + integrity sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg== + +playwright-core@1.48.1: + version "1.48.1" + resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.48.1.tgz#5fe28fb9a9326dae88d4608c35e819163cceeb23" + integrity sha512-Yw/t4VAFX/bBr1OzwCuOMZkY1Cnb4z/doAFSwf4huqAGWmf9eMNjmK7NiOljCdLmxeRYcGPPmcDgU0zOlzP0YA== + +playwright@^1.48.1: + version "1.48.1" + resolved "https://registry.yarnpkg.com/playwright/-/playwright-1.48.1.tgz#2a920cfbec4572c84789e757d8b044baaed49435" + integrity sha512-j8CiHW/V6HxmbntOfyB4+T/uk08tBy6ph0MpBXwuoofkSnLmlfdYNNkFTYD6ofzzlSqLA1fwH4vwvVFvJgLN0w== + dependencies: + playwright-core "1.48.1" + optionalDependencies: + fsevents "2.3.2" + +portfinder@^1.0.28: + version "1.0.32" + resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.32.tgz#2fe1b9e58389712429dc2bea5beb2146146c7f81" + integrity sha512-on2ZJVVDXRADWE6jnQaX0ioEylzgBpQk8r55NE4wjXW1ZxO+BgDlY6DXwj20i0V8eB4SenDQ00WEaxfiIQPcxg== + dependencies: + async "^2.6.4" + debug "^3.2.7" + mkdirp "^0.5.6" + +qs@^6.4.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.13.0.tgz#6ca3bd58439f7e245655798997787b0d88a51906" + integrity sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg== + dependencies: + side-channel "^1.0.6" + +requires-port@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" + integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ== + +resolve@^1.22.1: + version "1.22.8" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" + integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== + dependencies: + is-core-module "^2.13.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +rollup@4.x: + version "4.24.0" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.24.0.tgz#c14a3576f20622ea6a5c9cad7caca5e6e9555d05" + integrity sha512-DOmrlGSXNk1DM0ljiQA+i+o0rSLhtii1je5wgk60j49d1jHT5YYttBv1iWOnYSTG+fZZESUOSNiAl89SIet+Cg== + dependencies: + "@types/estree" "1.0.6" + optionalDependencies: + "@rollup/rollup-android-arm-eabi" "4.24.0" + "@rollup/rollup-android-arm64" "4.24.0" + "@rollup/rollup-darwin-arm64" "4.24.0" + "@rollup/rollup-darwin-x64" "4.24.0" + "@rollup/rollup-linux-arm-gnueabihf" "4.24.0" + "@rollup/rollup-linux-arm-musleabihf" "4.24.0" + "@rollup/rollup-linux-arm64-gnu" "4.24.0" + "@rollup/rollup-linux-arm64-musl" "4.24.0" + "@rollup/rollup-linux-powerpc64le-gnu" "4.24.0" + "@rollup/rollup-linux-riscv64-gnu" "4.24.0" + "@rollup/rollup-linux-s390x-gnu" "4.24.0" + "@rollup/rollup-linux-x64-gnu" "4.24.0" + "@rollup/rollup-linux-x64-musl" "4.24.0" + "@rollup/rollup-win32-arm64-msvc" "4.24.0" + "@rollup/rollup-win32-ia32-msvc" "4.24.0" + "@rollup/rollup-win32-x64-msvc" "4.24.0" + fsevents "~2.3.2" + +safe-buffer@5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +"safer-buffer@>= 2.1.2 < 3.0.0": + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +secure-compare@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/secure-compare/-/secure-compare-3.0.1.tgz#f1a0329b308b221fae37b9974f3d578d0ca999e3" + integrity sha512-AckIIV90rPDcBcglUwXPF3kg0P0qmPsPXAj6BBEENQE1p5yA1xfmDJzfi1Tappj37Pv2mVbKpL3Z1T+Nn7k1Qw== + +set-function-length@^1.2.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449" + integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg== + dependencies: + define-data-property "^1.1.4" + es-errors "^1.3.0" + function-bind "^1.1.2" + get-intrinsic "^1.2.4" + gopd "^1.0.1" + has-property-descriptors "^1.0.2" + +side-channel@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.6.tgz#abd25fb7cd24baf45466406b1096b7831c9215f2" + integrity sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA== + dependencies: + call-bind "^1.0.7" + es-errors "^1.3.0" + get-intrinsic "^1.2.4" + object-inspect "^1.13.1" + +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + +union@~0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/union/-/union-0.5.0.tgz#b2c11be84f60538537b846edb9ba266ba0090075" + integrity sha512-N6uOhuW6zO95P3Mel2I2zMsbsanvvtgn6jVqJv4vbVcz/JN0OkL9suomjQGmWtxJQXOCqUJvquc1sMeNz/IwlA== + dependencies: + qs "^6.4.0" + +url-join@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/url-join/-/url-join-4.0.1.tgz#b642e21a2646808ffa178c4c5fda39844e12cde7" + integrity sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA== + +uuid@^10.0.0: + version "10.0.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-10.0.0.tgz#5a95aa454e6e002725c79055fd42aaba30ca6294" + integrity sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ== + +whatwg-encoding@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz#e7635f597fd87020858626805a2729fa7698ac53" + integrity sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg== + dependencies: + iconv-lite "0.6.3" From 3b260d264bbcf58df766dd1a50e29411803ae077 Mon Sep 17 00:00:00 2001 From: David Ellis Date: Thu, 24 Oct 2024 12:34:20 -0500 Subject: [PATCH 02/20] Add install step to 'normal' test flow, too --- .github/workflows/node.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/node.yml b/.github/workflows/node.yml index cb9209a4..5dd00abb 100644 --- a/.github/workflows/node.yml +++ b/.github/workflows/node.yml @@ -11,6 +11,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + - name: Install deps + run: yarn - name: Run tests run: npm test test-js-gpgpu: From b89c6b1970f1536e283a79f11bb4c1499dac027b Mon Sep 17 00:00:00 2001 From: David Ellis Date: Thu, 24 Oct 2024 12:50:51 -0500 Subject: [PATCH 03/20] Make sure playwright is set up on the test machine --- .github/workflows/node.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/node.yml b/.github/workflows/node.yml index 5dd00abb..28352470 100644 --- a/.github/workflows/node.yml +++ b/.github/workflows/node.yml @@ -23,6 +23,8 @@ jobs: run: yarn - name: Bundle stdlib run: yarn bundle + - name: Set up Playwright + run: yarn playwright install - name: Start webserver run: yarn start-server - name: Test GPGPU From 8b009909bfaee350bc281a7e17e6d7c94ea66287 Mon Sep 17 00:00:00 2001 From: David Ellis Date: Thu, 24 Oct 2024 13:14:57 -0500 Subject: [PATCH 04/20] Always run the http server shutdown logic --- .github/workflows/node.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/node.yml b/.github/workflows/node.yml index 28352470..3c0bbb52 100644 --- a/.github/workflows/node.yml +++ b/.github/workflows/node.yml @@ -30,4 +30,5 @@ jobs: - name: Test GPGPU run: yarn test-gpgpu - name: Stop webserver + if: always() run: yarn stop-server From c8fc295e5543400593cdf3bf257b18daa80b4ccb Mon Sep 17 00:00:00 2001 From: David Ellis Date: Thu, 24 Oct 2024 13:28:55 -0500 Subject: [PATCH 05/20] killall doesn't work right on OSX, manual pid tracking is needed --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 7b55f4d9..297a2d19 100644 --- a/package.json +++ b/package.json @@ -6,8 +6,8 @@ "scripts": { "test": "node alan_std.test.js", "test-gpgpu": "node alan_std.test_gpgpu.js", - "start-server": "http-server &", - "stop-server": "killall http-server", + "start-server": "bash -c 'http-server &\necho $! > ./http-server.pid'", + "stop-server": "bash -c 'kill `cat ./http-server.pid`\n rm ./http-server.pid'", "bundle": "npx rollup alan_std.js --name alanStdBundle -p @rollup/plugin-node-resolve --file alanStdBundle.js" }, "type": "module", From 0fa93027c10ab1cf12d81cff7964b8aa71f28893 Mon Sep 17 00:00:00 2001 From: David Ellis Date: Thu, 24 Oct 2024 14:20:31 -0500 Subject: [PATCH 06/20] Add more tests for the GPGPU functions --- alan_std.js | 2 +- alan_std.test_gpgpu.js | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/alan_std.js b/alan_std.js index d63c1007..73e4259f 100644 --- a/alan_std.js +++ b/alan_std.js @@ -440,7 +440,7 @@ export function bufferlen(b) { } export function bufferid(b) { - return b.label; + return new Str(b.label); } export class GPGPU { diff --git a/alan_std.test_gpgpu.js b/alan_std.test_gpgpu.js index e485f680..e28de718 100644 --- a/alan_std.test_gpgpu.js +++ b/alan_std.test_gpgpu.js @@ -19,6 +19,40 @@ import { chromium } from 'playwright'; return error; }), ""); + assert.strictEqual(await page.evaluate(async () => { + let b = await alanStd.createEmptyBuffer(alanStd.storageBufferType(), 4); + return alanStd.bufferlen(b).valueOf(); + }), 4); + + assert(await page.evaluate(async () => { + let b = await alanStd.createEmptyBuffer(alanStd.storageBufferType(), 4); + return alanStd.bufferid(b).valueOf(); + }).startsWith("buffer_")); + + assert.strictEqual(await page.evaluate(async () => { + let b = await alanStd.createBufferInit(alanStd.storageBufferType(), [1, 2, 3, 4]); + let v = await alanStd.readBuffer(b); + return v.map((i) => i.valueOf()); + }), [1, 2, 3, 4]); + + assert.strictEqual(await page.evaluate(async () => { + let b = await alanStd.createBufferInit(alanStd.storageBufferType(), [ + new alanStd.I32(1), + new alanStd.I32(2), + new alanStd.I32(3), + new alanStd.I32(4) + ]); + let v = await alanStd.readBuffer(b); + return v.map((i) => i.valueOf()); + }), [1, 2, 3, 4]); + + assert.strictEqual(await page.evaluate(async () => { + let b = await alanStd.createBufferInit(alanStd.storageBufferType(), [1, 2, 3, 4]); + await alanStd.replaceBuffer(b, [5, 6, 7, 8]); + let v = await alanStd.readBuffer(b); + return v.map((i) => i.valueOf()); + }), [5, 6, 7, 8]); + await context.close(); await browser.close(); })(); From 3945f4b1632bd2c47ea5e30fdc11df2a16d804bf Mon Sep 17 00:00:00 2001 From: David Ellis Date: Thu, 24 Oct 2024 14:25:12 -0500 Subject: [PATCH 07/20] Fix bug in empty buffer allocation --- alan_std.js | 2 +- alan_std.test_gpgpu.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/alan_std.js b/alan_std.js index 73e4259f..ad26e79f 100644 --- a/alan_std.js +++ b/alan_std.js @@ -416,7 +416,7 @@ export async function createBufferInit(usage, vals) { export async function createEmptyBuffer(usage, size) { let g = await gpu(); let b = await g.device.createBuffer({ - size, + size: size.valueOf() * 4, usage, label: `buffer_${uuidv4().replaceAll('-', '_')}`, }); diff --git a/alan_std.test_gpgpu.js b/alan_std.test_gpgpu.js index e28de718..fe6eea73 100644 --- a/alan_std.test_gpgpu.js +++ b/alan_std.test_gpgpu.js @@ -22,7 +22,7 @@ import { chromium } from 'playwright'; assert.strictEqual(await page.evaluate(async () => { let b = await alanStd.createEmptyBuffer(alanStd.storageBufferType(), 4); return alanStd.bufferlen(b).valueOf(); - }), 4); + }), 4n); assert(await page.evaluate(async () => { let b = await alanStd.createEmptyBuffer(alanStd.storageBufferType(), 4); From fd5afe4cfe46ddbe6a3abdf57e5add971316804b Mon Sep 17 00:00:00 2001 From: David Ellis Date: Thu, 24 Oct 2024 14:27:23 -0500 Subject: [PATCH 08/20] Forgot to wrap the 'await' statement in parens to call a method on the result --- alan_std.test_gpgpu.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/alan_std.test_gpgpu.js b/alan_std.test_gpgpu.js index fe6eea73..e02bb74b 100644 --- a/alan_std.test_gpgpu.js +++ b/alan_std.test_gpgpu.js @@ -24,10 +24,10 @@ import { chromium } from 'playwright'; return alanStd.bufferlen(b).valueOf(); }), 4n); - assert(await page.evaluate(async () => { + assert((await page.evaluate(async () => { let b = await alanStd.createEmptyBuffer(alanStd.storageBufferType(), 4); return alanStd.bufferid(b).valueOf(); - }).startsWith("buffer_")); + })).startsWith("buffer_")); assert.strictEqual(await page.evaluate(async () => { let b = await alanStd.createBufferInit(alanStd.storageBufferType(), [1, 2, 3, 4]); From 378b25457394ca5f6d7ee1f817418a9316477db5 Mon Sep 17 00:00:00 2001 From: David Ellis Date: Thu, 24 Oct 2024 14:35:15 -0500 Subject: [PATCH 09/20] Attempt to fix buffer reading --- alan_std.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/alan_std.js b/alan_std.js index ad26e79f..25871bb8 100644 --- a/alan_std.js +++ b/alan_std.js @@ -494,13 +494,13 @@ export async function readBuffer(b) { encoder.copyBufferToBuffer(b, 0, tempBuffer, 0, b.size); g.queue.submit([encoder.finish()]); await tempBuffer.mapAsync(GPUMapMode.READ); - let data = tempBuffer.slice(0); - tempBuffer.unmap(); + let data = tempBuffer.getMappedRange(0, b.size); let vals = new Int32Array(data); let out = []; for (let i = 0; i < vals.length; i++) { out[i] = new I32(vals[i]); } + tempBuffer.unmap(); return out; } From 4e1fd6275a30e9bf5b2b2e12fe04946ca078f39c Mon Sep 17 00:00:00 2001 From: David Ellis Date: Thu, 24 Oct 2024 14:51:34 -0500 Subject: [PATCH 10/20] Fix replaceBuffer and use deepEqual for the array comparisons --- alan_std.js | 2 +- alan_std.test_gpgpu.js | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/alan_std.js b/alan_std.js index 25871bb8..ab482d53 100644 --- a/alan_std.js +++ b/alan_std.js @@ -509,7 +509,7 @@ export async function replaceBuffer(b, v) { return new AlanError("The input array is not the same size as the buffer"); } await b.mapAsync(GPUMapMode.WRITE); - let data = b.slice(0); + let data = b.getMappedRange(0, b.size); for (let i = 0; i < v.length; i++) { data[i] = v[i].valueOf(); } diff --git a/alan_std.test_gpgpu.js b/alan_std.test_gpgpu.js index e02bb74b..a4392d7b 100644 --- a/alan_std.test_gpgpu.js +++ b/alan_std.test_gpgpu.js @@ -29,13 +29,13 @@ import { chromium } from 'playwright'; return alanStd.bufferid(b).valueOf(); })).startsWith("buffer_")); - assert.strictEqual(await page.evaluate(async () => { + assert.deepEqual(await page.evaluate(async () => { let b = await alanStd.createBufferInit(alanStd.storageBufferType(), [1, 2, 3, 4]); let v = await alanStd.readBuffer(b); return v.map((i) => i.valueOf()); }), [1, 2, 3, 4]); - assert.strictEqual(await page.evaluate(async () => { + assert.deepEqual(await page.evaluate(async () => { let b = await alanStd.createBufferInit(alanStd.storageBufferType(), [ new alanStd.I32(1), new alanStd.I32(2), @@ -46,7 +46,7 @@ import { chromium } from 'playwright'; return v.map((i) => i.valueOf()); }), [1, 2, 3, 4]); - assert.strictEqual(await page.evaluate(async () => { + assert.deepEqual(await page.evaluate(async () => { let b = await alanStd.createBufferInit(alanStd.storageBufferType(), [1, 2, 3, 4]); await alanStd.replaceBuffer(b, [5, 6, 7, 8]); let v = await alanStd.readBuffer(b); From 699922e9254aa64359735564cf2dbd42ab2edbac Mon Sep 17 00:00:00 2001 From: David Ellis Date: Thu, 24 Oct 2024 14:56:42 -0500 Subject: [PATCH 11/20] More fixes for read and replace buffer functions --- alan_std.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/alan_std.js b/alan_std.js index ab482d53..1af6b9cd 100644 --- a/alan_std.js +++ b/alan_std.js @@ -501,6 +501,7 @@ export async function readBuffer(b) { out[i] = new I32(vals[i]); } tempBuffer.unmap(); + tempBuffer.destroy(); return out; } @@ -508,10 +509,16 @@ export async function replaceBuffer(b, v) { if (v.length != bufferlen(b)) { return new AlanError("The input array is not the same size as the buffer"); } + let tempBuffer = await createEmptyBuffer(mapWriteBufferType(), b.size); await b.mapAsync(GPUMapMode.WRITE); let data = b.getMappedRange(0, b.size); for (let i = 0; i < v.length; i++) { data[i] = v[i].valueOf(); } b.unmap(); + let g = await gpu(); + let encoder = g.device.createCommandEncoder(); + encoder.copyBufferToBuffer(tempBuffer, 0, b, 0, b.size); + g.queue.submit([encoder.finish()]); + tempBuffer.destroy(); } From 947428120f607b1bc34e9c1ba20db4cd026b5bea Mon Sep 17 00:00:00 2001 From: David Ellis Date: Thu, 24 Oct 2024 15:01:41 -0500 Subject: [PATCH 12/20] More read/replace fixes --- alan_std.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/alan_std.js b/alan_std.js index 1af6b9cd..d6b90931 100644 --- a/alan_std.js +++ b/alan_std.js @@ -489,7 +489,7 @@ export async function gpuRun(gg) { export async function readBuffer(b) { let g = await gpu(); - let tempBuffer = await createEmptyBuffer(mapReadBufferType(), b.size); + let tempBuffer = await createEmptyBuffer(mapReadBufferType(), b.size / 4); let encoder = g.device.createCommandEncoder(); encoder.copyBufferToBuffer(b, 0, tempBuffer, 0, b.size); g.queue.submit([encoder.finish()]); @@ -509,9 +509,9 @@ export async function replaceBuffer(b, v) { if (v.length != bufferlen(b)) { return new AlanError("The input array is not the same size as the buffer"); } - let tempBuffer = await createEmptyBuffer(mapWriteBufferType(), b.size); - await b.mapAsync(GPUMapMode.WRITE); - let data = b.getMappedRange(0, b.size); + let tempBuffer = await createEmptyBuffer(mapWriteBufferType(), b.size / 4); + await tempBuffer.mapAsync(GPUMapMode.WRITE); + let data = tempBuffer.getMappedRange(0, tempBuffer.size); for (let i = 0; i < v.length; i++) { data[i] = v[i].valueOf(); } From 6b9fef6f9cbbbf8a05043b1c5fcf1f6e1854fedb Mon Sep 17 00:00:00 2001 From: David Ellis Date: Thu, 24 Oct 2024 15:04:34 -0500 Subject: [PATCH 13/20] How did that not barf in a different place --- alan_std.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/alan_std.js b/alan_std.js index d6b90931..fa52e842 100644 --- a/alan_std.js +++ b/alan_std.js @@ -515,7 +515,7 @@ export async function replaceBuffer(b, v) { for (let i = 0; i < v.length; i++) { data[i] = v[i].valueOf(); } - b.unmap(); + tempBuffer.unmap(); let g = await gpu(); let encoder = g.device.createCommandEncoder(); encoder.copyBufferToBuffer(tempBuffer, 0, b, 0, b.size); From d19226309055f140ccf2561ac11ec2d0c104106a Mon Sep 17 00:00:00 2001 From: David Ellis Date: Thu, 24 Oct 2024 15:09:20 -0500 Subject: [PATCH 14/20] Does destroying the temp buffer 'too soon' affect things? --- alan_std.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/alan_std.js b/alan_std.js index fa52e842..e242f90a 100644 --- a/alan_std.js +++ b/alan_std.js @@ -520,5 +520,5 @@ export async function replaceBuffer(b, v) { let encoder = g.device.createCommandEncoder(); encoder.copyBufferToBuffer(tempBuffer, 0, b, 0, b.size); g.queue.submit([encoder.finish()]); - tempBuffer.destroy(); + //tempBuffer.destroy(); } From 144328b12448db2d7f6403f324804ce0c9c245f5 Mon Sep 17 00:00:00 2001 From: David Ellis Date: Thu, 24 Oct 2024 15:12:47 -0500 Subject: [PATCH 15/20] Trying a different tack on the replaceBuffer function --- alan_std.js | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/alan_std.js b/alan_std.js index e242f90a..bdb5ff2f 100644 --- a/alan_std.js +++ b/alan_std.js @@ -509,16 +509,10 @@ export async function replaceBuffer(b, v) { if (v.length != bufferlen(b)) { return new AlanError("The input array is not the same size as the buffer"); } - let tempBuffer = await createEmptyBuffer(mapWriteBufferType(), b.size / 4); - await tempBuffer.mapAsync(GPUMapMode.WRITE); - let data = tempBuffer.getMappedRange(0, tempBuffer.size); - for (let i = 0; i < v.length; i++) { - data[i] = v[i].valueOf(); - } - tempBuffer.unmap(); + let tempBuffer = await createBufferInit(mapWriteBufferType(), v); let g = await gpu(); let encoder = g.device.createCommandEncoder(); encoder.copyBufferToBuffer(tempBuffer, 0, b, 0, b.size); g.queue.submit([encoder.finish()]); - //tempBuffer.destroy(); + tempBuffer.destroy(); } From c4ac3ea58cb35f0e502b00a5719464e2a392b068 Mon Sep 17 00:00:00 2001 From: David Ellis Date: Thu, 24 Oct 2024 15:18:55 -0500 Subject: [PATCH 16/20] Add last GPGPU test, actual execution of a shader on the GPU --- alan_std.test_gpgpu.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/alan_std.test_gpgpu.js b/alan_std.test_gpgpu.js index a4392d7b..1e112cb1 100644 --- a/alan_std.test_gpgpu.js +++ b/alan_std.test_gpgpu.js @@ -53,6 +53,24 @@ import { chromium } from 'playwright'; return v.map((i) => i.valueOf()); }), [5, 6, 7, 8]); + assert.deepEqual(await page.evaluate(async () => { + let b = await alanStd.createBufferInit(alanStd.storageBufferType(), [2, 2, 2, 2]); + let plan = new alanStd.GPGPU(` + @group(0) + @binding(0) + var vals: array; + + @compute + @workgroup_size(1) + fn main(@builtin(global_invocation_id) id: vec3) { + vals[id.x] = vals[id.x] * i32(id.x); + } + `, [4, 1, 1], [[b]]); + await alanStd.gpuRun(plan); + let v = await alanStd.readBuffer(b); + return v.map((i) => i.valueOf()); + }), [0, 2, 4, 6]); + await context.close(); await browser.close(); })(); From d326331c58e9114eb34861c421e28fc704b116c8 Mon Sep 17 00:00:00 2001 From: David Ellis Date: Thu, 24 Oct 2024 15:52:58 -0500 Subject: [PATCH 17/20] It looks like splatting doesn't work here for some reason? --- alan_std.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/alan_std.js b/alan_std.js index bdb5ff2f..d6426154 100644 --- a/alan_std.js +++ b/alan_std.js @@ -483,7 +483,11 @@ export async function gpuRun(gg) { }); cpass.setBindGroup(i, bindGroup); } - cpass.dispatchWorkgroups(...gg.workgroupSizes); + cpass.dispatchWorkgroups( + gg.workgroupSizes[0].valueOf(), + (gg.workgroupSizes[1] ?? 1).valueOf(), + (gg.workgroupSizes[2] ?? 1).valueOf() + ); g.queue.submit([encoder.finish()]); } From 832a16148beb101367d262576173fd2b32469e96 Mon Sep 17 00:00:00 2001 From: David Ellis Date: Thu, 24 Oct 2024 15:57:39 -0500 Subject: [PATCH 18/20] Oops, I passed the args to the constructor in the wrong order --- alan_std.test_gpgpu.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/alan_std.test_gpgpu.js b/alan_std.test_gpgpu.js index 1e112cb1..0282da3d 100644 --- a/alan_std.test_gpgpu.js +++ b/alan_std.test_gpgpu.js @@ -65,7 +65,7 @@ import { chromium } from 'playwright'; fn main(@builtin(global_invocation_id) id: vec3) { vals[id.x] = vals[id.x] * i32(id.x); } - `, [4, 1, 1], [[b]]); + `, [[b]], [4, 1, 1]); await alanStd.gpuRun(plan); let v = await alanStd.readBuffer(b); return v.map((i) => i.valueOf()); From ca0517c1186b98193dbafb6057d39284bf0156ef Mon Sep 17 00:00:00 2001 From: David Ellis Date: Thu, 24 Oct 2024 16:05:29 -0500 Subject: [PATCH 19/20] Alter the readBuffer behavior to match the Rust side, which waits for the queue of work to empty before triggering a read, to make sure any side-effects are handled --- alan_std.js | 1 + 1 file changed, 1 insertion(+) diff --git a/alan_std.js b/alan_std.js index d6426154..0b956ff0 100644 --- a/alan_std.js +++ b/alan_std.js @@ -493,6 +493,7 @@ export async function gpuRun(gg) { export async function readBuffer(b) { let g = await gpu(); + await g.queue.onSubmittedWorkDone(); // Don't try to read until you're sure it's safe to let tempBuffer = await createEmptyBuffer(mapReadBufferType(), b.size / 4); let encoder = g.device.createCommandEncoder(); encoder.copyBufferToBuffer(b, 0, tempBuffer, 0, b.size); From 08af90674a7d692381e186565df51e7c8cb077bb Mon Sep 17 00:00:00 2001 From: David Ellis Date: Thu, 24 Oct 2024 16:12:10 -0500 Subject: [PATCH 20/20] I think wgpu doesn't require this of you, but maybe WebGPU does --- alan_std.js | 1 + 1 file changed, 1 insertion(+) diff --git a/alan_std.js b/alan_std.js index 0b956ff0..d8c4fb32 100644 --- a/alan_std.js +++ b/alan_std.js @@ -488,6 +488,7 @@ export async function gpuRun(gg) { (gg.workgroupSizes[1] ?? 1).valueOf(), (gg.workgroupSizes[2] ?? 1).valueOf() ); + cpass.end(); g.queue.submit([encoder.finish()]); }