/**
 * Hamilton product of two unit quaternions: `out = a ⊗ b`.
 *
 * Composition order is the usual rotation convention: the product `a ⊗ b`
 * applies `b` first, then `a` (i.e. `(a⊗b)·v·(a⊗b)* == a·(b·v·b*)·a*`). So to
 * rotate a vector by `q1` and then by `q2`, compose `q2 ⊗ q1`.
 *
 * Sign convention: `(x, y, z, w)` — `w` last, matching {@link Quaternion} and
 * the rest of the `core/geom/3d/quaternion/` family.
 *
 * `out` may alias neither `a` nor `b` component-wise (the result is computed
 * from all eight inputs before any write, so passing the same backing array
 * via different offsets is safe only if the ranges don't overlap).
 *
 * @param {number[]|Float32Array|Float64Array} out
 * @param {number} out_offset offset into `out`; receives 4 floats (x, y, z, w)
 * @param {number} ax @param {number} ay @param {number} az @param {number} aw
 * @param {number} bx @param {number} by @param {number} bz @param {number} bw
 */
export function quat3_multiply(out, out_offset, ax, ay, az, aw, bx, by, bz, bw) {
    out[out_offset]     = aw * bx + ax * bw + ay * bz - az * by;
    out[out_offset + 1] = aw * by - ax * bz + ay * bw + az * bx;
    out[out_offset + 2] = aw * bz + ax * by - ay * bx + az * bw;
    out[out_offset + 3] = aw * bw - ax * bx - ay * by - az * bz;
}
