anchor.diff:      else ","
     let file = try CsvFile.Parse(data,separators) with _ -> parseError "Failed to parse data as a CSV file."
     if Seq.isEmpty file.Rows then parseError "The specified CSV file contains no data."
  +
     let meta = 
       file.Rows 
  -    |> Seq.truncate 20
  -    |> Seq.map (fun r -> Array.map isNumeric r.Columns)
  -    |> Seq.reduce (Array.map2 (&&))
  +    |> Seq.truncate 100
  +    |> Seq.map (fun r -> Array.map inferType r.Columns)
  +    |> fun x -> printfn "%A" x; x
  +    |> Seq.reduce (Array.map2 unifyTypes)
       |> Seq.zip file.Headers.Value
       |> Array.ofSeq
     
  
positive.code: exception ParseError of string
  
  let parseError s = raise (ParseError s)
  let isNumeric (s:string) = Decimal.TryParse(s) |> fst
  
  let readCsvFile (data:string) =   
    if String.IsNullOrWhiteSpace data then parseError "The specified input was empty."
  
negative.code: let inline pickField name obj = 
    Array.pick (fun (n, v) -> if n = name then Some v else None) obj
  
  let asString = function String s -> s | Number n -> string n | Date d -> d.ToString("g") | Bool b -> string b
  let asDecimal = function String s -> decimal s | Number n -> n | Date d -> decimal d.Ticks | Bool true -> 1M | Bool false -> 0M
  
  let applyGroupAggregation kvals group = function
   | GroupAggregation.GroupKey -> kvals
   | GroupAggregation.CountAll -> [ "count", Number(group |> Seq.length |> decimal) ]
   | GroupAggregation.CountDistinct(fld) -> [ fld, Number(group |> Seq.distinctBy (pickField fld) |> Seq.length |> decimal) ]
   | GroupAggregation.ConcatValues(fld) -> [ fld, group |> Seq.map(fun obj -> pickField fld obj |> asString) |> Seq.distinct |> String.concat ", " |> String ]
   | GroupAggregation.Sum(fld) -> [ fld, group |> Seq.sumBy (fun obj -> pickField fld obj |> asDecimal) |> Number ]
   | GroupAggregation.Mean(fld) -> [ fld, group |> Seq.averageBy (fun obj -> pickField fld obj |> asDecimal) |> Number ]
  
  let applyWinAggregation kname group agg = 
    let nums fld = group |> Seq.map (fun obj -> pickField fld obj |> asDecimal)
    let kvalues = Seq.map (pickField kname) group |> Array.ofSeq
    match agg with
    | WindowAggregation.Mean(fld) -> [ fld, nums fld |> Seq.average |> Number ]
    | WindowAggregation.Min(fld) -> [ fld, nums fld |> Seq.min |> Number ]
    | WindowAggregation.Max(fld) -> [ fld, nums fld |> Seq.max |> Number ]
    | WindowAggregation.FirstKey -> [ "first " + kname, kvalues.[0] ]
    | WindowAggregation.LastKey -> [ "last " + kname, kvalues.[kvalues.Length-1] ]
    | WindowAggregation.MiddleKey -> [ "middle " + kname, kvalues.[(kvalues.Length-1)/2] ]
  
  let compareFields o1 o2 (fld, order) = 
    let reverse = if order = Descending then -1 else 1
  