import _ from 'lodash';
import '../../../../assert/js/assert.js';
import '../../../../globals.js';
// Copyright 2023-2024, University of Colorado Boulder
/**
 * Represents a data type that can be serialized/deserialized to/from a binary form, both in TypeScript and in WGSL.
 *
 * @author Jonathan Olson <jonathan.olson@colorado.edu>
 */
import { alpenglow, ByteEncoder, decimalS, i32S, u32S, wgsl, wgslJoin } from '../../imports.js';
import Vector2 from '../../../../dot/js/Vector2.js';
import Random from '../../../../dot/js/Random.js';
import Vector3 from '../../../../dot/js/Vector3.js';
import Vector4 from '../../../../dot/js/Vector4.js';
// eslint-disable-next-line phet/bad-sim-text
const random = new Random();
// TODO: potential separation of "WGSLType" - and the way to read the "raw" data out, is different than ConcreteType (which includes the JS representation)
// From the WGSL spec, https://www.w3.org/TR/WGSL/#roundup
const roundUp = (k, n) => Math.ceil(n / k) * k;
// const strideOfArrayOf = ( elementType: ConcreteType ): number => roundUp( elementType.wgslAlign, elementType.wgslSize );
// TODO: Have ConcreteType and things be classes? The Array type should contain more info, etc. But we also want to
// TODO: allow full flexibility...? Structure types we'll want to get things out of.
export const U32_IDENTITY_VALUES = {
    add: 0,
    max: 0,
    min: 0xffffffff,
    and: 0xffffffff,
    or: 0,
    xor: 0
};
// TODO: double-check some of these
export const I32_IDENTITY_VALUES = {
    add: 0,
    // max: -0x80000000, // what, why can't this be represented?
    max: -0x7fffffff,
    min: 0x7fffffff,
    and: -1,
    or: 0,
    xor: 0
};
export const getArrayType = (type, size, outOfRangeElement) => {
    const u32sPerElement = type.bytesPerElement / 4;
    return {
        name: `${type.name}[${size}]`,
        bytesPerElement: type.bytesPerElement * size,
        length: size,
        elementType: type,
        slice(start, end) {
            assert && assert(start === 0, 'We will need more logic to handle offsets');
            return getArrayType(type, end, outOfRangeElement);
        },
        equals(a, b) {
            assert && assert(a.length === size);
            assert && assert(b.length === size);
            for (let i = 0; i < size; i++) {
                if (!type.equals(a[i], b[i])) {
                    return false;
                }
            }
            return true;
        },
        equalsWGSL(a, b) {
            // TODO: this is excessive... we should just do a loop? I guess we need it as an expression?
            return wgslJoin(' && ', _.range(0, size).map(i => {
                return type.equalsWGSL(wgsl `${a}[ ${u32S(i)} ]`, wgsl `${b}[ ${u32S(i)} ]`);
            }));
        },
        encode(value, encoder) {
            assert && assert(outOfRangeElement !== undefined || type.outOfRangeElement !== undefined || value.length === size);
            for (let i = 0; i < size; i++) {
                type.encode(i < value.length ? value[i] : outOfRangeElement === undefined ? type.outOfRangeElement : outOfRangeElement, encoder);
            }
        },
        decode(encoder, offset) {
            const array = [];
            for (let i = 0; i < size; i++) {
                array.push(type.decode(encoder, offset + i * u32sPerElement));
            }
            return array;
        },
        valueType: wgsl `array<${type.valueType}, ${decimalS(size)}>`,
        writeU32s(storeStatement, value) {
            return wgslJoin('\n', _.range(0, size).map(i => {
                return type.writeU32s((offset, u32expr) => storeStatement(wgsl `( ${offset} + ${u32S(i * u32sPerElement)} )`, u32expr), wgsl `${value}[ ${u32S(i)} ]`);
            }));
        },
        wgslAlign: type.wgslAlign,
        wgslSize: size * roundUp(type.wgslAlign, type.wgslSize),
        generateRandom(fullSize = false) {
            return _.range(0, size).map(() => type.generateRandom(fullSize));
        },
        toDebugString(value) {
            return `[${value.map(v => type.toDebugString(v)).join(', ')}]`;
        }
    };
};
alpenglow.register('getArrayType', getArrayType);
export const getVariableLengthArrayType = (type, maxSize, outOfRangeElement) => {
    const u32sPerElement = type.bytesPerElement / 4;
    return {
        name: `${type.name}[max ${maxSize}]`,
        bytesPerElement: type.bytesPerElement * maxSize, // TODO: don't define?
        length: maxSize, // TODO: don't define?
        elementType: type,
        slice(start, end) {
            assert && assert(start === 0, 'We will need more logic to handle offsets');
            return getVariableLengthArrayType(type, end, outOfRangeElement);
        },
        // TODO: how to handle? this is a mess
        equals(a, b) {
            assert && assert(a.length <= maxSize);
            assert && assert(b.length <= maxSize);
            for (let i = 0; i < maxSize; i++) {
                if (!type.equals(a[i], b[i])) {
                    return false;
                }
            }
            return true;
        },
        equalsWGSL(a, b) {
            // TODO: this is excessive... we should just do a loop? I guess we need it as an expression?
            return wgslJoin(' && ', _.range(0, maxSize).map(i => {
                return type.equalsWGSL(wgsl `${a}[ ${u32S(i)} ]`, wgsl `${b}[ ${u32S(i)} ]`);
            }));
        },
        encode(value, encoder) {
            assert && assert(outOfRangeElement !== undefined || type.outOfRangeElement !== undefined || value.length <= maxSize);
            for (let i = 0; i < value.length; i++) {
                type.encode(i < value.length ? value[i] : outOfRangeElement === undefined ? type.outOfRangeElement : outOfRangeElement, encoder);
            }
        },
        // TODO: potential manual "length" included?
        decode(encoder, offset) {
            const array = [];
            for (let i = 0; i < maxSize; i++) {
                array.push(type.decode(encoder, offset + i * u32sPerElement));
            }
            return array;
        },
        valueType: wgsl `array<${type.valueType}>`,
        writeU32s(storeStatement, value) {
            return wgslJoin('\n', _.range(0, maxSize).map(i => {
                return type.writeU32s((offset, u32expr) => storeStatement(wgsl `( ${offset} + ${u32S(i * u32sPerElement)} )`, u32expr), wgsl `${value}[ ${u32S(i)} ]`);
            }));
        },
        wgslAlign: type.wgslAlign,
        wgslSize: maxSize * roundUp(type.wgslAlign, type.wgslSize),
        generateRandom(fullSize = false) {
            return _.range(0, maxSize).map(() => type.generateRandom(fullSize));
        },
        toDebugString(value) {
            return `[${value.map(v => type.toDebugString(v)).join(', ')}]`;
        }
    };
};
alpenglow.register('getVariableLengthArrayType', getVariableLengthArrayType);
export const getCastedType = (type, valueType) => {
    return {
        // eslint-disable-next-line phet/no-object-spread-on-non-literals
        ...type,
        valueType: valueType
    };
};
export const U32Type = {
    name: 'u32',
    bytesPerElement: 4,
    outOfRangeElement: 101010101,
    equals(a, b) {
        return a === b;
    },
    equalsWGSL: (a, b) => {
        return wgsl `( ${a} == ${b} )`;
    },
    encode(value, encoder) {
        encoder.pushU32(value);
    },
    decode(encoder, offset) {
        return encoder.fullU32Array[offset];
    },
    valueType: wgsl `u32`,
    writeU32s(storeStatement, value) {
        return wgsl `
       ${storeStatement(wgsl `0u`, value)}
    `;
    },
    wgslAlign: 4,
    wgslSize: 4,
    generateRandom: (fullSize = false) => random.nextIntBetween(0, fullSize ? 0xffffffff : 0xff),
    toDebugString: (value) => value.toString()
};
alpenglow.register('U32Type', U32Type);
export const U32AtomicType = {
    name: 'u32 atomic',
    bytesPerElement: 4,
    equals(a, b) {
        return a === b;
    },
    equalsWGSL: (a, b) => {
        return wgsl `( atomicLoad( &${a} ) == atomicLoad( &${b} ) )`;
    },
    encode(value, encoder) {
        encoder.pushU32(value);
    },
    decode(encoder, offset) {
        return encoder.fullU32Array[offset];
    },
    valueType: wgsl `atomic<u32>`,
    writeU32s(storeStatement, value) {
        return wgsl `
       ${storeStatement(wgsl `0u`, wgsl `atomicLoad( &${value} )`)}
    `;
    },
    wgslAlign: 4,
    wgslSize: 4,
    generateRandom: (fullSize = false) => random.nextIntBetween(0, fullSize ? 0xffffffff : 0xff),
    toDebugString: (value) => value.toString()
};
alpenglow.register('U32AtomicType', U32AtomicType);
export const U32Add = {
    name: 'u32 addition',
    type: U32Type,
    isCommutative: true,
    identity: U32_IDENTITY_VALUES.add,
    apply: (a, b) => a + b,
    identityWGSL: u32S(U32_IDENTITY_VALUES.add),
    combineExpression: (a, b) => wgsl `( ${a} + ${b} )`,
    combineStatements: (varName, a, b) => wgsl `${varName} = ${a} + ${b};`,
    atomicName: 'atomicAdd'
};
alpenglow.register('U32Add', U32Add);
export const U32Min = {
    name: 'u32 min',
    type: U32Type,
    isCommutative: true,
    identity: U32_IDENTITY_VALUES.min,
    apply: (a, b) => Math.min(a, b),
    identityWGSL: u32S(U32_IDENTITY_VALUES.min),
    combineExpression: (a, b) => wgsl `min( ${a}, ${b} )`,
    combineStatements: (varName, a, b) => wgsl `${varName} = min( ${a} + ${b} );`,
    atomicName: 'atomicMin'
};
alpenglow.register('U32Min', U32Min);
export const U32Max = {
    name: 'u32 max',
    type: U32Type,
    isCommutative: true,
    identity: U32_IDENTITY_VALUES.max,
    apply: (a, b) => Math.max(a, b),
    identityWGSL: u32S(U32_IDENTITY_VALUES.max),
    combineExpression: (a, b) => wgsl `max( ${a}, ${b} )`,
    combineStatements: (varName, a, b) => wgsl `${varName} = max( ${a} + ${b} );`,
    atomicName: 'atomicMax'
};
alpenglow.register('U32Max', U32Max);
export const U32And = {
    name: 'u32 and',
    type: U32Type,
    isCommutative: true,
    identity: U32_IDENTITY_VALUES.and,
    apply: (a, b) => (a & b) >>> 0,
    identityWGSL: u32S(U32_IDENTITY_VALUES.and),
    combineExpression: (a, b) => wgsl `( ${a} & ${b} )`,
    combineStatements: (varName, a, b) => wgsl `${varName} = ( ${a} & ${b} );`,
    atomicName: 'atomicAnd'
};
alpenglow.register('U32And', U32And);
export const U32Or = {
    name: 'u32 or',
    type: U32Type,
    isCommutative: true,
    identity: U32_IDENTITY_VALUES.or,
    apply: (a, b) => (a | b) >>> 0,
    identityWGSL: u32S(U32_IDENTITY_VALUES.or),
    combineExpression: (a, b) => wgsl `( ${a} | ${b} )`,
    combineStatements: (varName, a, b) => wgsl `${varName} = ( ${a} | ${b} );`,
    atomicName: 'atomicOr'
};
alpenglow.register('U32Or', U32Or);
export const U32Xor = {
    name: 'u32 xor',
    type: U32Type,
    isCommutative: true,
    identity: U32_IDENTITY_VALUES.xor,
    apply: (a, b) => (a ^ b) >>> 0,
    identityWGSL: u32S(U32_IDENTITY_VALUES.xor),
    combineExpression: (a, b) => wgsl `( ${a} ^ ${b} )`,
    combineStatements: (varName, a, b) => wgsl `${varName} = ( ${a} ^ ${b} );`,
    atomicName: 'atomicXor'
};
alpenglow.register('U32Xor', U32Xor);
export const U32Order = {
    name: 'u32 order',
    type: U32Type,
    compare(a, b) {
        return a - b;
    },
    getBits: (value, bitOffset, bitQuantity) => {
        return ((value >>> bitOffset) & ((1 << bitQuantity) - 1)) >>> 0;
    },
    compareWGSL: (a, b) => {
        // TODO: THIS FAILS RANGE CHECKS
        // return `( i32( ${a} ) - i32( ${b} ) )`;
        return wgsl `select( select( 0i, 1i, ${a} > ${b} ), -1i, ${a} < ${b} )`;
    },
    greaterThanWGSL: (a, b) => {
        return wgsl `( ${a} > ${b} )`;
    },
    lessThanOrEqualWGSL: (a, b) => {
        return wgsl `( ${a} <= ${b} )`;
    },
    getBitsWGSL: (value, bitOffset, bitQuantity) => {
        return wgsl `( ( ${value} >> ${u32S(bitOffset)} ) & ${u32S((1 << bitQuantity) - 1)} )`;
    }
};
alpenglow.register('U32Order', U32Order);
export const U32ReverseOrder = {
    name: 'u32 reverse order',
    type: U32Type,
    compare(a, b) {
        return b - a;
    },
    getBits: (value, bitOffset, bitQuantity) => {
        return (((0xffffffff - value) >>> bitOffset) & ((1 << bitQuantity) - 1)) >>> 0;
    },
    compareWGSL: (a, b) => {
        return wgsl `select( select( 0i, 1i, ${a} < ${b} ), -1i, ${a} > ${b} )`;
    },
    greaterThanWGSL: (a, b) => {
        return wgsl `( ${a} < ${b} )`;
    },
    lessThanOrEqualWGSL: (a, b) => {
        return wgsl `( ${a} >= ${b} )`;
    },
    getBitsWGSL: (value, bitOffset, bitQuantity) => {
        // TODO: is there a bitwise trick?
        return wgsl `( ( ( 0xffffffffu - ${value} ) >> ${u32S(bitOffset)} ) & ${u32S((1 << bitQuantity) - 1)} )`;
    }
};
alpenglow.register('U32ReverseOrder', U32ReverseOrder);
export const I32Type = {
    name: 'i32',
    bytesPerElement: 4,
    outOfRangeElement: 101010101,
    equals(a, b) {
        return a === b;
    },
    equalsWGSL: (a, b) => {
        return wgsl `( ${a} == ${b} )`;
    },
    encode(value, encoder) {
        encoder.pushI32(value);
    },
    decode(encoder, offset) {
        return encoder.fullI32Array[offset];
    },
    valueType: wgsl `i32`,
    writeU32s(storeStatement, value) {
        return wgsl `
       ${storeStatement(wgsl `0u`, wgsl `bitcast<u32>( ${value} )`)}
    `;
    },
    wgslAlign: 4,
    wgslSize: 4,
    generateRandom: (fullSize = false) => random.nextIntBetween(fullSize ? -0x7fffffff : -0x7f, fullSize ? 0x7fffffff : 0x7f),
    toDebugString: (value) => value.toString()
};
alpenglow.register('I32Type', I32Type);
export const I32AtomicType = {
    name: 'i32 atomic',
    bytesPerElement: 4,
    equals(a, b) {
        return a === b;
    },
    equalsWGSL: (a, b) => {
        return wgsl `( atomicLoad( &${a} ) == atomicLoad( &${b} ) )`;
    },
    encode(value, encoder) {
        encoder.pushU32(value);
    },
    decode(encoder, offset) {
        return encoder.fullU32Array[offset];
    },
    valueType: wgsl `atomic<i32>`,
    writeU32s(storeStatement, value) {
        return wgsl `
       ${storeStatement(wgsl `0u`, wgsl `bitcast<u32>( atomicLoad( &${value} ) )`)}
    `;
    },
    wgslAlign: 4,
    wgslSize: 4,
    generateRandom: (fullSize = false) => random.nextIntBetween(fullSize ? -0x7fffffff : -0x7f, fullSize ? 0x7fffffff : 0x7f),
    toDebugString: (value) => value.toString()
};
alpenglow.register('I32AtomicType', I32AtomicType);
export const I32Add = {
    name: 'i32 addition',
    type: I32Type,
    isCommutative: true,
    identity: I32_IDENTITY_VALUES.add,
    apply: (a, b) => a + b,
    identityWGSL: i32S(I32_IDENTITY_VALUES.add),
    combineExpression: (a, b) => wgsl `( ${a} + ${b} )`,
    combineStatements: (varName, a, b) => wgsl `${varName} = ${a} + ${b};`,
    atomicName: 'atomicAdd'
};
alpenglow.register('I32Add', I32Add);
export const I32Min = {
    name: 'i32 min',
    type: I32Type,
    isCommutative: true,
    identity: I32_IDENTITY_VALUES.min,
    apply: (a, b) => Math.min(a, b),
    identityWGSL: i32S(I32_IDENTITY_VALUES.min),
    combineExpression: (a, b) => wgsl `min( ${a}, ${b} )`,
    combineStatements: (varName, a, b) => wgsl `${varName} = min( ${a} + ${b} );`,
    atomicName: 'atomicMin'
};
alpenglow.register('I32Min', I32Min);
export const I32Max = {
    name: 'i32 max',
    type: I32Type,
    isCommutative: true,
    identity: I32_IDENTITY_VALUES.max,
    apply: (a, b) => Math.max(a, b),
    identityWGSL: i32S(I32_IDENTITY_VALUES.max),
    combineExpression: (a, b) => wgsl `max( ${a}, ${b} )`,
    combineStatements: (varName, a, b) => wgsl `${varName} = max( ${a} + ${b} );`,
    atomicName: 'atomicMax'
};
alpenglow.register('I32Max', I32Max);
export const I32And = {
    name: 'i32 and',
    type: I32Type,
    isCommutative: true,
    identity: I32_IDENTITY_VALUES.and,
    apply: (a, b) => (a & b) >>> 0,
    identityWGSL: i32S(I32_IDENTITY_VALUES.and),
    combineExpression: (a, b) => wgsl `( ${a} & ${b} )`,
    combineStatements: (varName, a, b) => wgsl `${varName} = ( ${a} & ${b} );`,
    atomicName: 'atomicAnd'
};
alpenglow.register('I32And', I32And);
export const I32Or = {
    name: 'i32 or',
    type: I32Type,
    isCommutative: true,
    identity: I32_IDENTITY_VALUES.or,
    apply: (a, b) => (a | b) >>> 0,
    identityWGSL: i32S(I32_IDENTITY_VALUES.or),
    combineExpression: (a, b) => wgsl `( ${a} | ${b} )`,
    combineStatements: (varName, a, b) => wgsl `${varName} = ( ${a} | ${b} );`,
    atomicName: 'atomicOr'
};
alpenglow.register('I32Or', I32Or);
export const I32Xor = {
    name: 'i32 xor',
    type: I32Type,
    isCommutative: true,
    identity: I32_IDENTITY_VALUES.xor,
    apply: (a, b) => (a ^ b) >>> 0,
    identityWGSL: i32S(I32_IDENTITY_VALUES.xor),
    combineExpression: (a, b) => wgsl `( ${a} ^ ${b} )`,
    combineStatements: (varName, a, b) => wgsl `${varName} = ( ${a} ^ ${b} );`,
    atomicName: 'atomicXor'
};
alpenglow.register('I32Xor', I32Xor);
// TODO: Do a full order, with bit sorts(!)
export const I32Order = {
    name: 'i32 order',
    type: I32Type,
    compare(a, b) {
        return a - b;
    },
    compareWGSL: (a, b) => {
        return wgsl `( ${a} - ${b} )`;
    },
    greaterThanWGSL: (a, b) => {
        return wgsl `( ${a} > ${b} )`;
    },
    lessThanOrEqualWGSL: (a, b) => {
        return wgsl `( ${a} <= ${b} )`;
    }
};
alpenglow.register('I32Order', I32Order);
export const F32Type = {
    name: 'f32',
    bytesPerElement: 4,
    outOfRangeElement: 7.101010101,
    equals(a, b) {
        return a === b;
    },
    equalsWGSL: (a, b) => {
        return wgsl `( ${a} == ${b} )`;
    },
    encode(value, encoder) {
        encoder.pushF32(value);
    },
    decode(encoder, offset) {
        return encoder.fullF32Array[offset];
    },
    valueType: wgsl `f32`,
    writeU32s(storeStatement, value) {
        return wgsl `
       ${storeStatement(wgsl `0u`, wgsl `bitcast<u32>( ${value} )`)}
    `;
    },
    wgslAlign: 4,
    wgslSize: 4,
    generateRandom: (fullSize = false) => random.nextDoubleBetween(fullSize ? -0x7fffffff : 0, fullSize ? 0x7fffffff : 1),
    toDebugString: (value) => value.toString()
};
alpenglow.register('F32Type', F32Type);
// TODO: Do a full order, with bit sorts(!)
export const F32Order = {
    name: 'f32 order',
    type: F32Type,
    compare(a, b) {
        return a - b;
    },
    compareWGSL: (a, b) => {
        return wgsl `select( select( 0i, 1i, ${a} > ${b} ), -1i, ${a} < ${b} )`;
    },
    greaterThanWGSL: (a, b) => {
        return wgsl `( ${a} > ${b} )`;
    },
    lessThanOrEqualWGSL: (a, b) => {
        return wgsl `( ${a} <= ${b} )`;
    }
};
alpenglow.register('F32Order', F32Order);
export const Vec2uType = {
    name: 'vec2u',
    bytesPerElement: 8,
    outOfRangeElement: new Vector2(101010101, 101010101),
    equals(a, b) {
        return a.equals(b);
    },
    equalsWGSL: (a, b) => {
        // TODO: test
        return wgsl `all( ${a} == ${b} )`;
    },
    encode(value, encoder) {
        encoder.pushU32(value.x);
        encoder.pushU32(value.y);
    },
    decode(encoder, offset) {
        return new Vector2(encoder.fullU32Array[offset], encoder.fullU32Array[offset + 1]);
    },
    valueType: wgsl `vec2u`,
    writeU32s(storeStatement, value) {
        return wgsl `
       ${storeStatement(wgsl `0u`, wgsl `${value}.x`)}
       ${storeStatement(wgsl `1u`, wgsl `${value}.y`)}
    `;
    },
    wgslAlign: 8,
    wgslSize: 8,
    generateRandom: (fullSize = false) => new Vector2(U32Type.generateRandom(fullSize), U32Type.generateRandom(fullSize)),
    toDebugString: (value) => `vec2u(${value.x}, ${value.y})`
};
alpenglow.register('Vec2uType', Vec2uType);
export const Vec2uAdd = {
    name: 'vec2u addition',
    type: Vec2uType,
    isCommutative: true,
    identity: Vector2.ZERO,
    apply: (a, b) => a.plus(b),
    identityWGSL: wgsl `vec2u()`,
    combineExpression: (a, b) => wgsl `( ${a} + ${b} )`,
    combineStatements: (varName, a, b) => wgsl `${varName} = ${a} + ${b};`
};
alpenglow.register('Vec2uAdd', Vec2uAdd);
export const Vec2uBic = {
    name: 'vec2u bicyclic semigroup',
    type: Vec2uType,
    isCommutative: false,
    identity: Vector2.ZERO,
    apply: (a, b) => {
        const min = Math.min(a.y, b.x);
        return new Vector2(a.x + b.x - min, a.y + b.y - min);
    },
    identityWGSL: wgsl `vec2( 0u )`,
    combineExpression: (a, b) => wgsl `( ${a} + ${b} - min( ${a}.y, ${b}.x ) )`,
    combineStatements: (varName, a, b) => wgsl `${varName} = ${a} + ${b} - min( ${a}.y, ${b}.x );`
};
alpenglow.register('Vec2uBic', Vec2uBic);
export const Vec2uLexicographicalOrder = {
    name: 'vec2u lexicographical order',
    type: Vec2uType,
    compare(a, b) {
        const xOrder = U32Order.compare(a.x, b.x);
        if (xOrder !== 0) {
            return xOrder;
        }
        else {
            return U32Order.compare(a.y, b.y);
        }
    },
    getBits: (value, bitOffset, bitQuantity) => {
        return ((ByteEncoder.rightShiftU32(value.x, bitOffset - 32) & ((1 << bitQuantity) - 1)) >>> 0) +
            ((ByteEncoder.rightShiftU32(value.y, bitOffset) & ((1 << bitQuantity) - 1)) >>> 0);
    },
    // TODO: potentially stuff this in a function so it is more readable?
    compareWGSL: (a, b) => {
        return wgsl `select( select( select( select( 0i, 1i, ${a}.y > ${b}.y ), -1i, ${a}.y < ${b}.y ), 1i, ${a}.x > ${b}.x ), -1i, ${a}.x < ${b}.x )`;
    },
    greaterThanWGSL: (a, b) => {
        return wgsl `( ${a}.x > ${b}.x || ( ${a}.x == ${b}.x && ${a}.y > ${b}.y ) )`;
    },
    lessThanOrEqualWGSL: (a, b) => {
        return wgsl `( ${a}.x <= ${b}.x && ( ${a}.x != ${b}.x || ${a}.y <= ${b}.y ) )`;
    },
    getBitsWGSL: (value, bitOffset, bitQuantity) => {
        // NOTE: WGSL doesn't like `<< 32u`, so we split it like so.
        if (bitOffset === 0) {
            return wgsl `( ( ${value}.y >> ${u32S(bitOffset)} ) & ${u32S((1 << bitQuantity) - 1)} )`;
        }
        else if (bitOffset < 32) {
            return wgsl `( ( ( ${value}.x << ${u32S(32 - bitOffset)} ) & ${u32S((1 << bitQuantity) - 1)} ) | ( ( ${value}.y >> ${u32S(bitOffset)} ) & ${u32S((1 << bitQuantity) - 1)} ) )`;
        }
        else {
            return wgsl `( ( ${value}.x >> ${u32S(bitOffset - 32)} ) & ${u32S((1 << bitQuantity) - 1)} )`;
        }
    }
};
alpenglow.register('Vec2uLexicographicalOrder', Vec2uLexicographicalOrder);
export const Vec3uType = {
    name: 'vec3u',
    bytesPerElement: 12,
    outOfRangeElement: new Vector3(101010101, 101010101, 101010101),
    equals(a, b) {
        return a.equals(b);
    },
    equalsWGSL: (a, b) => {
        // TODO: test
        return wgsl `all( ${a} == ${b} )`;
    },
    encode(value, encoder) {
        encoder.pushU32(value.x);
        encoder.pushU32(value.y);
        encoder.pushU32(value.z);
    },
    decode(encoder, offset) {
        return new Vector3(encoder.fullU32Array[offset], encoder.fullU32Array[offset + 1], encoder.fullU32Array[offset + 2]);
    },
    valueType: wgsl `vec3u`,
    writeU32s(storeStatement, value) {
        return wgsl `
       ${storeStatement(wgsl `0u`, wgsl `${value}.x`)}
       ${storeStatement(wgsl `1u`, wgsl `${value}.y`)}
       ${storeStatement(wgsl `2u`, wgsl `${value}.z`)}
    `;
    },
    wgslAlign: 16,
    wgslSize: 12,
    generateRandom: (fullSize = false) => new Vector3(U32Type.generateRandom(fullSize), U32Type.generateRandom(fullSize), U32Type.generateRandom(fullSize)),
    toDebugString: (value) => `vec3u(${value.x}, ${value.y}, ${value.z})`
};
alpenglow.register('Vec3uType', Vec3uType);
export const Vec3uAdd = {
    name: 'vec3u addition',
    type: Vec3uType,
    isCommutative: true,
    identity: Vector3.ZERO,
    apply: (a, b) => a.plus(b),
    identityWGSL: wgsl `vec3u()`,
    combineExpression: (a, b) => wgsl `( ${a} + ${b} )`,
    combineStatements: (varName, a, b) => wgsl `${varName} = ${a} + ${b};`
};
alpenglow.register('Vec3uAdd', Vec3uAdd);
export const Vec4uType = {
    name: 'vec4u',
    bytesPerElement: 16,
    outOfRangeElement: new Vector4(101010101, 101010101, 101010101, 101010101),
    equals(a, b) {
        return a.equals(b);
    },
    equalsWGSL: (a, b) => {
        // TODO: test
        return wgsl `all( ${a} == ${b} )`;
    },
    encode(value, encoder) {
        encoder.pushU32(value.x);
        encoder.pushU32(value.y);
        encoder.pushU32(value.z);
        encoder.pushU32(value.w);
    },
    decode(encoder, offset) {
        return new Vector4(encoder.fullU32Array[offset], encoder.fullU32Array[offset + 1], encoder.fullU32Array[offset + 2], encoder.fullU32Array[offset + 3]);
    },
    valueType: wgsl `vec4u`,
    writeU32s(storeStatement, value) {
        return wgsl `
       ${storeStatement(wgsl `0u`, wgsl `${value}.x`)}
       ${storeStatement(wgsl `1u`, wgsl `${value}.y`)}
       ${storeStatement(wgsl `2u`, wgsl `${value}.z`)}
       ${storeStatement(wgsl `3u`, wgsl `${value}.w`)}
    `;
    },
    wgslAlign: 16,
    wgslSize: 16,
    generateRandom: (fullSize = false) => new Vector4(U32Type.generateRandom(fullSize), U32Type.generateRandom(fullSize), U32Type.generateRandom(fullSize), U32Type.generateRandom(fullSize)),
    toDebugString: (value) => `vec4u(${value.x}, ${value.y}, ${value.z}, ${value.w})`
};
alpenglow.register('Vec4uType', Vec4uType);
export const Vec4uAdd = {
    name: 'vec4u addition',
    type: Vec4uType,
    isCommutative: true,
    identity: Vector4.ZERO,
    apply: (a, b) => a.plus(b),
    identityWGSL: wgsl `vec4u()`,
    combineExpression: (a, b) => wgsl `( ${a} + ${b} )`,
    combineStatements: (varName, a, b) => wgsl `${varName} = ${a} + ${b};`
};
alpenglow.register('Vec4uAdd', Vec4uAdd);
//# sourceMappingURL=ConcreteType.js.map