Search code examples
.netf#sequencesinvalidoperationexceptionseq

"The input sequence has an insufficient number of elements" on a Seq.length call?


I'm working on the Matasano Crypto Challenges but I'm running into an odd bug. I have a Seq of integers, and on a call to Seq.length (or Seq.sum), I get a 'System.InvalidOperationException' with the explanation of "The input sequence has an insufficient number of elements." Normally this error is associated with calling Seq.take or similar on an empty Seq, but this Seq is not empty nor is the call one that would normally cause such an exception.

let HammingDistance (b1:seq<byte>) (b2:seq<byte>) : int =
    let bytePairs = Seq.zip b1 b2
    let HammingWeight (b:byte) =
        let bitPositions = {0 .. 7}
        let isSet (byte1:byte) (offset:int) = if (byte1 &&& ((byte 1) <<< offset)) > (byte 0) then 1 else 0
        Seq.map (isSet b) bitPositions |> Seq.sum
    let HammingDistanceBytes (byte1, byte2) =
        HammingWeight (byte1 ^^^ byte2)
    let distances = Seq.map HammingDistanceBytes bytePairs
    printfn "Distances: %A" distances
    printfn "Distances length: %d" (Seq.length distances)
    Seq.sum distances

The last few lines of output before the exception follow:

Distances length: 1435
Distances: seq [3; 3; 1; 5; ...]
Distances length: 1436
Distances: seq [3; 6; 2; 1; ...]
Distances length: 1437
Distances: seq [0; 3; 2; 3; ...]
Distances length: 1438
Distances: seq [3; 3; 4; 2; ...]

Can anyone explain why this exception is happening, or give any steps I should take to further diagnose the problem?


Solution

  • The problem here was related to lazy evaluation - b2 could not have been generated in the first place, as it was derived from a larger list:

    // message:seq<byte>        
    let firstKeysizeBytes = Seq.take keysize message
    let secondKeysizeBytes = Seq.skip keysize message |> Seq.take keysize
    

    The exception just doesn't pop until the eventual sequence is forced to evaluate. The error message placement is extremely confusing, but such is life with lazy evaluation. I should probably move to using a strict sequential type.