docs: def synchronize_files(inputpaths, outpath, database=None, tqdm_bar=None, report_file=None, ptee=None, verbose=False):
      ''' Main function to synchronize files contents by majority vote
      The main job of this function is to walk through the input folders and align the files, so that we can compare every files across every folders, one by one.
      The whole trick here is to align files, so that we don't need to memorize all the files in memory and we compare all equivalent files together: to do that, we ensure that we walk through the input directories in alphabetical order, and we pick the relative filepath at the top of the alphabetical order, this ensures the alignment of files between different folders, without memorizing  the whole trees structures.
      '''
      # (Generator) Files Synchronization Algorithm:
      # Needs a function stable_dir_walking, which will walk through directories recursively but in always the same order on all platforms (same order for files but also for folders), whatever order it is, as long as it is stable.
      # Until there's no file in any of the input folders to be processed:
      # - curfiles <- load first file for each folder by using stable_dir_walking on each input folder.
      # - curfiles_grouped <- group curfiles_ordered:
      #    * curfiles_ordered <- order curfiles alphabetically (need to separate the relative parent directory and the filename, to account for both without ambiguity)
      #    * curfiles_grouped <- empty list
      #    * curfiles_grouped[0] = add first element in curfiles_ordered
      #    * last_group = 0
      #    * for every subsequent element nextelt in curfiles_ordered:
      #        . if nextelt == curfiles_grouped[last_group][0]: add nextelt into curfiles_grouped[last_group] (the latest group in curfiles_grouped)
      #        . else: create a new group in curfiles_grouped (last_group += 1) and add nextelt into curfiles_grouped[last_group]
      # At this stage, curfiles_grouped[0] should contain a group of files with the same relative filepath from different input folders, and since we used stable_dir_walking, we are guaranteed that this file is the next to be processed in alphabetical order.
      # - Majority vote byte-by-byte for each of curfiles_grouped[0], and output winning byte to the output file.
      # - Update files list alignment: we will now ditch files in curfiles_grouped[0] from curfiles, and replace by the next files respectively from each respective folder. Since we processed in alphabetical (or whatever) order, the next loaded files will match the files in other curfiles_grouped groups that we could not process before.
      # At this point (after the loop), all input files have been processed in order, without maintaining the whole files list in memory, just one file per input folder.
  
      # Init files walking generator for each inputpaths
      recgen = [recwalk(path, sorting=True) for path in inputpaths]
      curfiles = {}
      recgen_exhausted = {}
      recgen_exhausted_count = 0
      nbpaths = len(inputpaths)
      retcode = 0
  
      if not ptee: ptee = sys.stdout
  
      # Open report file and write header
      if report_file is not None:
          rfile = open(report_file, 'wb')
          r_writer = csv.writer(rfile, delimiter='|', lineterminator='\n', quotechar='"')
          r_header = ["filepath"] + ["dir%i" % (i+1) for i in xrange(nbpaths)] + ["hash-correct", "error_code", "errors"]
          r_length = len(r_header)
          r_writer.writerow(r_header)
  
      # Initialization: load the first batch of files, one for each folder
      for i in xrange(len(recgen)):
          recgen_exhausted[i] = False
          try:
              if curfiles.get(i, None) is None:
                  curfiles[i] = relpath_posix(recgen[i].next(), inputpaths[i])[1]
          except StopIteration:
              recgen_exhausted[i] = True
              recgen_exhausted_count += 1
  
      # Files lists alignment loop
      while recgen_exhausted_count < nbpaths:
          errcode = 0
          errmsg = None
  
          # Init a new report's row
          if report_file: r_row = ["-"] * r_length
  
          # -- Group equivalent relative filepaths together
          #print curfiles # debug
          curfiles_grouped = sort_group(curfiles, True)
  
          # -- Extract first group of equivalent filepaths (this allows us to process with the same alphabetical order on all platforms)
          # Note that the remaining files in other groups will be processed later, because their alphabetical order is higher to the first group, this means that the first group is to be processed now
          to_process = curfiles_grouped[0]
          #print to_process # debug
  
          # -- Byte-by-byte majority vote on the first group of files
          # Need the relative filepath also (note that there's only one since it's a group of equivalent relative filepaths, only the absolute path is different between files of a same group)
          relfilepath = path2unix(os.path.join(*to_process[0][1]))
          if report_file: r_row[0] = relfilepath
          if verbose: ptee.write("- Processing file %s." % relfilepath)
          # Generate output path
          outpathfull = os.path.join(outpath, relfilepath)
          create_dir_if_not_exist(os.path.dirname(outpathfull))
          # Initialize the list of absolute filepaths
          fileslist = []
          for elt in to_process:
              i = elt[0]
              fileslist.append(os.path.join(inputpaths[i], os.path.join(*elt[1])))
              if report_file: r_row[i+1] = 'X' # put an X in the report file below each folder that contains this file
          # If there's only one file, just copy it over
          if len(to_process) == 1:
              shutil.copyfile(fileslist[0], outpathfull)
              id = to_process[0][0]
              if report_file: r_row[id+1] = 'O'
          # Else, merge by majority vote
          else:
              # Before-merge check using rfigc database, if provided
              # If one of the files in the input folders is already correct, just copy it over
              correct_file = None
              if database:
                  for id, filepath in enumerate(fileslist):
                      if rfigc.main("-i \"%s\" -d \"%s\" -m --silent" % (filepath, database)) == 0:
                          correct_file = filepath
                          correct_id = to_process[id][0]
                          break
  
              # If one correct file was found, copy it over
              if correct_file:
                  create_dir_if_not_exist(os.path.dirname(outpathfull))
                  shutil.copyfile(correct_file, outpathfull)
                  if report_file:
                      r_row[correct_id+1] = "O"
                      r_row[-3] = "OK"
              # Else, we need to do the majority vote merge
              else:
                  # Do the majority vote merge
                  errcode, errmsg = majority_vote_byte_scan(relfilepath, fileslist, outpath)
  
          # After-merge/move check using rfigc database, if provided
          if database:
              if rfigc.main("-i \"%s\" -d \"%s\" -m --silent" % (outpathfull, database)) == 1:
                  errcode = 1
                  r_row[-3] = "KO"
                  if not errmsg: errmsg = ''
                  errmsg += " File could not be totally repaired according to rfigc database."
              else:
                  if report_file:
                      r_row[-3] = "OK"
                      if errmsg: errmsg += " But merged file is correct according to rfigc database."
  
          # Display errors if any
          if errcode:
              if report_file:
                  r_row[-2] = "KO"
                  r_row[-1] = errmsg
              ptee.write(errmsg)
              retcode = 1
          else:
              if report_file: r_row[-2] = "OK"
  
          # Save current report's row
          if report_file:
              r_writer.writerow(r_row)
  
          # -- Update files lists alignment (ie, retrieve new files but while trying to keep the alignment)
          for elt in to_process:  # for files of the first group (the ones we processed)
              i = elt[0]
              # Walk their respective folders and load up the next file
              try:
                  if not recgen_exhausted.get(i, False):
                      curfiles[i] = relpath_posix(recgen[i].next(), inputpaths[i])[1]
              # If there's no file left in this folder, mark this input folder as exhausted and continue with the others
              except StopIteration:
                  curfiles[i] = None
                  recgen_exhausted[i] = True
                  recgen_exhausted_count += 1
          if tqdm_bar: tqdm_bar.update()
      if tqdm_bar: tqdm_bar.close()
  
      # Closing report file
      if report_file:
          # Write list of directories and legend
          rfile.write("\n=> Input directories:")
          for id, ipath in enumerate(inputpaths):
              rfile.write("\n\t- dir%i = %s" % ((id+1), ipath))
          rfile.write("\n=> Output directory: %s" % outpath)
          rfile.write("\n=> Legend: X=existing/selected for majority vote, O=only used this file, - = not existing, OK = check correct, KO = check incorrect (file was not recovered)\n")
          # Close the report file handle
          rfile.close()
  
      return retcode