anchor.diff:      string lpExistingFileName,    
       IntPtr lpSecurityAttributes)
   
  +
  +module Directory = 
  +    /// get all transformed files of a directory by extension
  +    let rec GetFilesTransformed transform caseSensitive ext dir =
  +        let files =  try Directory.GetFiles dir with | _ -> [||]
  +        let dirs = try Directory.GetDirectories dir with | _ -> [||]
  +        seq { for file in files do
  +                  if caseSensitive then 
  +                      if file.EndsWith(ext) then 
  +                          yield (transform file)
  +                  else
  +                      if file.ToLowerInvariant().EndsWith(ext.ToLowerInvariant()) then 
  +                          yield (transform file)
  +              for sub in dirs do
  +                  yield! GetFilesTransformed transform caseSensitive ext sub }
  +
  +
   module Utils =
       /// Create hard link at dest with source
       let CreateHardLink dest source =
           Kernel32.CreateHardLink(dest, source, IntPtr.Zero)
  +    
  +    /// get all transformed files of a sequence of directories by extension
  +    let GetFilesTransformed transform caseSensitive ext dirs =
  +        dirs |> Seq.collect (Directory.GetFilesTransformed transform caseSensitive ext) |> Array.ofSeq
   
  -    
  -    /// get all FileInfo for ext in dir
  -    let GetFileTransformed transform caseSensitive ext dirs =
  -        let getFiles dir =  try Directory.GetFiles dir with | _ -> [||]
  -        let getDirectories dir = try Directory.GetDirectories dir with | _ -> [||]
  -        let rec getFileInfo caseSensitive ext dir =
  -            seq{ for file in getFiles dir do
  -                     if caseSensitive then 
  -                         if file.EndsWith(ext) then 
  -                             yield (transform file)
  -                     else
  -                         if file.ToLowerInvariant().EndsWith(ext.ToLowerInvariant()) then 
  -                             yield (transform file)
  -                 for sub in getDirectories dir do
  -                     yield! getFileInfo caseSensitive ext sub }
  -        dirs |> Seq.collect (getFileInfo caseSensitive ext) |> Array.ofSeq 
  +    /// get FileInfo of all files of a sequence of directories by extension
  +    let GetFileInfos caseSensitive ext dirs = 
  +        GetFilesTransformed (fun x -> new FileInfo(x)) caseSensitive ext dirs
   
  -    let GetFileInfo caseSensitive ext dirs = 
  -        GetFileTransformed (fun x -> new FileInfo(x)) caseSensitive ext dirs
  -
  -    let GetFile caseSensitive ext dirs = 
  -        GetFileTransformed id caseSensitive ext dirs
  +    /// get FullName of all files of a sequence of directories by extension
  +    let GetFiles caseSensitive ext dirs = 
  +        GetFilesTransformed id caseSensitive ext dirs
   
       let hashTypes = 
           [|"MD5"; "SHA1"; "SHA256"; "SHA384"; "SHA512"; "RIPEMD160"|] 
           |> Array.map (fun t -> t, HashAlgorithm.Create(t))
   
  -    let GetFileHashedDiagnostic verbose (hashTypes: (string*HashAlgorithm)[]) (files : string[]) =
  +    /// get array of variants of hashes for all given files
  +    let GetFilesHashedDiagnostic verbose (hashTypes: (string*HashAlgorithm)[]) (files : string[]) =
           let hashedFile file =       
               try
                   let content = File.ReadAllBytes(file)
  -                [| for (t,f) in hashTypes do
  -                    let zero = System.DateTime.Now.Ticks
  -                    let bytes = content |> f.ComputeHash
  -                    let ms = (System.DateTime.Now.Ticks - zero) / 1000L
  -                    let h = BitConverter.ToString(bytes).Replace("-", "").ToLowerInvariant()
  -                    if verbose then printfn "[%6d ms] %s %s" ms t h
  -                    yield ms, t, h, file |]
  +                let hashes =
  +                    [| for (t,f) in hashTypes do
  +                        let zero = System.DateTime.Now.Ticks
  +                        let bytes = content |> f.ComputeHash
  +                        let ms = (System.DateTime.Now.Ticks - zero) / 1000L
  +                        let hash = BitConverter.ToString(bytes).Replace("-", "").ToLowerInvariant()
  +                        if verbose then printfn "[%6d ms] %s %s" ms t hash
  +                        yield ms, t, hash |]
  +                (hashes, file)
                   |> Some
               with _ -> None
           files |> Array.map hashedFile |> Array.choose id
   
  +    /// get SHA1-hashes with label and elapsed calculation time for all given files
  +    let GetFileSHA1 verbose files = 
  +        files
  +        |> GetFilesHashedDiagnostic verbose [|"SHA1", HashAlgorithm.Create("SHA1")|] 
  +        |> Array.map (fun (h,f) -> 
  +            let h_ms, h_t, h_hash = h.[0]
  +            (h_ms, h_t, h_hash, f))
   
  -    let GetFileSHA1 verbose files = 
  -        GetFileHashedDiagnostic verbose [|"SHA1", HashAlgorithm.Create("SHA1")|] files
  +    /// get SHA1-grouping of the given files
  +    let GetFilesGroupedBySHA1 verbose files = 
  +        files
  +        |> GetFileSHA1 verbose 
  +        |> Seq.ofArray 
  +        |> Seq.groupBy (fun (_,_,h,_) -> h) 
  +        
  +    /// coerce files into hard links
  +    let rec CoerceFilesIntoHardLinks (files : seq<string>) = 
  +        let head = files |> Seq.head
  +        let tail = files |> Seq.skip 1
  +        let head2 = Seq.head tail
  +        if (try File.Delete(head2); true with | _ as ex -> false) then
  +            (try CreateHardLink head2 head with | _ as ex -> false) |> ignore
  +        else 
  +            CoerceFilesIntoHardLinks (seq { yield head; yield! (tail |> Seq.skip 1) })
   
   
   module Tests =
  
positive.code:     let msSHA1 = (System.DateTime.Now.Ticks - zeroSHA1) / 1000L
      let ``Utils.GetFileSHA1``() =
          hashedFilesSHA1        
  
      let Run() =
          ``Kernel32.Create hard link``()
          ``Utils.GetFile``() |> printfn "%d"
          ``Utils.GetFileHashedDiagnostic``() |> printfn "%A"
          ``Utils.GetFileSHA1``()  |> printfn "%A"
          printfn "[SHA1] hashed %d files in %d ms" hashedFilesSHA1.Length msSHA1
negative.code:     let files = 
          [|Environment.GetEnvironmentVariable("PROJECTS")
            Environment.GetEnvironmentVariable("HOMEPATH")|]
          |> fun x -> Utils.GetFiles false "dll" x
      let ``Utils.GetFile``() = files |> Array.length
  
      let hashedFilesDiagnostic = files.[0..90] |> Utils.GetFilesHashedDiagnostic true Utils.hashTypes
      let ``Utils.GetFileHashedDiagnostic``() =
          hashedFilesDiagnostic
  
  