How to convert [Nat8] to [Nat64]

I’m trying to create an RNG in Motoko, and I want to seed it with a Blob from Random.blob(). In Motoko, there is a method to convert a Blob to a [Nat8], but for the RNG I want to implement (Xoshiro256ss) I need a [Nat64]. Since [Nat8] would have length 32, the [Nat64] array should have length 4.

Here is my code:

private func initState(seed : Blob) : [var Nat64] {
    let blobArray : [Nat8] = Blob.toArray(seed);
    
    let state0 : [var Nat64] = [var 0, 0, 0, 0];
    for (i in Iter.range(0, 3)) {
        let first : Nat64 = Nat64.fromNat(Prim.nat8ToNat(blobArray[i * 8]));
        let second : Nat64 = Nat64.fromNat(Prim.nat8ToNat(blobArray[i * 8 + 1])) << 8;
        let third : Nat64 = Nat64.fromNat(Prim.nat8ToNat(blobArray[i * 8] + 2)) << 16;
        let fourth : Nat64 = Nat64.fromNat(Prim.nat8ToNat(blobArray[i * 8] + 3)) << 24;
        let fifth : Nat64 = Nat64.fromNat(Prim.nat8ToNat(blobArray[i * 8] + 4)) << 32;
        let sixth : Nat64 = Nat64.fromNat(Prim.nat8ToNat(blobArray[i * 8] + 5)) << 40;
        let seventh : Nat64 = Nat64.fromNat(Prim.nat8ToNat(blobArray[i * 8] + 6)) << 48;
        let eighth : Nat64 = Nat64.fromNat(Prim.nat8ToNat(blobArray[i * 8] + 7)) << 56;
        let newState : Nat64 = first | second | third | fourth | fifth | sixth | seventh | eighth;
        state0[i] := newState;
  };

  return state0;
};

When debugging with print statements, I see that for the first three iterations of the loop the code works fine, but on the last iteration, specifically in the line

state0[i] := newState;

I get an arithmetic overflow error.

Reject code: 5
Reject text: Canister rrkah-fqaaa-aaaaa-aaaaq-cai trapped explicitly: arithmetic overflow

I’m not sure if this is a bug in Motoko, or if I have errors in my code.

I would access the blob bytes with the iterator seed.vals() and then call next() on the iterator 8 times in a loop, each time getting one byte (Nat8) out. I mean instead of converting Blob to Nat8 array and accessing the array by index.

Then you convert the Nat8 to Nat64 (like you do in your code) and shift the new byte in from the right into your target Nat64 variable. And do that 8 times.

If you are not set on xoshiro then there is SFC available here: motoko-lib/Prng.mo at main · research-ag/motoko-lib · GitHub

I suspect the overflow happens because you wrote (blobArray[i * 8] + N) instead of (blobArray[i * 8 + N]) in most of those lines.

Wow, I’m embarrassed I didn’t catch that. I guess staring at a problem for a while gives you tunnel vision. Thank you.