anchor.diff:  
   let input = new StreamReader(``pride and prejudice``)
   //let input = new StreamReader(test)
  -let next  = input.ReadLine
  +
   let (|IsAlnum|_|) c = if System.Char.IsLetterOrDigit c then Some () else None
  -let (|Int|) input = System.Int32.Parse input
   
  -// very imperative, but the goal is to work with limited amount of memory
  +// note: each line in word_freqs is "word,count", so these two funcs just make 
  +// the code easier to read
  +let getWord (x : obj) = (x :?> string).Split(',').[0]
  +let getFreq (x : obj) = (x :?> string).Split(',').[1] |> Int32.Parse
  +
   let findWords (line : string) =
       seq {
  -        let mutable startIdx, len = -1, 0
  +        data.[2] <- -1 :> obj
  +        data.[3] <- 0  :> obj
           for i = 0 to line.Length-1 do
  -            match line.[i], startIdx with
  -            | IsAlnum, -1 -> startIdx <- i; len <- 1   // start of word
  -            | IsAlnum, _  -> len <- len + 1            // another char for word
  +            match line.[i], data.[2] :?> int with
  +            | IsAlnum, -1 -> // start of word
  +                data.[2] <- i :> obj
  +                data.[3] <- 1 :> obj
  +            | IsAlnum, _  -> // another char for word
  +                data.[3] <- (data.[3] :?> int + 1) :> obj
               | _, -1 -> () // no word, not started, ignore
               | _ -> // end of word
  -                yield line.Substring(startIdx, len)
  -                startIdx <- -1; len <- 0
  +                yield line.Substring(data.[2] :?> int, data.[3] :?> int)
  +                data.[2] <- -1 :> obj
  +                data.[3] <- 0  :> obj
   
  -        if len > 0 then yield line.Substring(startIdx, len)
  +        if (data.[3] :?> int) > 0 then 
  +            yield line.Substring(data.[2] :?> int, data.[3] :?> int)
       }
       |> Seq.map (fun x -> x.ToLower())
  -    |> Seq.filter (fun x -> x.Length >= 2 && stopWords.Contains x |> not)    
  +    |> Seq.filter (fun x -> x.Length >= 2 && stopWords.Contains x |> not)
   
  -let write (stream : Stream) word count = 
  -    let content = sprintf "%s,%6d\n" word count
  -    let bytes = System.Text.Encoding.ASCII.GetBytes(content)
  -    stream.Write(bytes, 0, bytes.Length)
  +let write (stream : Stream) word count =
  +    data.[5] <- sprintf "%s,%6d\n" word count :> obj
  +
  +    printfn "writing %s %d" word count
  +
  +    stream.Write(Encoding.ASCII.GetBytes(data.[5] :?> string), 0, (data.[5] :?> string).Length)
       stream.Flush()
   
   let incrFreq (word : string) =
       // since the reader reads 128 chars at a time, so use this to track where we are
  -    let mutable readPosition = 0L
  +    data.[6] <- 0L :> obj
       wordFreqs.Seek(0L, SeekOrigin.Begin) |> ignore
  -    use reader = new StreamReader(wordFreqs, System.Text.Encoding.ASCII, false, 128, true)
  +    use reader = new StreamReader(wordFreqs, Encoding.ASCII, false, 128, true)
   
  -    let rec loop : string -> bool = function
  -        | null -> false // end of file, word not found
  -        | line -> 
  -            let [| word'; (Int count) |] = line.Split(',')
  -            let lineLen = int64 line.Length + 1L // +1 for newline
  -            readPosition <- readPosition + lineLen
  -            if word' = word then
  -                wordFreqs.Seek(readPosition - lineLen, SeekOrigin.Begin) |> ignore
  -                write wordFreqs word' (count + 1)
  -                true
  -            else
  -                loop <| reader.ReadLine()
  +    data.[7] <- reader.ReadLine() :> obj
  +    while data.[7] <> null && data.[6] <> null do        
  +        if word = getWord data.[7] then
  +            wordFreqs.Seek(data.[6] :?> int64, SeekOrigin.Begin) |> ignore
  +            write wordFreqs word (getFreq data.[7] + 1)
   
  -    let found = loop <| reader.ReadLine()
  -    if not found then write wordFreqs word 1
  +            // set read pos to null to indicate we've found and incremented count
  +            data.[6] <- null
  +        else
  +            // note: +1 here to account for the newline character
  +            data.[6] <- (data.[6] :?> int64 + int64 (data.[7] :?> string).Length + 1L) :> obj
  +            data.[7] <- reader.ReadLine() :> obj
   
  -let rec proc (next : unit -> string) =
  -    match next() with
  -    | null -> () // end of file
  -    | line -> 
  -        findWords line |> Seq.iter incrFreq
  -        proc next
  -
  -proc next
  +    if data.[6] <> null then
  +        write wordFreqs word 1
  +    
  +data.[1] <- input.ReadLine() :> obj
  +while (data.[1] <> null) do
  +    data.[1] :?> string |> findWords |> Seq.iter incrFreq
  +    data.[1] <- input.ReadLine() :> obj
   
   // PART 2
   wordFreqs.Seek(0L, SeekOrigin.Begin)
  
positive.code: open System.IO
  #time
  let ``pride and prejudice`` = __SOURCE_DIRECTORY__ + "../pride-and-prejudice.txt"
  let test = __SOURCE_DIRECTORY__ + "../test.txt"
  
negative.code: 4. find the 25 most frequent words in secondary memory (file)
  *)
  
  // simulate the absense of identifiers, so everything goes into this array which
  // acts as the 'main memory'
  // 0 = stop words
  // 1 = input buffer (1024 bytes)
  // 2 = start index (used in findWords)
  // 3 = length of word (used in findWords)
  // 4 = the word 
  // 5 = write buffer (word + count)
  // 6 = current position in word_freqs
  // 7 = current word_freqs line
  // 8 = current min word freq of the top 25
  let data = Array.zeroCreate<obj> 9
  
  // PART 1
  let stopWords = File.ReadAllText(__SOURCE_DIRECTORY__ + "../stop_words.txt")
  data.[0] <- stopWords.Split(',') |> Set.ofArray :> obj
  
  let wordFreqs = File.Open(__SOURCE_DIRECTORY__ + "../word_freqs.txt", 
                            FileMode.Create, 
  