anchor.diff:          printfn "test sparse index %d ms" sw.ElapsedMilliseconds
           result
   
  -    let testSimpleRandom = testRandomQuery AsciiProvider.convertToSimple Reader.simpleQuery
  +    let testSimpleRandom = testRandomQuery AsciiProvider.convertToSimple (fun indices fileName -> SimpleReadWrite.readValues fileName indices)
  +    let testMemoryRandom = testRandomQuery AsciiProvider.convertToSimple (fun indices fileName -> MemoryMappedSimpleRead.readValues fileName indices)
       let testSparseRandom = testRandomQuery AsciiProvider.convertToSparse Reader.sparseQuery 
  +    let testBatchRandom = testRandomQuery AsciiProvider.convertToSimple (fun indices fileName -> BatchOrderedRead.readValues fileName indices)
  +
       let testSmallMarspec() =
           let outerlen = 10
           let innerlen = 10000
  +        let result3 = testBatchRandom "test batch SmallMarspec" [|@"D:\a\data\marspec\MARSPEC_10m\ascii\bathy_10m.asc"|] outerlen innerlen
           let result1 = testSimpleRandom "test simple SmallMarspec" [|@"D:\a\data\marspec\MARSPEC_10m\ascii\bathy_10m.asc"|] outerlen innerlen
           let result2 = testSparseRandom "test sparse SmallMarspec" [|@"D:\a\data\marspec\MARSPEC_10m\ascii\bathy_10m.asc"|] outerlen innerlen
  -        printfn "simple result: %A" result1
  -        printfn "sparse result: %A" result2
  +                
  +//        printfn "simple result: %A" result1
  +//        printfn "sparse result: %A" result2
  +//        printfn "batch result: %A" result2
  +        ()
   
       let testAllBioOracle() =
           ignore // TODO
  
positive.code:         converter fileName sbg
  
      let testRandomQuery converter query name paths outerlen innerlen =
          printfn "START %s" name
          let sbgPaths = paths |> Array.map (testPrepareLoad converter)
          
          let r = new Random(1)
  
negative.code:             |> List.map (readValue map reader)
          values
  
  module BatchOrderedRead = // asummes sorted and distinct input
  
      let readValuesBatch (reader:BinaryReader) (indices:int64 []) = 
          // set stream to correct location
          let first,last = indices.[0], (indices.[indices.Length-1])
          reader.BaseStream.Position <- first*4L
          let bytes = reader.ReadBytes(int (((last + 1L) - first) * 4L))
          let readValue index =
              match (BitConverter.ToInt32(bytes, int (index-first))) with 
              | Int32.MinValue -> None 
              | v -> Some(v)
  
          Array.map readValue indices
          
      let readValues fileName indices = 
          use reader = new BinaryReader(File.Open(fileName, FileMode.Open, FileAccess.Read, FileShare.Read))
          // Use list or array to force creation of values (otherwise reader gets disposed before the values are read)
          let batchSize = 400L
          let folder (first,lists) i = 
              if ((i - first) < batchSize) && ((i - first) >= 0L) then 
                  match lists with
                  | x::xs -> (first, (i::x)::xs)
                  | [] -> (i, [[i]])
              else
                  (i, ([i]::lists))
              
          let grouped = indices |> Seq.fold folder (-1L,[]) |> snd |> List.map (List.rev>>Array.ofList) |> List.rev
          grouped |> List.map (readValuesBatch reader)
  
  module AsciiProvider = 
      let parseValue nodata (v:string) =
          if v <> nodata then
  