docs: def demultiplex_samples(fastq, out_dir, nedit, barcodes):
      ''' Demultiplex a fastqtransformed FASTQ file into a FASTQ file for
      each sample.
      '''
      annotations = detect_fastq_annotations(fastq)
      re_string = construct_transformed_regex(annotations)
      parser_re = re.compile(re_string)
  
      if barcodes:
          barcodes = set(barcode.strip() for barcode in barcodes)
      else:
          barcodes = set()
  
      if nedit == 0:
          filter_bc = partial(exact_sample_filter, barcodes=barcodes)
      else:
          barcodehash = MutationHash(barcodes, nedit)
          filter_bc = partial(correcting_sample_filter, barcodehash=barcodehash)
  
      sample_set = set()
      batch = collections.defaultdict(list)
      parsed = 0
      safe_makedir(out_dir)
      for read in read_fastq(fastq):
          parsed += 1
          read = filter_bc(read)
          if not read:
              continue
          match = parser_re.search(read).groupdict()
          sample = match['SB']
          sample_set.add(sample)
          batch[sample].append(read)
          # write in batches to avoid opening up file handles repeatedly
          if not parsed % 10000000:
              for sample, reads in batch.items():
                  out_file = os.path.join(out_dir, sample + ".fq")
                  with open(out_file, "a") as out_handle:
                      for read in reads:
                          fixed = filter_bc(read)
                          if fixed:
                              out_handle.write(fixed)
              batch = collections.defaultdict(list)
  
      for sample, reads in batch.items():
          out_file = os.path.join(out_dir, sample + ".fq")
          with open(out_file, "a") as out_handle:
              for read in reads:
                  fixed = filter_bc(read)
                  if fixed:
                      out_handle.write(read)