From 1c4a20f572b7663d646f73f768e6627f1ce9f035 Mon Sep 17 00:00:00 2001 From: Maryam Date: Fri, 12 Jun 2026 11:49:00 -0700 Subject: [PATCH 01/52] do not attemp to read the fasta file multiple times --- bygul/_cli.py | 1 + bygul/utils.py | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/bygul/_cli.py b/bygul/_cli.py index e0ac912..6112268 100644 --- a/bygul/_cli.py +++ b/bygul/_cli.py @@ -120,6 +120,7 @@ def simulate_proportions( ) # validare simulation arugments validate_simulation_args(simulation_mode, primers, reference) + reference = next(SeqIO.parse(reference, "fasta")) # needed to pass simulation specific flags extra_simulator_flags = ctx.args ctx = click.get_current_context() diff --git a/bygul/utils.py b/bygul/utils.py index cddacf1..7bd9225 100644 --- a/bygul/utils.py +++ b/bygul/utils.py @@ -169,7 +169,6 @@ def assess_genome_quality_from_fasta(fasta_path): def extract_sequence(reference, chrom, start, end): """Extracts sequence from reference based on coordinates and strand.""" - reference = next(SeqIO.parse(reference, "fasta")) if reference.id not in chrom: raise ValueError(f"Chromosome {chrom} not found in reference.") seq = reference.seq[start:end] # Extract sequence From d707265ac376f6ba3274e8505b1de0156f47fa6c Mon Sep 17 00:00:00 2001 From: Maryam Date: Fri, 12 Jun 2026 11:55:49 -0700 Subject: [PATCH 02/52] add missing comment --- bygul/_cli.py | 1 + 1 file changed, 1 insertion(+) diff --git a/bygul/_cli.py b/bygul/_cli.py index 6112268..0532ab4 100644 --- a/bygul/_cli.py +++ b/bygul/_cli.py @@ -120,6 +120,7 @@ def simulate_proportions( ) # validare simulation arugments validate_simulation_args(simulation_mode, primers, reference) + # read the reference sequence reference = next(SeqIO.parse(reference, "fasta")) # needed to pass simulation specific flags extra_simulator_flags = ctx.args From 7e481132d0d2e423244d48b700907f7b3a3cca36 Mon Sep 17 00:00:00 2001 From: Maryam Date: Fri, 12 Jun 2026 11:59:26 -0700 Subject: [PATCH 03/52] read reference sequence only once in check-primers function --- bygul/_cli.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bygul/_cli.py b/bygul/_cli.py index 0532ab4..bedc9db 100644 --- a/bygul/_cli.py +++ b/bygul/_cli.py @@ -295,6 +295,8 @@ def check_primers(genomes, primers, reference, maxmismatch): ) assess_genome_quality_from_fasta(genomes) + # read the reference sequence + reference = next(SeqIO.parse(reference, "fasta")) primer_df = preprocess_primers(primers, reference) print("Reading and preprocessing the primer file...") all_results = [] From c5000c49cb90bc00983bd42b46f91a8f616fab28 Mon Sep 17 00:00:00 2001 From: Maryam Date: Mon, 15 Jun 2026 10:44:13 -0700 Subject: [PATCH 04/52] change apply to itertuples for faster operation --- bygul/utils.py | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/bygul/utils.py b/bygul/utils.py index 7bd9225..087b1e3 100644 --- a/bygul/utils.py +++ b/bygul/utils.py @@ -346,15 +346,10 @@ def preprocess_primers(primer_file, reference): "This is not recommended.", UserWarning, ) - primer_bed["primer_seq"] = primer_bed.apply( - lambda row: extract_sequence( - reference, - row["ref"], - row["start"], - row["end"], - ), - axis=1, - ) + primer_bed["primer_seq"] = [ + extract_sequence(reference, row.ref, row.start, row.end) + for row in primer_bed.itertuples(index=False) + ] else: neg_strand = primer_bed["strand"] == "-" primer_bed.loc[neg_strand, "primer_seq"] = ( From 5267d9e42132166243612d2c80f1a4de7de9cf7e Mon Sep 17 00:00:00 2001 From: Maryam Date: Mon, 15 Jun 2026 10:47:53 -0700 Subject: [PATCH 05/52] use pure string reverse compliment instead of Bio Seq reverse complement for faster operation --- bygul/utils.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/bygul/utils.py b/bygul/utils.py index 087b1e3..6be0be4 100644 --- a/bygul/utils.py +++ b/bygul/utils.py @@ -352,10 +352,11 @@ def preprocess_primers(primer_file, reference): ] else: neg_strand = primer_bed["strand"] == "-" - primer_bed.loc[neg_strand, "primer_seq"] = ( - primer_bed.loc[neg_strand, "primer_seq"] - .apply(lambda seq: str(Seq(seq).reverse_complement())) - ) + _rc = str.maketrans("ACGTacgt", "TGCAtgca") + primer_bed.loc[neg_strand, "primer_seq"] = [ + seq.translate(_rc)[::-1] + for seq in primer_bed.loc[neg_strand, "primer_seq"] + ] # split the amplicon name into number and left/right primer_bed["amplicon_number"] = primer_bed["left_right"].str.split( "_").str[1] From 55832fe56ba793e5014bc06ea80a3ad954ccef4f Mon Sep 17 00:00:00 2001 From: Maryam Date: Mon, 15 Jun 2026 10:49:26 -0700 Subject: [PATCH 06/52] lint --- bygul/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bygul/utils.py b/bygul/utils.py index 6be0be4..f06d5e7 100644 --- a/bygul/utils.py +++ b/bygul/utils.py @@ -347,14 +347,14 @@ def preprocess_primers(primer_file, reference): UserWarning, ) primer_bed["primer_seq"] = [ - extract_sequence(reference, row.ref, row.start, row.end) + extract_sequence(reference, row.ref, row.start, row.end) for row in primer_bed.itertuples(index=False) ] else: neg_strand = primer_bed["strand"] == "-" _rc = str.maketrans("ACGTacgt", "TGCAtgca") primer_bed.loc[neg_strand, "primer_seq"] = [ - seq.translate(_rc)[::-1] + seq.translate(_rc)[::-1] for seq in primer_bed.loc[neg_strand, "primer_seq"] ] # split the amplicon name into number and left/right From ba2e7040af5c938e266ea040cef2bbeb50659696 Mon Sep 17 00:00:00 2001 From: Maryam Date: Mon, 15 Jun 2026 10:53:33 -0700 Subject: [PATCH 07/52] move log message before the primer processing has started --- bygul/_cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bygul/_cli.py b/bygul/_cli.py index bedc9db..cb03bc8 100644 --- a/bygul/_cli.py +++ b/bygul/_cli.py @@ -143,8 +143,8 @@ def simulate_proportions( # read counts defined pased on proportions read_cnts = [i * int(readcnt) for i in proportions] if simulation_mode == "amplicon": - df_primers_template = preprocess_primers(primers, reference) print("Reading and preprocessing the primer file...") + df_primers_template = preprocess_primers(primers, reference) with tqdm(total=len(sample_names), desc="Simulation progress...") as pbar: From d50a63845014693b935021bab0b0ddb39d10aff0 Mon Sep 17 00:00:00 2001 From: Maryam Date: Mon, 15 Jun 2026 10:56:56 -0700 Subject: [PATCH 08/52] read count integer coversion not to be repeated in every loop --- bygul/_cli.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bygul/_cli.py b/bygul/_cli.py index cb03bc8..52988d1 100644 --- a/bygul/_cli.py +++ b/bygul/_cli.py @@ -141,7 +141,8 @@ def simulate_proportions( sample_names, outdir) # read counts defined pased on proportions - read_cnts = [i * int(readcnt) for i in proportions] + rc = int(readcnt) + read_cnts = [i * rc for i in proportions] if simulation_mode == "amplicon": print("Reading and preprocessing the primer file...") df_primers_template = preprocess_primers(primers, reference) From f5b76c3d1cb3a5cb00c1d5d5ecfb6a9084c9fa66 Mon Sep 17 00:00:00 2001 From: Maryam Date: Mon, 15 Jun 2026 11:20:10 -0700 Subject: [PATCH 09/52] move ambiguity function outside find_closest_primer_match for more optimized performance --- bygul/utils.py | 35 +++++++++++++++++------------------ 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/bygul/utils.py b/bygul/utils.py index f06d5e7..d51decc 100644 --- a/bygul/utils.py +++ b/bygul/utils.py @@ -524,6 +524,23 @@ def run_simulation_on_fasta_single_genome( print(f"An error occurred while running the command: {e}") +# Extended ambiguity-aware mismatch display +def mismatch_alignment(primer, matched_seq): + """ + Returns matched sequence with mismatches shown in parentheses. + Also returns a flag if primer contains any ambiguous base. + """ + ambiguous_bases = {'R', 'Y', 'S', 'W', + 'K', 'M', 'B', 'D', + 'H', 'V', 'N'} + has_ambiguity = any(base in ambiguous_bases + for base in matched_seq.upper()) + aligned = [] + for p, m in zip(primer.upper(), matched_seq.upper()): + aligned.append(m if p == m else f"({m})") + return "".join(aligned), has_ambiguity + + def find_closest_primer_match(df, reference_seq, maxmismatch): """ For each row in df, find all left/right primer match positions (as lists), @@ -531,26 +548,8 @@ def find_closest_primer_match(df, reference_seq, maxmismatch): on the same strand. Returns original df columns + matches, mismatch maps, strand, and whether primers contain ambiguous bases (IUPAC codes). """ - - # Extended ambiguity-aware mismatch display - def mismatch_alignment(primer, matched_seq): - """ - Returns matched sequence with mismatches shown in parentheses. - Also returns a flag if primer contains any ambiguous base. - """ - ambiguous_bases = {'R', 'Y', 'S', 'W', - 'K', 'M', 'B', 'D', - 'H', 'V', 'N'} - has_ambiguity = any(base in ambiguous_bases - for base in matched_seq.upper()) - aligned = [] - for p, m in zip(primer.upper(), matched_seq.upper()): - aligned.append(m if p == m else f"({m})") - return "".join(aligned), has_ambiguity - results = [] warned = False - for _, row in df.iterrows(): primer_left = row["primer_seq_x"] primer_right = row["primer_seq_y"] From d77800684f26fec141ca77d0b1e3ca886367dfe4 Mon Sep 17 00:00:00 2001 From: Maryam Date: Mon, 15 Jun 2026 11:33:41 -0700 Subject: [PATCH 10/52] cvhange iterrows with itertuples for faster perfomance --- bygul/utils.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/bygul/utils.py b/bygul/utils.py index d51decc..c7eb2bf 100644 --- a/bygul/utils.py +++ b/bygul/utils.py @@ -550,9 +550,9 @@ def find_closest_primer_match(df, reference_seq, maxmismatch): """ results = [] warned = False - for _, row in df.iterrows(): - primer_left = row["primer_seq_x"] - primer_right = row["primer_seq_y"] + for row in df.itertuples(index=False): + primer_left = row.primer_seq_x + primer_right = row.primer_seq_y pattern_left = f"({primer_left}){{s<={maxmismatch}}}" pattern_right = f"({primer_right}){{s<={maxmismatch}}}" @@ -631,7 +631,7 @@ def find_closest_primer_match(df, reference_seq, maxmismatch): "Matches may be unreliable.") warned = True - result_row = row.to_dict() + result_row = row._asdict() result_row["ambiguous_bases"] = has_ambiguous_base if left_fwd and right_fwd: From 64f5d5fd4c6802f81af2d72efb4cd228627a6326 Mon Sep 17 00:00:00 2001 From: Maryam Date: Mon, 15 Jun 2026 11:34:30 -0700 Subject: [PATCH 11/52] lint --- bygul/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bygul/utils.py b/bygul/utils.py index c7eb2bf..99b3735 100644 --- a/bygul/utils.py +++ b/bygul/utils.py @@ -531,8 +531,8 @@ def mismatch_alignment(primer, matched_seq): Also returns a flag if primer contains any ambiguous base. """ ambiguous_bases = {'R', 'Y', 'S', 'W', - 'K', 'M', 'B', 'D', - 'H', 'V', 'N'} + 'K', 'M', 'B', 'D', + 'H', 'V', 'N'} has_ambiguity = any(base in ambiguous_bases for base in matched_seq.upper()) aligned = [] From ab15c84cca5ee8555b75b2869b51e0f7f327fa94 Mon Sep 17 00:00:00 2001 From: Maryam Date: Tue, 16 Jun 2026 14:04:26 -0700 Subject: [PATCH 12/52] parallelize samples for faster performance --- bygul/_cli.py | 174 +++++++++++++------------------------------------ bygul/utils.py | 100 ++++++++++++++++++++++++++++ 2 files changed, 145 insertions(+), 129 deletions(-) diff --git a/bygul/_cli.py b/bygul/_cli.py index 52988d1..6a639c5 100644 --- a/bygul/_cli.py +++ b/bygul/_cli.py @@ -4,6 +4,7 @@ from tqdm import tqdm import numpy as np import pandas as pd +from concurrent.futures import ProcessPoolExecutor, as_completed @click.group(context_settings={"show_default": True}) @@ -106,17 +107,13 @@ def simulate_proportions( ): from bygul.utils import ( preprocess_primers, - create_valid_primer_combinations, - make_amplicon, - write_fasta_group, - run_simulation_on_fasta, - run_simulation_on_fasta_single_genome, merge_fastq_files, - find_closest_primer_match, assess_genome_quality_from_fasta, validate_simulation_args, check_dir, - process_sample_proportions + process_sample_proportions, + process_amplicon_worker, + process_genome_worker ) # validare simulation arugments validate_simulation_args(simulation_mode, primers, reference) @@ -143,136 +140,55 @@ def simulate_proportions( # read counts defined pased on proportions rc = int(readcnt) read_cnts = [i * rc for i in proportions] + + # Global outputs for merging + output_path1 = os.path.join(os.path.abspath(outdir), "reads_1.fastq") + output_path2 = os.path.join(os.path.abspath(outdir), "reads_2.fastq") + if simulation_mode == "amplicon": print("Reading and preprocessing the primer file...") df_primers_template = preprocess_primers(primers, reference) - with tqdm(total=len(sample_names), - desc="Simulation progress...") as pbar: - for name, path, cnt in zip(sample_names, sample_paths, read_cnts): - print(f"Processing sample {name}...") - # List to store amplicon - # DataFrames from all contigs in this sample - sample_amplicons_list = [] - # Iterate through EVERY contig/sequence in the FASTA file - for genome_seq in SeqIO.parse(path, "fasta"): - print(" Extracting amplicons" - f"from contig: {genome_seq.id}...") - # Find matches on the current contig - contig_df = find_closest_primer_match( - df_primers_template.copy(), - str(genome_seq.seq), - maxmismatch) - all_amplicons = create_valid_primer_combinations(contig_df) - all_amplicons = all_amplicons.fillna(0) - # Calculate lengths - all_amplicons["amplicon_length"] = np.where( - (all_amplicons["primer_start"] != 0) & - (all_amplicons["primer_end"] != 0), - all_amplicons["primer_end"] - - all_amplicons["primer_start"] + - all_amplicons["primer_seq_y"].str.len(), - 0, - ) - - # Generate sequences for this contig - all_amplicons["amplicon_sequence"] = all_amplicons.apply( - lambda row: make_amplicon( - row["primer_start"], - row["primer_end"], - row["primer_seq_y"], - genome_seq.seq, - ), - axis=1, - ) - # Tag with contig ID to avoid - # confusion if IDs overlap between contigs - all_amplicons["contig_id"] = genome_seq.id - sample_amplicons_list.append(all_amplicons) + task_args = [ + (name, path, cnt, df_primers_template, maxmismatch, outdir, + simulator, wgsim_insert_size, wgsim_read_length, wgsim_error_rate, + extra_simulator_flags) + for name, path, cnt in zip(sample_names, sample_paths, read_cnts) + ] + worker_func = process_amplicon_worker - # Combine results from all contigs for this sample - if not sample_amplicons_list: - print(f"Warning: No sequences found in {path}") - continue - full_sample_df = pd.concat(sample_amplicons_list, - ignore_index=True) - # Output directory setup - amp_out_dir = os.path.join(outdir, name, "amplicons") - os.makedirs(amp_out_dir, exist_ok=True) - full_sample_df.to_csv(os.path.join(amp_out_dir, - "amplicon_stats.csv"), - index=False) - # Write FASTA files for simulation - full_sample_df["amplicon_suffix"] =\ - full_sample_df["amplicon_number"].apply( - lambda x: x.split("_")[0] if "_" in x else x - ) - for n, g in full_sample_df.groupby("amplicon_suffix"): - write_fasta_group(g, n, amp_out_dir) - - print("Starting read simulation...") - read_dir = os.path.join(outdir, name, "reads") - os.makedirs(read_dir, exist_ok=True) - - fasta_files = [ - os.path.join(amp_out_dir, f) - for f in os.listdir(amp_out_dir) - if f.endswith((".fasta", ".fa")) - ] - - for fasta_file in fasta_files: - run_simulation_on_fasta( - fasta_file, - read_dir, - cnt, - simulator, - wgsim_insert_size, - wgsim_read_length, - wgsim_error_rate, - extra_flags=extra_simulator_flags - ) - # Merging logic remains the same - read_path1 = os.path.join(os.path.abspath(outdir), - name, "reads/merged_reads_1.fastq") - read_path2 = os.path.join(os.path.abspath(outdir), - name, "reads/merged_reads_2.fastq") - output_path1 = os.path.join(os.path.abspath(outdir), - "reads_1.fastq") - output_path2 = os.path.join(os.path.abspath(outdir), - "reads_2.fastq") - print(f"Merging reads for {name}...") - merge_fastq_files(read_path1, output_path1) - merge_fastq_files(read_path2, output_path2) - pbar.update(1) - print("Finished all samples!") else: + # Setup for standard/else clause simulation mapping + task_args = [ + (name, path, cnt, outdir, simulator, extra_simulator_flags) + for name, path, cnt in zip(sample_names, sample_paths, read_cnts) + ] + worker_func = process_genome_worker + # Run Parallel Pool Execution + print(f"Spinning up parallel execution for {len(sample_names)} samples...") + with ProcessPoolExecutor() as executor: + futures = {executor.submit(worker_func, + arg): arg[0] for arg in task_args} + with tqdm(total=len(sample_names), desc="Simulation progress...") as pbar: - for name, path, cnt in zip(sample_names, sample_paths, read_cnts): - if not os.path.exists(os.path.join(outdir, name, "reads")): - os.makedirs(os.path.join(outdir, name, "reads")) - run_simulation_on_fasta_single_genome( - path, - os.path.join(outdir, name, "reads"), - cnt, - simulator, - extra_flags=extra_simulator_flags - ) - read_path1 = os.path.join( - os.path.abspath(outdir), name, "reads/reads_1.fastq" - ) - read_path2 = os.path.join( - os.path.abspath(outdir), name, "reads/reads_2.fastq" - ) - output_path1 = os.path.join( - os.path.abspath(outdir), "reads_1.fastq") - output_path2 = os.path.join( - os.path.abspath(outdir), "reads_2.fastq") - print("Merging all reads...") - merge_fastq_files(read_path1, output_path1) - merge_fastq_files(read_path2, output_path2) - print("Finished!") - pbar.update(1) + for future in as_completed(futures): + sample_name = futures[future] + try: + result = future.result() + if result[0] == "success": + _, name, r_path1, r_path2 = result + print(f"\nMerging reads for {name}...") + merge_fastq_files(r_path1, output_path1) + merge_fastq_files(r_path2, output_path2) + elif result[0] == "warning": + print(f"\n[{sample_name}] {result[1]}") + except Exception as e: + print(f"\nError processing sample {sample_name}: {e}") + finally: + pbar.update(1) + + print("Finished all samples!") @cli.command() diff --git a/bygul/utils.py b/bygul/utils.py index 99b3735..0a4892e 100644 --- a/bygul/utils.py +++ b/bygul/utils.py @@ -725,3 +725,103 @@ def write_fasta_group(group, amplicon_number, output_dir): "no file written.please checkout the amplicon_stats.csv " "file for more information." ) + + +def process_amplicon_worker(args): + """Worker for the 'amplicon' simulation mode.""" + (name, path, cnt, df_primers_template, maxmismatch, outdir, + simulator, wgsim_insert_size, wgsim_read_length, wgsim_error_rate, + extra_simulator_flags) = args + + sample_amplicons_list = [] + + for genome_seq in SeqIO.parse(path, "fasta"): + contig_df = find_closest_primer_match( + df_primers_template.copy(), + str(genome_seq.seq), + maxmismatch + ) + all_amplicons = create_valid_primer_combinations(contig_df) + all_amplicons = all_amplicons.fillna(0) + + all_amplicons["amplicon_length"] = np.where( + (all_amplicons["primer_start"] != 0) & + (all_amplicons["primer_end"] != 0), + all_amplicons["primer_end"] - + all_amplicons["primer_start"] + + all_amplicons["primer_seq_y"].str.len(), + 0, + ) + + all_amplicons["amplicon_sequence"] = all_amplicons.apply( + lambda row: make_amplicon( + row["primer_start"], + row["primer_end"], + row["primer_seq_y"], + genome_seq.seq, + ), axis=1, + ) + all_amplicons["contig_id"] = genome_seq.id + sample_amplicons_list.append(all_amplicons) + + if not sample_amplicons_list: + return ("warning", f"Warning: No sequences found in {path}") + + full_sample_df = pd.concat(sample_amplicons_list, ignore_index=True) + amp_out_dir = os.path.join(outdir, name, "amplicons") + os.makedirs(amp_out_dir, exist_ok=True) + full_sample_df.to_csv(os.path.join(amp_out_dir, + "amplicon_stats.csv"), + index=False) + + full_sample_df["amplicon_suffix"] = full_sample_df[ + "amplicon_number"].apply( + lambda x: x.split("_")[0] if "_" in x else x + ) + for n, g in full_sample_df.groupby("amplicon_suffix"): + write_fasta_group(g, n, amp_out_dir) + + read_dir = os.path.join(outdir, name, "reads") + os.makedirs(read_dir, exist_ok=True) + + fasta_files = [ + os.path.join(amp_out_dir, f) + for f in os.listdir(amp_out_dir) + if f.endswith((".fasta", ".fa")) + ] + + for fasta_file in fasta_files: + run_simulation_on_fasta( + fasta_file, read_dir, cnt, simulator, + wgsim_insert_size, wgsim_read_length, wgsim_error_rate, + extra_flags=extra_simulator_flags + ) + + # Expected paths for merging step in main thread + read_path1 = os.path.join(os.path.abspath(outdir), + name, "reads/merged_reads_1.fastq") + read_path2 = os.path.join(os.path.abspath(outdir), + name, "reads/merged_reads_2.fastq") + return ("success", name, read_path1, read_path2) + + +def process_genome_worker(args): + """Worker for the default/standard genome simulation mode (else clause).""" + name, path, cnt, outdir, simulator, extra_simulator_flags = args + read_dir = os.path.join(outdir, name, "reads") + os.makedirs(read_dir, exist_ok=True) + + run_simulation_on_fasta_single_genome( + path, + read_dir, + cnt, + simulator, + extra_flags=extra_simulator_flags + ) + + # Expected paths for merging step in main thread + read_path1 = os.path.join(os.path.abspath(outdir), + name, "reads/reads_1.fastq") + read_path2 = os.path.join(os.path.abspath(outdir), + name, "reads/reads_2.fastq") + return ("success", name, read_path1, read_path2) From 9a332aa9b395cd0549578ac3afdc0a2cfa1a4330 Mon Sep 17 00:00:00 2001 From: Joshua Levy Date: Wed, 17 Jun 2026 14:02:34 -0700 Subject: [PATCH 13/52] Efficient merging of fastqfiles --- bygul/utils.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/bygul/utils.py b/bygul/utils.py index 0a4892e..10165df 100644 --- a/bygul/utils.py +++ b/bygul/utils.py @@ -259,9 +259,8 @@ def merge_fastq_files(fastq_file, output_file): fastq_file (str): Path to the input FASTQ file. output_file (str): Path to the output FASTQ file. """ - command = f'cat "{fastq_file}" >> "{output_file}"' - subprocess.call(command, shell=True) - + with open(fastq_file, "rb") as src, open(output_file, "ab") as dst: + shutil.copyfileobj(src, dst) def create_valid_primer_combinations(df): # Ensure the column exists before assignment @@ -469,9 +468,8 @@ def run_simulation_on_fasta( except subprocess.CalledProcessError as e: print(f"An error occurred while running the command: {e}") # Merge the contig-specific output into the final merged output files - command_merge = f'cat "{output1}" >> "{merged_output1}" \ - && cat "{output2}" >> "{merged_output2}"' - subprocess.call(command_merge, shell=True) + merge_fastq_files(output1, merged_output1) + merge_fastq_files(output2, merged_output2) def run_simulation_on_fasta_single_genome( From fbe562bd76f8eb58a0bc2e4e0171cdb8bcfa0846 Mon Sep 17 00:00:00 2001 From: Joshua Levy Date: Wed, 17 Jun 2026 14:04:03 -0700 Subject: [PATCH 14/52] Efficient merging of fastqfiles-lint --- bygul/utils.py | 1 + 1 file changed, 1 insertion(+) diff --git a/bygul/utils.py b/bygul/utils.py index 10165df..aeeaabe 100644 --- a/bygul/utils.py +++ b/bygul/utils.py @@ -262,6 +262,7 @@ def merge_fastq_files(fastq_file, output_file): with open(fastq_file, "rb") as src, open(output_file, "ab") as dst: shutil.copyfileobj(src, dst) + def create_valid_primer_combinations(df): # Ensure the column exists before assignment if "valid_combinations" not in df.columns: From 56d77566b9f32e9d3950bd841abf1c8ce3ca56e9 Mon Sep 17 00:00:00 2001 From: Maryam Date: Tue, 30 Jun 2026 11:20:59 -0700 Subject: [PATCH 15/52] only run reverse search when no forward matches found --- bygul/utils.py | 133 ++++++++++++++++++++++++++++--------------------- 1 file changed, 75 insertions(+), 58 deletions(-) diff --git a/bygul/utils.py b/bygul/utils.py index aeeaabe..2180544 100644 --- a/bygul/utils.py +++ b/bygul/utils.py @@ -556,80 +556,97 @@ def find_closest_primer_match(df, reference_seq, maxmismatch): pattern_left = f"({primer_left}){{s<={maxmismatch}}}" pattern_right = f"({primer_right}){{s<={maxmismatch}}}" - # Forward strand search - left_fwd = [m.start() for m in re.finditer(pattern_left, - reference_seq, - flags=re.IGNORECASE, - overlapped=True)] - right_fwd = [m.start() for m in re.finditer(pattern_right, - reference_seq, - flags=re.IGNORECASE, - overlapped=True)] - - left_fwd_actual = [reference_seq[pos:pos+len(primer_left)] - for pos in left_fwd] - right_fwd_actual = [reference_seq[pos:pos+len(primer_right)] - for pos in right_fwd] + # --- 1. INITIALIZE REVERSE VARIABLES UP FRONT --- + left_rev, right_rev = [], [] + left_rev_actual, right_rev_actual = [], [] + left_rev_mismatch_map, right_rev_mismatch_map = [], [] + left_rev_has_ambig, right_rev_has_ambig = [], [] + + # --- 2. FORWARD STRAND SEARCH --- + left_fwd_data = [ + (m.start(), m.group()) + for m in re.finditer(pattern_left, reference_seq, + flags=re.IGNORECASE, overlapped=True) + ] + right_fwd_data = [ + (m.start(), m.group()) + for m in re.finditer(pattern_right, reference_seq, + flags=re.IGNORECASE, overlapped=True) + ] + + left_fwd = [pos for pos, _ in left_fwd_data] + right_fwd = [pos for pos, _ in right_fwd_data] + + left_fwd_actual = [seq for _, seq in left_fwd_data] + right_fwd_actual = [seq for _, seq in right_fwd_data] + left_fwd_mismatch_map = [] left_fwd_has_ambig = [] for seq in left_fwd_actual: aligned, has_ambig = mismatch_alignment(primer_left, seq) left_fwd_mismatch_map.append(aligned) left_fwd_has_ambig.append(has_ambig) + right_fwd_mismatch_map = [] right_fwd_has_ambig = [] for seq in right_fwd_actual: aligned, has_ambig = mismatch_alignment(primer_right, seq) right_fwd_mismatch_map.append(aligned) right_fwd_has_ambig.append(has_ambig) - # Reverse strand search - # get complimentary reverse of primers - # right and left primer change direction - right_rev = str(Seq(primer_left).reverse_complement()) - left_rev = str(Seq(primer_right).reverse_complement()) - pattern_left_rev = f"({left_rev}){{s<={maxmismatch}}}" - pattern_right_rev = f"({right_rev}){{s<={maxmismatch}}}" - # Forward strand search - left_rev_pos = [m.start() for m in re.finditer(pattern_left_rev, - reference_seq, - flags=re.IGNORECASE, - overlapped=True)] - right_rev_pos = [m.start() for m in re.finditer(pattern_right_rev, - reference_seq, - flags=re.IGNORECASE, - overlapped=True)] - left_rev_actual = [reference_seq[pos:pos+len(left_rev)] - for pos in left_rev_pos] - right_rev_actual = [reference_seq[pos:pos+len(right_rev)] - for pos in right_rev_pos] - left_rev_mismatch_map = [] - left_rev_has_ambig = [] - for seq in left_rev_actual: - aligned, has_ambig = mismatch_alignment(left_rev, seq) - left_rev_mismatch_map.append(aligned) - left_rev_has_ambig.append(has_ambig) - right_rev_mismatch_map = [] - right_rev_has_ambig = [] - for seq in right_rev_actual: - aligned, has_ambig = mismatch_alignment(right_rev, seq) - right_rev_mismatch_map.append(aligned) - right_rev_has_ambig.append(has_ambig) - - # Check if any ambiguous bases are in the primers or alignments + + # --- 3. REVERSE STRAND SEARCH (IF FORWARD FAILS) --- + if not left_fwd or not right_fwd: + # Using unique variable names (_seq) to avoid overwriting coordinate lists + right_rev_seq = str(Seq(primer_left).reverse_complement()) + left_rev_seq = str(Seq(primer_right).reverse_complement()) + + pattern_left_rev = f"({left_rev_seq}){{s<={maxmismatch}}}" + pattern_right_rev = f"({right_rev_seq}){{s<={maxmismatch}}}" + + left_rev_data = [ + (m.start(), m.group()) + for m in re.finditer(pattern_left_rev, reference_seq, + flags=re.IGNORECASE, overlapped=True) + ] + right_rev_data = [ + (m.start(), m.group()) + for m in re.finditer(pattern_right_rev, reference_seq, + flags=re.IGNORECASE, overlapped=True) + ] + + left_rev = [pos for pos, _ in left_rev_data] + right_rev = [pos for pos, _ in right_rev_data] + + left_rev_actual = [seq for _, seq in left_rev_data] + right_rev_actual = [seq for _, seq in right_rev_data] + + for seq in left_rev_actual: + aligned, has_ambig = mismatch_alignment(left_rev_seq, seq) + left_rev_mismatch_map.append(aligned) + left_rev_has_ambig.append(has_ambig) + + for seq in right_rev_actual: + aligned, has_ambig = mismatch_alignment(right_rev_seq, seq) + right_rev_mismatch_map.append(aligned) + right_rev_has_ambig.append(has_ambig) + + # --- 4. AMBIGUOUS BASE CHECK --- has_ambiguous_base = any([ any(b in primer_left.upper() for b in "RYSWKMBDHVN"), any(b in primer_right.upper() for b in "RYSWKMBDHVN"), - any(left_fwd_has_ambig) if left_fwd_has_ambig else False, - any(right_fwd_has_ambig) if right_fwd_has_ambig else False, - any(left_rev_has_ambig) if left_rev_has_ambig else False, - any(right_rev_has_ambig) if right_rev_has_ambig else False, + any(left_fwd_has_ambig), + any(right_fwd_has_ambig), + any(left_rev_has_ambig), + any(right_rev_has_ambig), ]) + if has_ambiguous_base and not warned: - warnings.warn("One or more primers contain ambiguous" - "bases (e.g., N, R, Y, etc)." + warnings.warn("One or more primers contain ambiguous " + "bases (e.g., N, R, Y, etc). " "Matches may be unreliable.") warned = True + # --- 5. BUILD RESULT DICTIONARY --- result_row = row._asdict() result_row["ambiguous_bases"] = has_ambiguous_base @@ -642,10 +659,10 @@ def find_closest_primer_match(df, reference_seq, maxmismatch): "left_match": left_fwd_mismatch_map, "right_match": right_fwd_mismatch_map, }) - elif left_rev_pos and right_rev_pos: + elif left_rev and right_rev: # Fixed variable naming conflict here result_row.update({ - "left_primer_loc": left_rev_pos, - "right_primer_loc": right_rev_pos, + "left_primer_loc": left_rev, + "right_primer_loc": right_rev, "left_seq_actual": left_rev_actual, "right_seq_actual": right_rev_actual, "left_match": left_rev_mismatch_map, @@ -662,7 +679,7 @@ def find_closest_primer_match(df, reference_seq, maxmismatch): }) results.append(result_row) - + return pd.DataFrame(results) From 485d36557cae54b7ea108f12dd073e673f627a97 Mon Sep 17 00:00:00 2001 From: Maryam Date: Tue, 30 Jun 2026 11:30:40 -0700 Subject: [PATCH 16/52] lint --- bygul/utils.py | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/bygul/utils.py b/bygul/utils.py index 2180544..9697fd4 100644 --- a/bygul/utils.py +++ b/bygul/utils.py @@ -573,7 +573,6 @@ def find_closest_primer_match(df, reference_seq, maxmismatch): for m in re.finditer(pattern_right, reference_seq, flags=re.IGNORECASE, overlapped=True) ] - left_fwd = [pos for pos, _ in left_fwd_data] right_fwd = [pos for pos, _ in right_fwd_data] @@ -586,7 +585,6 @@ def find_closest_primer_match(df, reference_seq, maxmismatch): aligned, has_ambig = mismatch_alignment(primer_left, seq) left_fwd_mismatch_map.append(aligned) left_fwd_has_ambig.append(has_ambig) - right_fwd_mismatch_map = [] right_fwd_has_ambig = [] for seq in right_fwd_actual: @@ -596,13 +594,12 @@ def find_closest_primer_match(df, reference_seq, maxmismatch): # --- 3. REVERSE STRAND SEARCH (IF FORWARD FAILS) --- if not left_fwd or not right_fwd: - # Using unique variable names (_seq) to avoid overwriting coordinate lists + # Using unique variable names (_seq) + # to avoid overwriting coordinate lists right_rev_seq = str(Seq(primer_left).reverse_complement()) left_rev_seq = str(Seq(primer_right).reverse_complement()) - pattern_left_rev = f"({left_rev_seq}){{s<={maxmismatch}}}" pattern_right_rev = f"({right_rev_seq}){{s<={maxmismatch}}}" - left_rev_data = [ (m.start(), m.group()) for m in re.finditer(pattern_left_rev, reference_seq, @@ -613,18 +610,15 @@ def find_closest_primer_match(df, reference_seq, maxmismatch): for m in re.finditer(pattern_right_rev, reference_seq, flags=re.IGNORECASE, overlapped=True) ] - left_rev = [pos for pos, _ in left_rev_data] right_rev = [pos for pos, _ in right_rev_data] left_rev_actual = [seq for _, seq in left_rev_data] right_rev_actual = [seq for _, seq in right_rev_data] - for seq in left_rev_actual: aligned, has_ambig = mismatch_alignment(left_rev_seq, seq) left_rev_mismatch_map.append(aligned) left_rev_has_ambig.append(has_ambig) - for seq in right_rev_actual: aligned, has_ambig = mismatch_alignment(right_rev_seq, seq) right_rev_mismatch_map.append(aligned) @@ -639,7 +633,6 @@ def find_closest_primer_match(df, reference_seq, maxmismatch): any(left_rev_has_ambig), any(right_rev_has_ambig), ]) - if has_ambiguous_base and not warned: warnings.warn("One or more primers contain ambiguous " "bases (e.g., N, R, Y, etc). " @@ -679,7 +672,6 @@ def find_closest_primer_match(df, reference_seq, maxmismatch): }) results.append(result_row) - return pd.DataFrame(results) From 5c2dd3c25842c209e2b1fd02bcca9412171d6efc Mon Sep 17 00:00:00 2001 From: Maryam Date: Tue, 30 Jun 2026 13:36:50 -0700 Subject: [PATCH 17/52] use basic string instead of Bio complimentary reverse --- bygul/utils.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/bygul/utils.py b/bygul/utils.py index 9697fd4..1d00121 100644 --- a/bygul/utils.py +++ b/bygul/utils.py @@ -594,10 +594,10 @@ def find_closest_primer_match(df, reference_seq, maxmismatch): # --- 3. REVERSE STRAND SEARCH (IF FORWARD FAILS) --- if not left_fwd or not right_fwd: - # Using unique variable names (_seq) - # to avoid overwriting coordinate lists - right_rev_seq = str(Seq(primer_left).reverse_complement()) - left_rev_seq = str(Seq(primer_right).reverse_complement()) + complement_table = str.maketrans("ATCGatcg", "TAGCtagc") + # Reverse complement without Bio Python + right_rev_seq = primer_left.translate(complement_table)[::-1] + left_rev_seq = primer_right.translate(complement_table)[::-1] pattern_left_rev = f"({left_rev_seq}){{s<={maxmismatch}}}" pattern_right_rev = f"({right_rev_seq}){{s<={maxmismatch}}}" left_rev_data = [ From 9425dfa76a01f9859580d7fa266754ad8f0b8dca Mon Sep 17 00:00:00 2001 From: Maryam Date: Wed, 1 Jul 2026 15:24:48 -0700 Subject: [PATCH 18/52] have --genomes as an option now that a csv file can be used --- bygul/_cli.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/bygul/_cli.py b/bygul/_cli.py index 6a639c5..9ba7163 100644 --- a/bygul/_cli.py +++ b/bygul/_cli.py @@ -18,7 +18,10 @@ def cli(): allow_extra_args=True, )) @click.pass_context -@click.argument("genomes", type=str) +@click.option("--genomes", + default="NA", + help="Comma-separated list of" + "genome file paths in fasta format") @click.option( "--reference", default="NA", From e43fb8a3e0319b5ff7a5560e57d913a1f1a0de3c Mon Sep 17 00:00:00 2001 From: Maryam Date: Wed, 1 Jul 2026 15:28:26 -0700 Subject: [PATCH 19/52] read samples and proportions as a cvs file --- bygul/_cli.py | 39 +++++++++++++++++++++++++++++++-------- bygul/utils.py | 38 +++++++++++++++++++++++--------------- 2 files changed, 54 insertions(+), 23 deletions(-) diff --git a/bygul/_cli.py b/bygul/_cli.py index 9ba7163..7fdc178 100644 --- a/bygul/_cli.py +++ b/bygul/_cli.py @@ -45,6 +45,17 @@ def cli(): "If not provided, the program will randomly assign proportions" ), ) +@click.option( + "--csv", + default="NA", + type=click.Path(exists=True, dir_okay=False), + help=( + "Path to a CSV file containing sample paths " + "and proportions. The CSV should have two columns: " + "'sample_path' and 'proportion'. " + "Cannot be used together with --proportions --genomes" + ), +) @click.option( "--outdir", default="results", @@ -106,7 +117,8 @@ def simulate_proportions( maxmismatch, simulator, redo, - simulation_mode + simulation_mode, + csv ): from bygul.utils import ( preprocess_primers, @@ -119,7 +131,9 @@ def simulate_proportions( process_genome_worker ) # validare simulation arugments - validate_simulation_args(simulation_mode, primers, reference) + validate_simulation_args(simulation_mode, primers, + reference, proportions, + csv, genomes) # read the reference sequence reference = next(SeqIO.parse(reference, "fasta")) # needed to pass simulation specific flags @@ -127,10 +141,18 @@ def simulate_proportions( ctx = click.get_current_context() # check directory exists- if redo specified make again check_dir(outdir, redo) - # split the sample names and paths into a list - sample_names = [fp.split("/")[-1].split(".")[0] - for fp in str(genomes).split(",")] - sample_paths = str(genomes).split(",") + if csv: + # read the CSV file and extract sample paths and proportions + df = pd.read_csv(csv) + sample_paths = df['sample_path'].tolist() + proportions = df['proportion'].tolist() + sample_names = [os.path.basename(fp).split(".")[0] + for fp in sample_paths] + else: + # split the sample names and paths into a list + sample_names = [fp.split("/")[-1].split(".")[0] + for fp in str(genomes).split(",")] + sample_paths = str(genomes).split(",") # Print information about the quality of the provided file for genome in sample_paths: assess_genome_quality_from_fasta(genome) @@ -138,8 +160,9 @@ def simulate_proportions( # assign proportions randomly if not provided proportions = process_sample_proportions(proportions, sample_names, - sample_names, - outdir) + sample_paths, + outdir, + csv) # read counts defined pased on proportions rc = int(readcnt) read_cnts = [i * rc for i in proportions] diff --git a/bygul/utils.py b/bygul/utils.py index 1d00121..46f4ba2 100644 --- a/bygul/utils.py +++ b/bygul/utils.py @@ -17,22 +17,21 @@ def process_sample_proportions( proportions, sample_names, sample_paths, - outdir + outdir, + csv, ): """ Processes sample proportions for read simulation. Parameters: - proportions (str or list): Either "NA" or comma-separated proportions. - sample_names (list): List of sample names. - sample_paths (list): List of sample file paths. - outdir (str): Output directory. - - Returns: - list: Processed proportions as floats. + proportions (str or list): Either "NA", a comma-separated string, + or a list of proportions from a CSV. """ - if proportions == "NA": + if csv: + proportions = [float(p) for p in proportions] + + elif proportions == "NA": if len(sample_names) == 1: print( "Only one sample provided. " @@ -50,19 +49,19 @@ def process_sample_proportions( with open( os.path.join(outdir, "sample_proportions.txt"), - "w" + "w", ) as file: for name, proportion in zip(sample_names, proportions): file.write(f"{name}: {proportion}\n") else: - proportions = list(map(float, str(proportions).split(","))) + proportions = [float(x) for x in proportions.split(",")] # Validate lengths if not ( - len(sample_names) == - len(proportions) == - len(sample_paths) + len(sample_names) + == len(proportions) + == len(sample_paths) ): raise Exception( "Number of samples, proportions, and sample paths should match!" @@ -92,7 +91,8 @@ def check_dir(outdir, redo): os.makedirs(outdir) -def validate_simulation_args(simulation_mode, primers, reference): +def validate_simulation_args(simulation_mode, primers, reference, + proportions, proportions_csv, genomes): if simulation_mode == "amplicon" and primers == "NA": print("Primer file is required for simulation mode amplicon") sys.exit(1) @@ -105,6 +105,14 @@ def validate_simulation_args(simulation_mode, primers, reference): if simulation_mode == "amplicon" and reference == "NA": print("Reference file is required for simulation mode amplicon") sys.exit(1) + if proportions_csv != "NA": + if proportions != "NA": + print("Cannot use --proportions with --csv") + sys.exit(1) + + if genomes != "NA": + print("Cannot use --genomes with --csv") + sys.exit(1) def assess_genome_quality_from_fasta(fasta_path): From dcecb8aae4d8545a274998824574e7056936ab4c Mon Sep 17 00:00:00 2001 From: Maryam Date: Wed, 1 Jul 2026 15:35:12 -0700 Subject: [PATCH 20/52] updating tests --- bygul/tests/data/sample_proportions.csv | 3 +++ bygul/tests/data/test_cli.py | 27 ++++++++++++++++--------- 2 files changed, 21 insertions(+), 9 deletions(-) create mode 100644 bygul/tests/data/sample_proportions.csv diff --git a/bygul/tests/data/sample_proportions.csv b/bygul/tests/data/sample_proportions.csv new file mode 100644 index 0000000..e73f714 --- /dev/null +++ b/bygul/tests/data/sample_proportions.csv @@ -0,0 +1,3 @@ +sample_path,proportion +bygul/tests/data/ATM-2FFMD73N3.fasta,0.54 +bygul/tests/data/CA-SEARCH-43254.fasta,0.46 \ No newline at end of file diff --git a/bygul/tests/data/test_cli.py b/bygul/tests/data/test_cli.py index d5503ca..d2293a9 100644 --- a/bygul/tests/data/test_cli.py +++ b/bygul/tests/data/test_cli.py @@ -14,7 +14,7 @@ def test_version(self): def test_simulation(self): os.system( "bygul simulate-proportions \ - bygul/tests/data/ATM-2FFMD73N3.fasta \ + --genomes bygul/tests/data/ATM-2FFMD73N3.fasta \ --primers bygul/tests/data/ARTIC_V4-1.bed \ --reference bygul/tests/data/reference.fasta --redo" ) @@ -23,7 +23,7 @@ def test_simulation(self): def test_simulation_with_simulator(self): os.system( "bygul simulate-proportions \ - bygul/tests/data/ATM-2FFMD73N3.fasta \ + --genomes bygul/tests/data/ATM-2FFMD73N3.fasta \ --primers bygul/tests/data/ARTIC_V4-1.bed \ --reference bygul/tests/data/reference.fasta \ --simulator wgsim --redo" @@ -33,7 +33,7 @@ def test_simulation_with_simulator(self): def test_simulation_with_max_mismatch(self): os.system( "bygul simulate-proportions \ - bygul/tests/data/ATM-2FFMD73N3.fasta \ + --genomes bygul/tests/data/ATM-2FFMD73N3.fasta \ --primers bygul/tests/data/ARTIC_V4-1.bed \ --reference bygul/tests/data/reference.fasta \ --maxmismatch 2 --redo" @@ -43,7 +43,7 @@ def test_simulation_with_max_mismatch(self): def test_simulation_with_readcnt(self): os.system( "bygul simulate-proportions \ - bygul/tests/data/ATM-2FFMD73N3.fasta \ + --genomes bygul/tests/data/ATM-2FFMD73N3.fasta \ --primers bygul/tests/data/ARTIC_V4-1.bed --readcnt 200 \ --reference bygul/tests/data/reference.fasta --redo" ) @@ -52,7 +52,7 @@ def test_simulation_with_readcnt(self): def test_simulation_with_readlength(self): os.system( "bygul simulate-proportions \ - bygul/tests/data/ATM-2FFMD73N3.fasta \ + --genomes bygul/tests/data/ATM-2FFMD73N3.fasta \ --primers bygul/tests/data/ARTIC_V4-1.bed -1 130 \ -2 130 --reference bygul/tests/data/reference.fasta --redo" ) @@ -61,7 +61,7 @@ def test_simulation_with_readlength(self): def test_simulation_with_2genomes(self): os.system( "bygul simulate-proportions " - "bygul/tests/data/ATM-2FFMD73N3.fasta," + "--genomes bygul/tests/data/ATM-2FFMD73N3.fasta," "bygul/tests/data/KR-SEARCH-120354.fasta " "--primers bygul/tests/data/ARTIC_V4-1.bed " "--reference bygul/tests/data/reference.fasta --redo" @@ -71,7 +71,7 @@ def test_simulation_with_2genomes(self): def test_simulation_with_2genomes_proportions(self): os.system( "bygul simulate-proportions " - "bygul/tests/data/ATM-2FFMD73N3.fasta," + "--genomes bygul/tests/data/ATM-2FFMD73N3.fasta," "bygul/tests/data/KR-SEARCH-120354.fasta " "--primers bygul/tests/data/ARTIC_V4-1.bed " "--reference bygul/tests/data/reference.fasta " @@ -82,7 +82,7 @@ def test_simulation_with_2genomes_proportions(self): def test_simulation_with_metagenomics(self): os.system( "bygul simulate-proportions " - "bygul/tests/data/ATM-2FFMD73N3.fasta," + "--genomes bygul/tests/data/ATM-2FFMD73N3.fasta," "bygul/tests/data/KR-SEARCH-120354.fasta " "--simulation_mode metagenomics " "--proportions 0.8,0.2 --redo -1 150 -2 150" @@ -92,13 +92,22 @@ def test_simulation_with_metagenomics(self): def test_simulation_with_mason(self): os.system( "bygul simulate-proportions " - "bygul/tests/data/ATM-2FFMD73N3.fasta," + "--genomes bygul/tests/data/ATM-2FFMD73N3.fasta," "bygul/tests/data/KR-SEARCH-120354.fasta " "--proportions 0.8,0.2 --redo --simulator mason " "--illumina-read-length 200" ) self.assertTrue(file_exists(".", "results/reads_1.fastq")) + def test_simulation_with_csv(self): + os.system( + "bygul simulate-proportions " + "--csv bygul/tests/data/sample_proportions.csv " + "--redo --simulator mason " + "--illumina-read-length 200" + ) + self.assertTrue(file_exists(".", "results/reads_1.fastq")) + def test_check_primers(self): os.system( "bygul check-primers " From 99d4a1a19faddf6d2ad681fba446c1bc9a1bd95d Mon Sep 17 00:00:00 2001 From: Maryam Date: Thu, 2 Jul 2026 10:04:39 -0700 Subject: [PATCH 21/52] no need to check if the csv file exists as this results in error when not provided --- bygul/_cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bygul/_cli.py b/bygul/_cli.py index 7fdc178..49ab33b 100644 --- a/bygul/_cli.py +++ b/bygul/_cli.py @@ -48,7 +48,7 @@ def cli(): @click.option( "--csv", default="NA", - type=click.Path(exists=True, dir_okay=False), + type=str, help=( "Path to a CSV file containing sample paths " "and proportions. The CSV should have two columns: " From f8d618c08a2d0fca8e61a32404e2efe396680c45 Mon Sep 17 00:00:00 2001 From: Maryam Date: Thu, 2 Jul 2026 10:06:03 -0700 Subject: [PATCH 22/52] check the directories after the names are assigned based on whether a csv file is provided or not- also csv is not a true or false value so change to if it is not NA --- bygul/_cli.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/bygul/_cli.py b/bygul/_cli.py index 49ab33b..207c16c 100644 --- a/bygul/_cli.py +++ b/bygul/_cli.py @@ -139,9 +139,7 @@ def simulate_proportions( # needed to pass simulation specific flags extra_simulator_flags = ctx.args ctx = click.get_current_context() - # check directory exists- if redo specified make again - check_dir(outdir, redo) - if csv: + if csv != "NA": # read the CSV file and extract sample paths and proportions df = pd.read_csv(csv) sample_paths = df['sample_path'].tolist() From 6f8132c6f37efec99f23e4019d6244b4a26a3d4e Mon Sep 17 00:00:00 2001 From: Maryam Date: Thu, 2 Jul 2026 10:08:00 -0700 Subject: [PATCH 23/52] moving directory checking function --- bygul/_cli.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bygul/_cli.py b/bygul/_cli.py index 207c16c..ce613f3 100644 --- a/bygul/_cli.py +++ b/bygul/_cli.py @@ -151,6 +151,8 @@ def simulate_proportions( sample_names = [fp.split("/")[-1].split(".")[0] for fp in str(genomes).split(",")] sample_paths = str(genomes).split(",") + # check directory exists- if redo specified make again + check_dir(outdir, redo, sample_names) # Print information about the quality of the provided file for genome in sample_paths: assess_genome_quality_from_fasta(genome) From f550c8132fdc8e8e0f761136da2842322c62dffa Mon Sep 17 00:00:00 2001 From: Maryam Date: Thu, 2 Jul 2026 10:08:23 -0700 Subject: [PATCH 24/52] csv file is not true/false type --- bygul/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bygul/utils.py b/bygul/utils.py index 4cef1b0..5095290 100644 --- a/bygul/utils.py +++ b/bygul/utils.py @@ -28,7 +28,7 @@ def process_sample_proportions( or a list of proportions from a CSV. """ - if csv: + if csv != "NA": proportions = [float(p) for p in proportions] elif proportions == "NA": From 09ff3cd49c1a03de6cf10d66b7b278f263d91ca1 Mon Sep 17 00:00:00 2001 From: Maryam Date: Thu, 2 Jul 2026 10:08:33 -0700 Subject: [PATCH 25/52] update tests --- bygul/tests/data/test_proportions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bygul/tests/data/test_proportions.py b/bygul/tests/data/test_proportions.py index cb86202..eb4e307 100644 --- a/bygul/tests/data/test_proportions.py +++ b/bygul/tests/data/test_proportions.py @@ -9,7 +9,7 @@ def test_prop_with_freyja(self): # 1. Run the simulation os.system( "bygul simulate-proportions " - "bygul/tests/data/BCN-SEARCH-105346.fasta," + "--genomes bygul/tests/data/BCN-SEARCH-105346.fasta," "bygul/tests/data/CA-SEARCH-43254.fasta " "--primers bygul/tests/data/ARTIC_V4-1.bed " "--reference bygul/tests/data/reference.fasta " From 0509fef50706e9bbea842336d83858ebeb7067f9 Mon Sep 17 00:00:00 2001 From: Maryam Date: Thu, 2 Jul 2026 11:23:52 -0700 Subject: [PATCH 26/52] do not store valid combinations to save time --- bygul/utils.py | 75 +++++++++++++++++++++----------------------------- 1 file changed, 31 insertions(+), 44 deletions(-) diff --git a/bygul/utils.py b/bygul/utils.py index 5095290..9894125 100644 --- a/bygul/utils.py +++ b/bygul/utils.py @@ -290,54 +290,41 @@ def merge_fastq_files(fastq_file, output_file): def create_valid_primer_combinations(df): - # Ensure the column exists before assignment - if "valid_combinations" not in df.columns: - df["valid_combinations"] = None - - valid_primers = [] # Use a list instead of concatenating DataFrames - - for i in range(len(df)): - # Pair coordinates with their mismatch maps - left_coords = zip(df.at[i, "left_primer_loc"], - df.at[i, "left_match"]) - right_coords = zip(df.at[i, "right_primer_loc"], - df.at[i, "right_match"]) - # Safe assignment using .at[] - df.at[i, "valid_combinations"] = evaluate_matches(left_coords, - right_coords) - for ( - primer_start, - primer_end, - left_match, - right_match - ) in df.at[i, "valid_combinations"]: - valid_primers.append( - { - "amplicon_number": df.at[i, "amplicon_number"], - "primer_start": primer_start, - "primer_end": primer_end, - "left_match": left_match, - "right_match": right_match - } - ) + valid_primers = [] + + for row in df.itertuples(index=False): + combinations = evaluate_matches( + zip(row.left_primer_loc, row.left_match), + zip(row.right_primer_loc, row.right_match), + ) + + valid_primers.extend( + { + "amplicon_number": row.amplicon_number, + "primer_start": primer_start, + "primer_end": primer_end, + "left_match": left_match, + "right_match": right_match, + } + for primer_start, primer_end, left_match, right_match in combinations + ) - # Check if we found any valid primers if not valid_primers: raise ValueError( - "No primer matches found, please check your primer file.") - - # Convert collected data to DataFrame efficiently - valid_primers_df = pd.DataFrame.from_records(valid_primers) - # Merge with original DataFrame to include additional columns - df = df[["amplicon_number", "primer_seq_x", - "primer_seq_y", "ambiguous_bases"]] - all_amplicons = df.merge( - valid_primers_df, - how="left", - on="amplicon_number", - ) + "No primer matches found, please check your primer file." + ) - return all_amplicons + return ( + df[[ + "amplicon_number", + "primer_seq_x", + "primer_seq_y", + "ambiguous_bases", + ]] + .merge(pd.DataFrame(valid_primers), + on="amplicon_number", + how="left") + ) def preprocess_primers(primer_file, reference): From 16c6e171f9705df89be5cd14f3dd1fcb1d689fb1 Mon Sep 17 00:00:00 2001 From: Maryam Date: Thu, 2 Jul 2026 11:24:21 -0700 Subject: [PATCH 27/52] lint --- bygul/utils.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bygul/utils.py b/bygul/utils.py index 9894125..f48fa31 100644 --- a/bygul/utils.py +++ b/bygul/utils.py @@ -306,7 +306,8 @@ def create_valid_primer_combinations(df): "left_match": left_match, "right_match": right_match, } - for primer_start, primer_end, left_match, right_match in combinations + for primer_start, primer_end, + left_match, right_match in combinations ) if not valid_primers: From f445451c414fd5f8ac9eea53d38396aa1085f2fa Mon Sep 17 00:00:00 2001 From: Maryam Date: Wed, 8 Jul 2026 11:04:32 -0700 Subject: [PATCH 28/52] assigning proportions based on a multinomial distribution --- bygul/_cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bygul/_cli.py b/bygul/_cli.py index ce613f3..d70259e 100644 --- a/bygul/_cli.py +++ b/bygul/_cli.py @@ -165,7 +165,7 @@ def simulate_proportions( csv) # read counts defined pased on proportions rc = int(readcnt) - read_cnts = [i * rc for i in proportions] + read_cnts = np.random.multinomial(rc, proportions) # Global outputs for merging output_path1 = os.path.join(os.path.abspath(outdir), "reads_1.fastq") From de0360977a3650ccd80cb76ec195817c6ec524f0 Mon Sep 17 00:00:00 2001 From: Maryam Date: Wed, 8 Jul 2026 15:45:57 -0700 Subject: [PATCH 29/52] moving genome quality assessment into the amplicon worker not to read fasta file multiple times --- bygul/_cli.py | 3 --- bygul/utils.py | 25 ++++++++++++------------- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/bygul/_cli.py b/bygul/_cli.py index d70259e..dd99e61 100644 --- a/bygul/_cli.py +++ b/bygul/_cli.py @@ -153,9 +153,6 @@ def simulate_proportions( sample_paths = str(genomes).split(",") # check directory exists- if redo specified make again check_dir(outdir, redo, sample_names) - # Print information about the quality of the provided file - for genome in sample_paths: - assess_genome_quality_from_fasta(genome) # process the proportions and give warnings if necessary # assign proportions randomly if not provided proportions = process_sample_proportions(proportions, diff --git a/bygul/utils.py b/bygul/utils.py index f48fa31..95fd921 100644 --- a/bygul/utils.py +++ b/bygul/utils.py @@ -133,7 +133,7 @@ def validate_simulation_args(simulation_mode, primers, reference, sys.exit(1) -def assess_genome_quality_from_fasta(fasta_path): +def assess_genome_quality_from_fasta(sequence): """ Parses a FASTA genome file and assesses quality by: - Counting ambiguous (non-ACGT) bases. @@ -142,7 +142,7 @@ def assess_genome_quality_from_fasta(fasta_path): - Printing a genome quality summary. Parameters: - fasta_path (str): Path to the FASTA file. + sequence (SeqRecord): The FASTA sequence record. Returns: dict: { @@ -150,15 +150,12 @@ def assess_genome_quality_from_fasta(fasta_path): 'contig_lengths': dict of {contig_id: length} } """ - ambiguous_bases = {'R', 'Y', 'S', 'W', 'K', 'M', 'B', 'D', 'H', 'V', 'N'} total_ambiguous = 0 contig_lengths = {} - - for record in SeqIO.parse(fasta_path, "fasta"): - seq = str(record.seq).upper() - contig_lengths[record.id] = len(seq) - total_ambiguous += sum(1 for base in seq if base in ambiguous_bases) + seq = str(sequence.seq).upper() + contig_lengths[sequence.id] = len(seq) + total_ambiguous += sum(1 for base in seq if base in ambiguous_bases) report = { 'total_ambiguous_bases': total_ambiguous, @@ -171,19 +168,19 @@ def assess_genome_quality_from_fasta(fasta_path): # Warnings if num_ambiguous > 0: warnings.warn( - f"{fasta_path}: Contains {num_ambiguous} ambiguous base(s). " + f"{sequence.id}: Contains {num_ambiguous} ambiguous base(s). " "Please choose a better quality genome." ) if num_contigs > 1: warnings.warn( - f"{fasta_path}: Contains {num_contigs} contigs. " + f"{sequence.id}: Contains {num_contigs} contigs. " "Does your organism have more than one chromosome? " "Are you providing high quality assemblies?" ) # Print results - print(f"\nGenome: {fasta_path}") + print(f"\nGenome: {sequence.id}") print(f" Total ambiguous bases: {num_ambiguous}") print(" Contig lengths:") @@ -756,8 +753,10 @@ def process_amplicon_worker(args): extra_simulator_flags) = args sample_amplicons_list = [] - - for genome_seq in SeqIO.parse(path, "fasta"): + genome_seqs = list(SeqIO.parse(path, "fasta")) + for genome_seq in genome_seqs: + # Print information about the quality of the provided file + assess_genome_quality_from_fasta(genome_seq) contig_df = find_closest_primer_match( df_primers_template.copy(), str(genome_seq.seq), From 833eb12fcda204b3ee0cc382c7573fcba5d41196 Mon Sep 17 00:00:00 2001 From: Maryam Date: Wed, 8 Jul 2026 15:59:49 -0700 Subject: [PATCH 30/52] check_primers -do not repeatedly read the fasta file, load sequences in a list --- bygul/_cli.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bygul/_cli.py b/bygul/_cli.py index dd99e61..93c0cd2 100644 --- a/bygul/_cli.py +++ b/bygul/_cli.py @@ -233,8 +233,8 @@ def check_primers(genomes, primers, reference, maxmismatch): find_closest_primer_match, create_valid_primer_combinations, ) - - assess_genome_quality_from_fasta(genomes) + genome_seqs = list(SeqIO.parse(genomes, "fasta")) + assess_genome_quality_from_fasta(genome_seqs) # read the reference sequence reference = next(SeqIO.parse(reference, "fasta")) primer_df = preprocess_primers(primers, reference) @@ -242,7 +242,7 @@ def check_primers(genomes, primers, reference, maxmismatch): all_results = [] # Parse ALL sequences in the multifasta - for genome_record in SeqIO.parse(genomes, "fasta"): + for genome_record in genome_seqs: genome_id = genome_record.id genome_seq = str(genome_record.seq) df = find_closest_primer_match(primer_df, genome_seq, From 2214c0b9eb7264bc9e113a9a8341beaf48035eaa Mon Sep 17 00:00:00 2001 From: Maryam Date: Wed, 8 Jul 2026 16:00:56 -0700 Subject: [PATCH 31/52] do not need this function imported anymore --- bygul/_cli.py | 1 - 1 file changed, 1 deletion(-) diff --git a/bygul/_cli.py b/bygul/_cli.py index 93c0cd2..274b246 100644 --- a/bygul/_cli.py +++ b/bygul/_cli.py @@ -123,7 +123,6 @@ def simulate_proportions( from bygul.utils import ( preprocess_primers, merge_fastq_files, - assess_genome_quality_from_fasta, validate_simulation_args, check_dir, process_sample_proportions, From a0d013a2d2ed6a6fb0cbd0abad3db62695990323 Mon Sep 17 00:00:00 2001 From: Maryam Date: Thu, 9 Jul 2026 14:13:20 -0700 Subject: [PATCH 32/52] Adding multifasta option and storing sequences in a dictionary --- bygul/_cli.py | 56 +++++++++++++++++++++++++++++++++++--------------- bygul/utils.py | 6 +++--- 2 files changed, 42 insertions(+), 20 deletions(-) diff --git a/bygul/_cli.py b/bygul/_cli.py index 274b246..d69a7c2 100644 --- a/bygul/_cli.py +++ b/bygul/_cli.py @@ -5,7 +5,7 @@ import numpy as np import pandas as pd from concurrent.futures import ProcessPoolExecutor, as_completed - +from collections import defaultdict @click.group(context_settings={"show_default": True}) @click.version_option("3.2.0") @@ -56,6 +56,16 @@ def cli(): "Cannot be used together with --proportions --genomes" ), ) +@click.option( + "--multifasta", + default="NA", + type=str, + help=( + "Path to a multifasta file containing all sample sequences" + "Cannot be used together with --genomes and --proportions." + "Must be used together with --csv" + ), +) @click.option( "--outdir", default="results", @@ -118,7 +128,8 @@ def simulate_proportions( simulator, redo, simulation_mode, - csv + csv, + multifasta ): from bygul.utils import ( preprocess_primers, @@ -132,31 +143,40 @@ def simulate_proportions( # validare simulation arugments validate_simulation_args(simulation_mode, primers, reference, proportions, - csv, genomes) + csv, genomes, multifasta) # read the reference sequence reference = next(SeqIO.parse(reference, "fasta")) # needed to pass simulation specific flags extra_simulator_flags = ctx.args ctx = click.get_current_context() - if csv != "NA": - # read the CSV file and extract sample paths and proportions + genome_map = defaultdict(list) + if csv != "NA" and multifasta != "NA": df = pd.read_csv(csv) - sample_paths = df['sample_path'].tolist() - proportions = df['proportion'].tolist() - sample_names = [os.path.basename(fp).split(".")[0] - for fp in sample_paths] + sample_names = df["sample_name"].tolist() + proportions = df["proportion"].tolist() + for record in SeqIO.parse(multifasta, "fasta"): + sample = record.id.split("_")[0] + genome_map[sample].append(record) + # Ensure every sample in the CSV exists + missing = set(sample_names) - set(genome_map) + if missing: + raise click.ClickException( + "Samples missing from multifasta:" + f" {', '.join(sorted(missing))}" + ) else: - # split the sample names and paths into a list - sample_names = [fp.split("/")[-1].split(".")[0] - for fp in str(genomes).split(",")] sample_paths = str(genomes).split(",") - # check directory exists- if redo specified make again + for path in sample_paths: + for record in SeqIO.parse(path, "fasta"): + sample = record.id.split("_")[0] + genome_map[sample].append(record) + sample_names = list(genome_map.keys()) + # check directory exists- if redo specified make again check_dir(outdir, redo, sample_names) # process the proportions and give warnings if necessary # assign proportions randomly if not provided proportions = process_sample_proportions(proportions, sample_names, - sample_paths, outdir, csv) # read counts defined pased on proportions @@ -172,10 +192,12 @@ def simulate_proportions( df_primers_template = preprocess_primers(primers, reference) task_args = [ - (name, path, cnt, df_primers_template, maxmismatch, outdir, - simulator, wgsim_insert_size, wgsim_read_length, wgsim_error_rate, + (name, genome_map[name], cnt, + df_primers_template, maxmismatch, outdir, + simulator, wgsim_insert_size, + wgsim_read_length, wgsim_error_rate, extra_simulator_flags) - for name, path, cnt in zip(sample_names, sample_paths, read_cnts) + for name, cnt in zip(sample_names, read_cnts) ] worker_func = process_amplicon_worker diff --git a/bygul/utils.py b/bygul/utils.py index 95fd921..52d6634 100644 --- a/bygul/utils.py +++ b/bygul/utils.py @@ -748,12 +748,11 @@ def write_fasta_group(group, amplicon_number, output_dir): def process_amplicon_worker(args): """Worker for the 'amplicon' simulation mode.""" - (name, path, cnt, df_primers_template, maxmismatch, outdir, + (name, genome_seqs, cnt, df_primers_template, maxmismatch, outdir, simulator, wgsim_insert_size, wgsim_read_length, wgsim_error_rate, extra_simulator_flags) = args sample_amplicons_list = [] - genome_seqs = list(SeqIO.parse(path, "fasta")) for genome_seq in genome_seqs: # Print information about the quality of the provided file assess_genome_quality_from_fasta(genome_seq) @@ -786,7 +785,8 @@ def process_amplicon_worker(args): sample_amplicons_list.append(all_amplicons) if not sample_amplicons_list: - return ("warning", f"Warning: No sequences found in {path}") + return ("warning", + f"Warning: No sequences found in {name}") full_sample_df = pd.concat(sample_amplicons_list, ignore_index=True) amp_out_dir = os.path.join(outdir, name, "amplicons") From a47f136a610ff54215dfe812f59ac31132587fb6 Mon Sep 17 00:00:00 2001 From: Maryam Date: Thu, 9 Jul 2026 14:14:16 -0700 Subject: [PATCH 33/52] update argument validation function --- bygul/utils.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/bygul/utils.py b/bygul/utils.py index 52d6634..8180aef 100644 --- a/bygul/utils.py +++ b/bygul/utils.py @@ -16,7 +16,6 @@ def process_sample_proportions( proportions, sample_names, - sample_paths, outdir, csv, ): @@ -61,7 +60,6 @@ def process_sample_proportions( if not ( len(sample_names) == len(proportions) - == len(sample_paths) ): raise Exception( "Number of samples, proportions, and sample paths should match!" @@ -110,7 +108,8 @@ def check_dir(outdir, redo, sample_names): def validate_simulation_args(simulation_mode, primers, reference, - proportions, proportions_csv, genomes): + proportions, proportions_csv, genomes, + multifasta): if simulation_mode == "amplicon" and primers == "NA": print("Primer file is required for simulation mode amplicon") sys.exit(1) @@ -131,6 +130,9 @@ def validate_simulation_args(simulation_mode, primers, reference, if genomes != "NA": print("Cannot use --genomes with --csv") sys.exit(1) + if multifasta != "NA" and proportions_csv == "NA": + print("--multifasta must be used together with --csv.") + sys.exit(1) def assess_genome_quality_from_fasta(sequence): From 2e2f8960f2dbb3a43972128cd588c981f61c262c Mon Sep 17 00:00:00 2001 From: Maryam Date: Thu, 9 Jul 2026 14:29:00 -0700 Subject: [PATCH 34/52] updating test files --- bygul/tests/data/sample_proportions.csv | 6 +- bygul/tests/data/test_proportions.py | 149 ++++++++++++++++++------ 2 files changed, 114 insertions(+), 41 deletions(-) diff --git a/bygul/tests/data/sample_proportions.csv b/bygul/tests/data/sample_proportions.csv index e73f714..78790b4 100644 --- a/bygul/tests/data/sample_proportions.csv +++ b/bygul/tests/data/sample_proportions.csv @@ -1,3 +1,3 @@ -sample_path,proportion -bygul/tests/data/ATM-2FFMD73N3.fasta,0.54 -bygul/tests/data/CA-SEARCH-43254.fasta,0.46 \ No newline at end of file +sample_name,proportion +ATM-2FFMD73N3,0.54 +CA-SEARCH-43254,0.46 \ No newline at end of file diff --git a/bygul/tests/data/test_proportions.py b/bygul/tests/data/test_proportions.py index eb4e307..1a8238c 100644 --- a/bygul/tests/data/test_proportions.py +++ b/bygul/tests/data/test_proportions.py @@ -5,51 +5,124 @@ class ProportionTests(unittest.TestCase): - def test_prop_with_freyja(self): - # 1. Run the simulation - os.system( - "bygul simulate-proportions " - "--genomes bygul/tests/data/BCN-SEARCH-105346.fasta," - "bygul/tests/data/CA-SEARCH-43254.fasta " - "--primers bygul/tests/data/ARTIC_V4-1.bed " - "--reference bygul/tests/data/reference.fasta " - "--proportions 0.8,0.2 --redo") - self.assertTrue(os.path.exists("results/reads_1.fastq")) - - # 2. Run Freyja Pipeline (Align -> Variants -> Demix) - # Note: Replace 'minimap2' with your preferred aligner + def run_freyja(self): ref = "bygul/tests/data/reference.fasta" - os.system(f"minimap2 -ax sr {ref} " - "results/reads_1.fastq results/reads_2.fastq | " - "samtools sort -o results/merged.bam") - os.system("samtools index results/merged.bam") - # Freyja commands - os.system("freyja variants results/merged.bam " - "--variants results/variants.tsv " - f"--depths results/depths.tsv --ref {ref}") - os.system("freyja update --outdir .") - os.system("freyja demix results/variants.tsv " - "results/depths.tsv --output " - "results/demix.tsv --depthcutoff 20 " - "--lineageyml lineages.yml") - - # 3. Validate Proportions + + self.assertEqual( + os.system( + f"minimap2 -ax sr {ref} " + "results/reads_1.fastq results/reads_2.fastq | " + "samtools sort -o results/merged.bam" + ), + 0, + ) + + self.assertEqual( + os.system("samtools index results/merged.bam"), + 0, + ) + + self.assertEqual( + os.system( + "freyja variants results/merged.bam " + "--variants results/variants.tsv " + f"--depths results/depths.tsv --ref {ref}" + ), + 0, + ) + + self.assertEqual( + os.system("freyja update --outdir ."), + 0, + ) + + self.assertEqual( + os.system( + "freyja demix results/variants.tsv " + "results/depths.tsv " + "--output results/demix.tsv " + "--depthcutoff 20 " + "--lineageyml lineages.yml" + ), + 0, + ) + self.assertTrue(os.path.exists("results/demix.tsv")) - # Read the Freyja output - df = pd.read_csv("results/demix.tsv", sep='\t', index_col=0) + df = pd.read_csv( + "results/demix.tsv", + sep="\t", + index_col=0, + ) + + abundances = [ + float(x) + for x in df.loc["abundances"].values[0].split() + ] + + return sorted(abundances, reverse=True) + + def test_prop_with_freyja_manual_proportions(self): + # Test explicit --proportions workflow + self.assertEqual( + os.system( + "bygul simulate-proportions " + "--genomes bygul/tests/data/BCN-SEARCH-105346.fasta," + "bygul/tests/data/CA-SEARCH-43254.fasta " + "--primers bygul/tests/data/ARTIC_V4-1.bed " + "--reference bygul/tests/data/reference.fasta " + "--proportions 0.8,0.2 " + "--redo" + ), + 0, + ) + + self.assertTrue(os.path.exists("results/reads_1.fastq")) + + abundances = self.run_freyja() + + self.assertAlmostEqual(abundances[0], 0.8, delta=0.1) + self.assertAlmostEqual(abundances[1], 0.2, delta=0.1) + + def test_prop_with_freyja_csv_mason(self): + # Test CSV + multifasta workflow + self.assertEqual( + os.system( + "bygul simulate-proportions " + "--csv bygul/tests/data/sample_proportions.csv " + "--multifasta bygul/tests/data/sample_genomes.fasta " + "--redo " + "--simulator mason " + "--illumina-read-length 200" + ), + 0, + ) + + self.assertTrue(os.path.exists("results/reads_1.fastq")) + + abundances = self.run_freyja() - # Access the string of numbers in the 'abundances' row - abundances_str = df.loc['abundances'].values[0] + expected = pd.read_csv( + "bygul/tests/data/sample_proportions.csv" + ) - abundances = [float(x) for x in abundances_str.split()] + expected_props = sorted( + expected["proportion"].tolist(), + reverse=True, + ) - top_abundance = abundances[0] - second_abundance = abundances[1] + self.assertEqual( + len(abundances), + len(expected_props), + ) - self.assertAlmostEqual(top_abundance, 0.8, delta=0.1) - self.assertAlmostEqual(second_abundance, 0.2, delta=0.1) + for observed, expected in zip(abundances, expected_props): + self.assertAlmostEqual( + observed, + expected, + delta=0.1, + ) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() From 6535f917205b2b123a8ed2be8a2ebeb672c259a1 Mon Sep 17 00:00:00 2001 From: Maryam Date: Fri, 10 Jul 2026 13:39:45 -0700 Subject: [PATCH 35/52] update contig names --- bygul/tests/data/ATM-2FFMD73N3.fasta | 4 +++- bygul/tests/data/CA-SEARCH-43254.fasta | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/bygul/tests/data/ATM-2FFMD73N3.fasta b/bygul/tests/data/ATM-2FFMD73N3.fasta index 209119a..2ec25fe 100644 --- a/bygul/tests/data/ATM-2FFMD73N3.fasta +++ b/bygul/tests/data/ATM-2FFMD73N3.fasta @@ -1,4 +1,4 @@ ->hCoV-19/USA/ATM-2FFMD73N3/2021 +>ATM-2FFMD73N3 NNNNNNNNNNNNNNNNNNNNNNNNNAACAAACCAACCAACTTTCGATCTCTTGTAGATCT GTTCTCTAAACGAACTTTAAAATCTGTGTGGCTGTCACTCGGCTGCATGCTTAGTGCACT CACGCAGTATAATTAATAACTAATTACTGTCGTTGACAGGACACGAGTAACTCGTCTATC @@ -498,3 +498,5 @@ AGAGCCACCACATTTTCACCGAGGCCACTCGGAGTACGATCGAGTGTACAGTGAACAATG CTAGGGAGAGCTGCCTATATGGAAGAGCCCTAATGTGTAAAATTAATTTTAGTAGTGCTA TCCCCATGTGATTTTAATAGNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN NNNNNNNNNN +>hCoV-19/USA/ATM-2FFMD73N3/2 +ATCCCCCCCCCGGT \ No newline at end of file diff --git a/bygul/tests/data/CA-SEARCH-43254.fasta b/bygul/tests/data/CA-SEARCH-43254.fasta index c78a4a9..d52fc45 100644 --- a/bygul/tests/data/CA-SEARCH-43254.fasta +++ b/bygul/tests/data/CA-SEARCH-43254.fasta @@ -1,2 +1,2 @@ ->hCoV-19/USA/CA-SEARCH-43254/2021 +>CA-SEARCH-43254 nnnaaaggtttatacccttcccaggtaacaaaccaaccaactttcgatctcttgtagatctgttctctaaacgaactttaaaatctgtgtggctgtcactcggctgcatgcttagtgcactcacgcagtataattaataactaattactgtcgttgacaggacacgagtaactcgtctatcttctgcaggctgcttacggtttcgtccgttttgcagccgatcatcagcacatctaggttttgtccgggtgtgaccgaaaggtaagatggagagccttgtccctggtttcaacgagaaaacacacgtccaactcagtttgcctgttttacaggttcgcgacgtgctcgtacgtggctttggagactccgtggaggaggtcttatcagaggcacgtcaacatcttaaagatggcacttgtggcttagtagaagttgaaaaaggcgttttgcctcaacttgaacagccctatgtgttcatcaaacgttcggatgctcgaactgcacctcatggtcatgttatggttgagctggtagcagaactcgaaggcattcagtacggtcgtagtggtgagacacttggtgtccttgtccctcatgtgggcgaaataccagtggcttaccgcaaggttcttcttcgtaagaacggtaataaaggagctggtggccatagttacggcgccgatctaaagtcatttgacttaggcgacgagcttggcactgatccttatgaagattttcaagaaaactggaacactaaacatagcagtggtgttacccgtgaactcatgcgtgagcttaacggaggggcatacactcgctatgtcgataacaacttctgtggccctgatggctaccctcttgagtgcattaaagaccttctagcacgtgctggtaaagcttcatgcactttgtccgaacaactggactttattgacactaagaggggtgtatactgctgccgtgaacatgagcatgaaattgcttggtacacggaacgttctgaaaagagctatgaattgcagacaccttttgaaattaaattggcaaagaaatttgacaccttcaatggggaatgtccaaattttgtatttcccttaaattccataatcaagactattcaaccaagggttgaaaagaaaaagcttgatggctttatgggtagaattcgatctgtctatccagttgcgtcaccaaatgaatgcaaccaaatgtgcctttcaactctcatgaagtgtgatcattgtggtgaaacttcatggcagacgggcgattttgttaaagccacttgcgaattttgtggcactgagaatttgactaaagaaggtgccactacttgtggttacttaccccaaaatgctgttgttaaaatttattgtccagcatgtcacaattcagaagtaggacctgagcatagtcttgccgaataccataatgaatctggcttgaaaaccattcttcgtaagggtggtcgcactattgcctttggaggctgtgtgttctcttatgttggttgccataacaagtgtgcctattgggttccacgtgctagcgctaacataggttgtaaccatacaggtgttgttggagaaggttccgaaggtcttaatgacaaccttcttgaaatactccaaaaagagaaagtcaacatcaatattgttggtgactttaaacttaatgaagagatcgccattattttggcatctttttctgcttccacaagtgcttttgtggaaactgtgaaaggtttggattataaagcattcaaacaaattgttgaatcctgtggtaattttaaagttacaaaaggaaaagctaaaaaaggtgcctggaatattggtgaacagaaatcaatactgagtcctctttatgcatttgcatcagaggctgctcgtgttgtacgatcaattttctcccgcactcttgaaactgctcaaaattctgtgcgtgttttacagaaggccgctataacaatactagatggaatttcacagtattcactgagactcattgatgctatgatgttcacatctgatttggctactaacaatctagttgtaatggcctacattacaggtggtgttgttcagttgacttcgcagtggctaactaacatctttggcactgtttatgaaaaactcaaacccgtccttgattggcttgaagagaagtttaaggaaggtgtagagtttcttagagacggttgggaaattgttaaatttatctcaacctgtgcttgtgaaattgtcggtggacaaattgtcacctgtgcaaaggaaattaaggagagtgttcagacattctttaagcttgtaaataaatttttggctttgtgtgctgactctatcattattggtggagctaaacttaaagccttgaatttaggtgaaacatttgtcacgcactcaaagggattgtacagaaagtgtgttaaatccagagaagaaactggcctactcatgcctctaaaagccccaaaagaaattatcttcttagagggagaaacacttcccacagaagtgttaacagaggaagttgtcttgaaaactggtgatttacaaccattagaacaacctactagtgaagctgttgaagctccattggttggtacaccagtttgtattaacgggcttatgttgctcgaaatcaaagacacagaaaagtactgtgcccttgcacctaatatgatggtaacaaacaataccttcacactcaaaggcggtgcaccaacaaaggttacttttggtgatgacactgtgatagaagtgcaaggttacaagagtgtgaatatcacttttgaacttgatgaaaggattgataaagtacttaatgagaagtgctctgcctatacagttgaactcggtacagaagtaaatgagttcgcctgtgttgtggcagatgctgtcataaaaacgttgcaaccagtatctgaattacttacaccactgggcattgatttagatgagtggagtatggctacatactacttatttgatgagtctggtgagtttaaattggcttcacatatgtattgttctttttaccctccagatgaggatgaagaagaaggtgattgtgaagaagaagagtttgagccatcaactcaatatgagtatggtactgaagatgattaccaaggtaaacctttggaatttggtgccacttctgctgctcttcaacctgaagaagagcaagaagaagattggttagatgatgatagtcaacaaactgttggtcaacaagacggcagtgaggacaatcagacaactactattcaaacaattgttgaggttcaacctcaattagagatggaacttacaccagttgttcagactattgaagtgaatagttttagtggttatttaaaacttactgacaatgtatacattaaaaatgcagacattgtggaagaagctaaaaaggtaaaaccaacagtggttgttaatgcagccaatgtttaccttaaacatggaggaggtgttgcaggagccttaaataaggctactaacaatgccatgcaagttgaatctgatgattacatagctactaatggaccacttaaagtgggtggtagttgtgttttaagcggacacaatcttgctaaacactgtcttcatgttgtcggcccaaatgttaacaaaggtgaagacattcaacttcttaagagtgcttatgaaaattttaatcagcacgaagttctacttgcaccattattatcagctggtatttttggtgctgaccctatacattctttaagagtttgtgtagatactgttcgcacaaatgtctacttagctgtctttgataaaaatctctatgacaaacttgtttcaagctttttggaaatgaagagtgaaaagcaagttgaacaaaagatcgctgagactcctaaagaggaagttaagccatttataactgaaagtaaaccttcagttgaacagagaaaacaagatgataagaaaatcaaagcttgtgttgaagaagttacaacaactctggaagaaactaagttcctcacagaaaacttgttactttatattgacattaatggcaatcttcatccagattctgccactcttgttagtgacattgacatcactttcttaaagaaagatgctccatatatagtgggtgatgttgttcaagagggtgttttaactgctgtggttatacctactaaaaaggctggtggcactactgaaatgctagcgaaagctttgagaaaagtgccaacagacaattatataaccacttacccgggtcagggtttaaatggttacactgtagaggaggcaaagacagtgcttaaaaagtgtaaaagtgccttttacattctaccatctattatctctaatgagaagcaagaaattcttggaactgtttcttggaatttgcgagaaatgcttgcacatgcagaagaaacacgcaaattaatgcctgtctgtgtggaaactaaagccatagtttcaactatacagcgtaaatataagggtattaaaatacaagagggtgtggttgattatggtgctagattttacttttacaccagtaaaacaactgtagcgtcacttatcaacacacttaacgatctaaatgaaactcttgttacaatgccacttggctatgtaacacatggcttaaatttggaagaagctgctcggtatatgagatctctcaaagtgccagctacagtttctgtttcttcacctgatgctgttacagcgtataatggttatcttacttcttcttctaaaacacctgaagaacattttattgaaaccatctcacttgctggttcctataaagattggtcctattctggacaatctacacaactaggtatagaatttcttaagagaggtgataaaagtgtatattacactagtaatcctaccacattccacctagatggtgaagttatcacctttgacaatcttaagacacttctttctttgagagaagtgaggactattaaggtgtttacaacagtagacaacattaacctccacacgcaagttgtggacatgtcaatgacatatggacaacagtttggtccaacttatttggatggagctgatgttactaaaataaaacctcataattcacatgaaggtaaaacattttatgttttacctaatgatgacactctacgtgttgaggcttttgagtactaccacacaactgatcttagttttctgggtaggtacatgtcagcattaaatcacactaaaaagtggaaatacccacaagttaatggtttaacttctattaaatgggcagataacaactgttatcttgccactgcattgttaacactccaacaaatagagttgaagtttaatccacctgctctacaagatgcttattacagagcaagggctggtgaagctgctaacttttgtgcacttatcttagcctactgtaataagacagtaggtgagttaggtgatgttagagaaacaatgagttacttgtttcaacatgccaatttagattcttgcaaaagagtcttgaacgtggtgtgtaaaacttgtggacaacagcagacaacccttaagggtgtagaagctgttatgtacatgggcacgctttcttatgaacaatttaagaaaggtgttcagataccttgtacgtgtggtaaacaagctacaaaatatctagtacaacaggagtcaccttttgttatgatgtcagcaccacctgctcagtatgaacttaagcatggtacatttacttgtgctagtgagtacactggtaattaccagtgtggtcactataaacatataacttctaaagaaactttgtattgcatagacggtgctttacttacaaagtcctcagaatacaaaggtcctattacggatgttttctacaaagaaaacagttacacaacaaccataaaaccagttacttataaattggatggtgttgtttgtacagaaattgaccctaagttggacaattattataagaaagacaattcttatttcacagagcaaccaattgatcttgtaccaaaccaaccatatccaaacgcaagcttcgataattttaagtttgtatgtgataatatcaaatttgctgatgatttaaaccagttaactggttataagaaacctgcttcaagagagcttaaagttacatttttccctgacttaaatggtgatgtggtggctattgattataaacactacacaccctcttttaagaaaggagctaaattgttacataaacctattgtttggcatgttaacaatgcaactaataaagccacgtataaaccaaatacctggtgtatacgttgtctttggagcacaaaaccagttgaaacatcaaattcgtttgatgtactgaagtcagaggacgcgcagggaatggataatcttgcctgcgaagatctaaaaccagtctctgaagaagtagtggaaaatcctaccatacagaaagacgttcttgagtgtaatgtgaaaactaccgaagttgtaggagacattatacttaaaccagcaaataatagtttaaaaattacagaagaggttggccacacagatctaatggctgcttatgtagacaattctagtcttactattaagaaacctaatgaattatctagagtattaggtttgaaaacccttgctactcatggtttagctgctgttaatagtgtcccttgggatactatagctaattatgctaagccttttcttaacaaagttgttagtacaactactaacatagttacacggtgtttaaaccgtgtttgtactaattatatgccttatttctttactttattgctgcaattgtgtacttttactagaagtacaaattctagaattaaagcatctatgccgactactatagcaaagaatactgttaagagtgtcggtaaattttgtctagaggcttcatttaattatttgaagtcacctaatttttctaaactgataaatattataatttggtttttactattaagtgtttgcctaggttctttaatctactcaaccgctgctttaggtgttttaatgtctaatttaggcatgccttcttactgtactggttacagagaaggctatttgaactctactaatgtcactattgcaacctactgtactggttctataccttgtagtgtttgtcttagtggtttagattctttagacacctatccttctttagaaactatacaaattaccatttcatcttttaaatgggatttaactgcttttggcttagttgcagagtggtttttggcatatattcttttcactaggtttttctatgtacttggattggctgcaatcatgcaattgtttttcagctattttgcagtacattttattagtaattcttggcttatgtggttaataattaatcttgtacaaatggccccgatttcagctatggttagaatgtacatcttctttgcatcattttattatgtatggaaaagttatgtgcatgttgtagacggttgtaattcatcaacttgtatgatgtgttacaaacgtaatagagcaacaagagtcgaatgtacaactattgttaatggtgttagaaggtccttttatgtctatgctaatggaggtaaaggcttttgcaaactacacaattggaattgtgttaattgtgatacattctgtgctggtagtacatttattagtgatgaagttgcgagagacttgtcactacagtttaaaagaccaataaatcctactgaccagtcttcttacatcgttgatagtgttacagtgaagaatggttccatccatctttactttgataaagctggtcaaaagacttatgaaagacattctctctctcattttgttaacttagacaacctgagagctaataacactaaaggttcattgcctattaatgttatagtttttgatggtaaatcaaaatgtgaagaatcatctgcaaaatcagcgtctgtttactacagtcagcttatgtgtcaacctatactgttactagatcaggcattagtgtctgatgttggtgatagtgcggaagttgcagttaaaatgtttgatgcttacgttaatacgttttcatcaacttttaacgtaccaatggaaaaactcaaaacactagttgcaactgcagaagctgaacttgcaaagaatgtgtccttagacaatgtcttatctacttttatttcagcagctcggcaagggtttgttgattcagatgtagaaactaaagatgttgttgaatgtcttaaattgtcacatcaatctgacatagaagttactggcgatagttgtaataactatatgctcacctataacaaagttgaaaacatgacaccccgtgaccttggtgcttgtattgactgtagtgcgcgtcatattaatgcgcaggtagcaaaaagtcacaacattgctttgatatggaacgttaaagatttcatgtcattgtctgaacaactacgaaaacaaatacgtagtgctgctaaaaagaataacttaccttttaagttgacatgtgcaactactagacaagttgttaatgttgtaacaacaaagatagcacttaagggtggtaaaattgttaataattggttgaagcagttaattaaagttacacttgtgttcctttttgttgctgctattttctatttaataacacctgttcatgtcatgtctaaacatactgacttttcaagtgaaatcataggatacaaggctattgatggtggtgtcactcgtgacatagcatctacagatacttgttttgctaacaaacatgctgattttgacacatggtttagccagcgtggtggtagttatactaatgacaaagcttgcccattgattgctgcagtcataacaagagaagtgggttttgtcgtgcctggtttgcctggcacgatattacgcacaactaatggtgactttttgcatttcttacctagagtttttagtgcagttggtaacatctgttacacaccatcaaaacttatagagtacactgactttgcaacatcagcttgtgttttggctgctgaatgtacaatttttaaagatgcttctggtaagccagtaccatattgttatgataccaatgtactagaaggttctgttgcttatgaaagtttacgccctgacacacgttatgtgctcatggatggctctattattcaatttcctaacacctaccttgaaggttctgttagagtggtaacaacttttgattctgagtactgtaggcacggcacttgtgaaagatcagaagctggtgtttgtgtatctactagtggtagatgggtacttaacaatgattattacagatctttaccaggagttttctgtggtgtagatgctgtaaatttacttactaatatgtttacaccactaattcaacctattggtgctttggacatatcagcatctatagtagctggtggtattgtagctatcgtagtaacatgccttgcctactattttatgaggtttagaagagcttttggtgaatacagtcatgtagttgcctttaatactttactattccttatgtcattcactgtactctgtttaacaccagtttactcattcttacctggtgtttattctgttatttacttgtacttgacattttatcttactaatgatgtttcttttttagcacatattcagtggatggttatgttcacacctttagtacctttctggataacaattgcttatatcatttgtatttccacaaagcatttctattggttctttagtaattacctaaagagacgtgtagtctttaatggtgtttcctttagtacttttgaagaagctgcgctgtgcacctttttgttaaataaagaaatgtatctaaagttgcgtagtgatgtgctattacctcttacgcaatataatagatacttagttctttataataagtacaagtattttagtggagcaatggatacaactagctacagagaagctgcttgttgtcatctcgcaaaggctctcaatgacttcagtaactcaggttctgatgttctttaccaaccaccacaaacctctatcacctcagctgttttgcagagtggttttagaaaaatggcattcccatctggtaaagttgagggttgtatggtacaagtaacttgtggtacaactacacttaacggtctttggcttgatgacgtagtttactgtccaagacatgtgatctgcacctctgaagacatgcttaaccctaattatgaagatttactcattcgtaagtctaatcataatttcttggtacaggctggtaatgttcaactcagggttattggacattctatgcaaaattgtgtacttaagcttaaggttgatacagccaatcctaagacacctaagtataagtttgttcgcattcaaccaggacagactttttcagtgttagcttgttacaatggttcaccatctggtgtttaccaatgtgctatgaggcccaatttcactattaagggttcattccttaatggttcatgtggtagtgttggttttaacatagattatgactgtgtctctttttgttacatgcaccatatggaattaccaactggagttcatgctggcacagacttagaaggtaacttttatggaccttttgttgacaggcaaacagcacaagcagctggtacggacacaactattacagttaatgttttagcttggttgtacgctgctgttataaatggagacaggtggtttctcaatcgatttaccacaactcttaatgactttaaccttgtggctatgaagtacaattatgaacctctaacacaagaccatgttgacatactaggacctctttctgctcaaactggaattgccgttttagatatgtgtgcttcattaaaagaattactgcaaaatggtatgaatggacgtaccatattgggtagtgctttattagaagatgaatttacaccttttgatgttgttagacaatgctcaggtgttactttccaaagtgcagtgaaaagaacaatcaagggtacacaccactggttgttactcacaattttgacttcacttttagttttagtccagagtactcaatggtctttgttcttttttttgtatgaaaatgcctttttaccttttgctatgggtattattgctatgtctgcttttgcaatgatgtttgtcaaacataagcatgcatttctctgtttgtttttgttaccttctcttgccactgtagcttattttaatatggtctatatgcctgctagttgggtgatgcgtattatgacatggttggatatggttgatactagtttgtctggttttaagctaaaagactgtgttatgtatgcatcagctgtagtgttactaatccttatgacagcaagaactgtgtatgatgatggtgctaggagagtgtggacacttatgaatgtcttgacactcgcttataaagtttattatggtaatgctttagatcaagccatttccatgtgggctcttataatctctgttacttctaactactcaggtgtagttacaattgtcatgtttttggccagaggtattgtttttatgtgtgttgagtattgccctattttcttcataactggtaatacacttcagtgtataatgctagtttattgtttcttaggctatttttgtacttgttactttggcctcttttgtttactcaaccgctactttagactgactcttggtgtttatgattacttagtttctacacaggagtttagatatatgaattcacagggactactcccacccaagaatagcatagatgccttcaaactcaacattaaattgttgggtgttggtggcaaaccttgtatcaaagtagccactgtacagtctaaaatgtcagatgtaaagtgcacatcagtagtcttactctcagttttgcaacaactcagagtagaatcatcatctaaattgtgggctcaatgtgtccagttacacaatgacattctcttagctaaagatactactgaagcctttgaaaaaatggtttcactactttctgttttgctttccatgcagggtgctgtagacataaacaagctttgtgaagaaatgctggacaacagggcaaccttacaagctatagcctcagagtttagttcccttccatcatatgcagcttttgctactgctcaagaagcttatgagcaggctgttgctaatggtgattctgaagttgttcttaaaaagttgaagaagtctttgaatgtggctaaatctgaatttgaccgtgatgcagccatgcaacgtaagttggaaaagatggctgatcaagctatgacccaaatgtataaacaggctagatctgaggacaagagggcaaaagttactagtgctatgcagacaatgcttttcactatgcttagaaagttggataatgatgcactcaacaacattatcaacaatgcaagagatggttgtgttcccttgaacataatacctcttacaacagcagccaaactaatggttgtcataccagactataacacatataaaaatacgtgtgatggtacaacatttacttatgcatcagcattgtgggaaatccaacaggttgtagatgcagatagtaaaattgttcaacttagtgaaattagtatggacaattcacctaatttagcatggcctcttattgtaacagctttaagggccaattctgctgtcaaattacagaataatgagcttagtcctgttgcactacgacagatgtcttgtgctgccggtactacacaaactgcttgcactgatgacaatgcgttagcttactacaacacaacaaagggaggtaggtttgtacttgcactgttatccgatttacaggatttgaaatgggctagattccctaagagtgatggaactggtactatctatacagaactggaaccaccttgtaggtttgttacagacacacctaaaggtcctaaagtgaagtatttatactttattaaaggattaaacaacctaaatagaggtatggtacttggtagtttagctgccacagtacgtttacaagctggtaatgcaacagaagtgcctgccaattcaactgtattatctttctgtgcttttgctgtagatgctgctaaagcttacaaagattatctagctagtgggggacaaccaatcactaattgtgttaagatgttgtgtacacacactggtactggtcaggcaataacagttacaccggaagccaatatggatcaagaatcctttggtggtgcatcgtgttgtctgtactgccgttgccacatagatcatccaaatcctaaaggattttgtgacttaaaaggtaagtatgtacaaatacctacaacttgtgctaatgaccctgtgggttttacacttaaaaacacagtctgtaccgtctgcggtatgtggaaaggttatggctgtagttgtgatcaactccgcgaacccatgcttcagtcagctgatgcacaatcgtttttaaacgggtttgcggtgtaagtgcagcccgtcttacaccgtgcggcacaggcactagtactgatgtcgtatacagggcttttgacatctacaatgataaagtagctggttttgctaaattcctaaaaactaattgttgtcgcttccaagaaaaggacgaagatgacaatttaattgattcttactttgtagttaagagacacactttctctaactaccaacatgaagaaacaatttataatttacttaaggattgtccagctgttgctaaacatgacttctttaagtttagaatagacggtgacatggtaccacatatatcacgtcaacgtcttactaaatacacaatggcagacctcgtctatgctttaaggcattttgatgaaggtaattgtgacacattaaaagaaatacttgtcacatacaattgttgtgatgatgattatttcaataaaaaggactggtatgattttgtagaaaacccagatatattacgcgtatacgccaacttaggtgaacgtgtacgccaagctttgttaaaaacagtacaattctgtgatgccatgcgaaatgctggtattgttggtgtactgacattagataatcaagatctcaatggtaactggtatgatttcggtgatttcatacaaaccacgccaggtagtggagttcctgttgtagattcttattattcattgttaatgcctatattaaccttgaccagggctttaactgcagagtcacatgttgacactgacttaacaaagccttacattaagtgggatttgttaaaatatgacttcacggaagagaggttaaaactctttgaccgttattttaaatattgggatcagacataccacccaaattgtgttaactgtttggatgacagatgcattctgcattgtgcaaactttaatgttttattctctacagtgttcccacttacaagttttggaccactagtgagaaaaatatttgttgatggtgttccatttgtagtttcaactggataccacttcagagagctaggtgttgtacataatcaggatgtaaacttacatagctctagacttagttttaaggaattacttgtgtatgctgctgaccctgctatgcacgctgcttctggtaatctattactagataaacgcactacgtgcttttcagtagctgcacttactaacaatgttgcttttcaaactgtcaaacccggtaattttaacaaagacttctatgactttgctgtgtctaagggtttctttaaggaaggaagttctgttgaattaaaacacttcttctttgctcaggatggtaatgctgctatcagcgattatgactactatcgttataatctaccaacaatgtgtgatatcagacaactactatttgtagttgaagttgttgataagtactttgattgttacgatggtggctgtattaatgctaaccaagtcatcgtcaacaacctagacaaatcagctggttttccatttaataaatggggtaaggctagactttattatgattcaatgagttatgaggatcaagatgcacttttcgcatatacaaaacgtaatgtcatccctactataactcaaatgaatcttaagtatgccattagtgcaaagaatagagctcgcaccgtagctggtgtctctatctgtagtactatgaccaatagacagtttcatcaaaaattattgaaatcaatagccgccactagaggagctactgtagtaattggaacaagcaaattctatggtggttggcacaacatgttaaaaactgtttatagtgatgtagaaaaccctcaccttatgggttgggattatcctaaatgtgatagagccatgcctaacatgcttagaattatggcctcacttgttcttgctcgcaaacatacaacgtgttgtagcttgtcacaccgtttctatagattagctaatgagtgtgctcaagtattgagtgaaatggtcatgtgtggcagttcactatatgttaaaccaggtggaacctcatcaggagatgccacaactgcttatgctaatagtgtttttaacatttgtcaagctgtcacggccaatgttaatgcacttttatctactgatggtaacaaaattgccgataagtatgtccgcaatttacaacacagactttatgagtgtctctatagaaatagagatgttgacacagactttgtgaatgagttttacgcatatttgcgtaaacatttctcaatgatgatactctctgacgatgctgttgtgtgtttcaatagcacttatgcatctcaaggtctagtggctagcataaagaactttaagtcagttctttattatcaaaacaatgtttttatgtctgaagcaaaatgttggactgagactgaccttactaaaggacctcatgaattttgctctcaacatacaatgctagttaaacagggtgatgattatgtgtaccttccttacccagatccatcaagaatcctaggggccggctgttttgtagatgatatcgtaaaaacagatggtacacttatgattgaacggttcgtgtctttagctatagatgcttacccacttactaaacatcctaatcaggagtatgctgatgtctttcatttgtacttacaatacataagaaagctacatgatgagttaacaggacacatgttagacatgtattctgttatgcttactaatgataacacttcaaggtattgggaacctgagttttatgaggctatgtacacaccgcatacagtcttacaggctgttggggcttgtgttctttgcaattcacagacttcattaagatgtggtgcttgcatacgtagaccattcttatgttgtaaatgctgttacgaccatgtcatatcaacatcacataaattagtcttgtctgttaatccgtatgtttgcaatgctccaggttgtgatgtcacagatgtgactcaactttacttaggaggtatgagctattattgtaaatcacataaactacccattagttttccattgtgtgctaatggacaagtttttggtttatataaaaatacatgtgttggtagcgataatgttactgactttaatgcaattgcaacatgtgactggacaaatgctggtgattacattttagctaacacctgtactgaaagactcaagctttttgcagcagaaacgctcaaagctactgaggagacatttaaactgtcttatggtattgctactgtacgtgaagtgctgtctgacagagaattacatctttcatgggaagttggtaaacctagaccaccacttaaccgaaattatgtctttactggttatcgtgtaactaaaaacagtaaagtacaaataggagagtacacctttgaaaaaggtgactatggtgatgctgttgtttaccgaggtacaacaacttacaaattaaatgttggtgattattttgtgctgacatcacatacagtaatgccattaagtgcacctacactagtgccacaagagcactatgttagaattactggcttatacccaacactcaatatctcagatgagttttctagcaatgttgcaaattatcaaaaggttggtatgcaaaagtattctacactccagggaccacctggtactggtaagagtcattttgctattggcctagctctctactacccttctgctcgcatagtgtatacagcttgctctcatgccgctgttgatgcactatgtgagaaggcattaaaatatttgcctatagataaatgtagtagaattatacctgcacgtgctcgtgtagagtgttttgataaattcaaagtgaattcaacattagaacagtatgtcttttgtactgtaaatgcattgcctgagacgacagcagatatagttgtctttgatgaaatttcaatggccacaaattatgatttgagtgttgtcaatgccagattacgtgctaagcactatgtgtacattggcgaccctgctcaattacctgcaccacgcacattgctaactaagggcacactagaaccagaatatttcaattcagtgtgtagacttatgaaaactataggtccagacatgttcctcggaacttgtcggcgttgtcctgctgaaattgttgacactgtgagtgctttggtttatgataataagcttaaagcacataaagacaaatcagctcaatgctttaaaatgttttataagggtgttatcacgcatgatgtttcatctgcaattaacaggccacaaataggcgtggtaagagaattccttacacgtaaccctgcttggagaaaagctgtctttatttcaccttataattcacagaatgctgtagcctcaaagattttgggactaccaactcaaactgttgattcatcacagggctcagaatatgactatgtcatattcactcaaaccactgaaacagctcactcttgtaatgtaaacagatttaatgttgctattaccagagcaaaagtaggcatactttgcataatgtctgatagagacctttatgacaagttgcaatttacaagtcttgaaattccacgtaggaatgtggcaactttacaagctgaaaatgtaacaggactctttaaagattgtagtaaggtaatcactgggttacatcctacacaggcacctacacacctcagtgttgacactaaattcaaaactgaaggtttatgtgttgacatacctggcatacctaaggacatgacctatagaagactcatctctatgatgggttttaaaatgaattatcaagttaatggttaccctaacatgtttatcacccgcgaagaagctataagacatgtacgtgcatggattggcttcgatgttgaggggtgtcatgctactagagaagctgttggtaccaatttacctttacagctaggtttttctacaggtgttaacctagttgctgtacctacaggttatgttgatacacctaataatacagatttttccagagttagtgctaaaccaccgcctggagatcaatttaaacacctcataccacttatgtacaaaggacttccttggaatgtagtgcgtataaagattgtacaaatgttaagtgacacacttaaaaatctctctgacagagtcgtatttgtcttatgggcacatggctttgagttgacatctatgaagtattttgtgaaaataggacctgagcgcacctgttgtctatgtgatagacgtgccacatgcttttccactgcttcagacacttatgcctgttggcatcattctattggatttgattacgtctataatccgtttatgattgatgttcaacaatggggttttacaggtaacctacaaagcaaccatgatctgtattgtcaagtccatggtaatgcacatgtagctagttgtgatgcaatcatgactaggtgtctagctgtccacgagtgctttgttaagcgtgttgactggactattgaatatcctataattggtgatgaactgaagattaatgcggcttgtagaaaggttcaacacatggttgttaaagctgcattattagcagacaaattcccagttcttcacgacattggtaaccctaaagctattaagtgtgtacctcaagctgatgtagaatggaagttctatgatgcacagccttgtagtgacaaagcttataaaatagaagaattattctattcttatgccacacattctgacaaattcacagatggtgtatgcctattttggaattgcaatgtcgatagatatcctgctaattccattgtttgtagatttgacactagagtgctatctaaccttaacttgcctggttgtgatggtggcagtttgtatgtaaataaacatgcattccacacaccagcttttgataaaagtgcttttgttaatttaaaacaattaccatttttctattactctgacagtccatgtgagtctcatggaaaacaagtagtgtcagatatagattatgtaccactaaagtctgctacgtgtataacacgttgcaatttaggtggtgctgtctgtagacatcatgctaatgagtacagattgtatctcgatgcttataacatgatgatctcagctggctttagcttgtgggtttacaaacaatttgatacttataacctctggaacacttttacaagacttcagagtttagaaaatgtggcttttaatgttgtaaataagggacactttgatggacaacagggtgaagtaccagtttctatcattaataacactgtttacacaaaagttgatggtgttgatgtagaattgtttgaaaataaaacaacattacctgttaatgtagcatttgagctttgggctaagcgcaacattaaaccagtaccagaggtgaaaatactcaataatttgggtgtggacattgctgctaatactgtgatctgggactacaaaagagatgctccagcacatatatctactattggtgtttgttctatgactgacatagccaagaaaccaactgaaacgatttgtgcaccactcactgtcttttttgatggtagagttgatggtcaagtagacttatttagaaatgcccgtaatggtgttcttattacagaaggtagtgttaaaggtttacaaccatctgtaggtcccaaacaagctagtcttaatggagtcacattaattggagaagccgtaaaaacacagttcaattattataagaaagttgatggtgttgtccaacaattacctgaaacttactttactcagagtagaaatttacaagaatttaaacccaggagtcaaatggaaattgatttcttagaattagctatggatgaattcattgaacggtataaattagaaggctatgccttcgaacatatcgtttatggagattttagtcatagtcagttaggtggtttacatctactgattggactagctaaacgttttaaggaatcaccttttgaattagaagattttattcctatggacagtacagttaaaaactatttcataacagatgcgcaaacaggttcatctaagtgtgtgtgttctgttattgatttattacttgatgattttgttgaaataataaaatcccaagatttatctgtagtttctaaggttgtcaaagtgactattgactatacagaaatttcatttatgctttggtgtaaagatggccatgtagaaacattttacccaaaattacaatctagtcaagcgtggcaaccgggtgttgctatgcctaatctttacaaaatgcaaagaatgctattagaaaagtgtgaccttcaaaattatggtgatagtgcaacattacctaaaggcataatgatgaatgtcgcaaaatatactcaactgtgtcaatatttaaacacattaacattagctgtaccctataatatgagagttatacattttggtgctggttctgataaaggagttgcaccaggtacagctgttttaagacagtggttgcctacgggtacgctgcttgtcgattcagatcttaatgactttgtctctgatgcagattcaactttgattggtgattgtgcaactgtacatacagctaataaatgggatctcattattagtgatatgtacgaccctaagactaaaaatgttacaaaagaaaatgactctaaagagggttttttcacttacatttgtgggtttatacaacaaaagctagctcttggaggttccgtggctataaagataacagaacattcttggaatgctgatctttataagctcatgggacacttcgcatggtggacagcctttgttactaatgtgaatgcgtcatcatctgaagcatttttaattggatgtaattatcttggcaaaccacgcgaacaaatagatggttatgtcatgcatgcaaattacatattttggaggaatacaaatccaattcagttgtcttcctattctttatttgacatgagtaaatttccccttaaattaaggggtactgctgttatgtctttaaaagaaggtcaaatcaatgatatgattttatctcttcttagtaaaggtagacttataattagagaaaacaacagagttgttatttctagtgatgttcttgttaacaactaaacgaacaatgtttgtttttcttgttttattgccactagtctctagtcagtgtgttaatcttagaaccagaactcaattaccccctgcatacactaattctttcacacgtggtgtttattaccctgacaaagttttcagatcctcagttttacattcaactcaggacttgttcttacctttcttttccaatgttacttggttccatgctatacatgtctctgggaccaatggtactaagaggtttgataaccctgtcctaccatttaatgatggtgtttattttgcttccactgagaagtctaacataataagaggctggatttttggtactactttagattcgaagacccagtccctacttattgttaataacgctactaatgttgttattaaagtctgtgaatttcaattttgtaatgatccatttttggatgtttattaccacaaaaacaacaaaagttggatggaaagtggagtttattctagtgcgaataattgcacttttgaatatgtctctcagccttttcttatggaccttgaaggaaaacagggtaatttcaaaaatcttagggaatttgtgtttaagaatattgatggttattttaaaatatattctaagcacacgcctattaatttagtgcgtgatctccctcagggtttttcggttttagaaccattggtagatttgccaataggtattaacatcactaggtttcaaactttacttgctttacatagaagttatttgactcctggtgattcttcttcaggttggacagctggtgctgcagcttattatgtgggttatcttcaacctaggacttttctattaaaatataatgaaaatggaaccattacagatgctgtagactgtgcacttgaccctctctcagaaacaaagtgtacgttgaaatccttcactgtagaaaaaggaatctatcaaacttctaactttagagtccaaccaacagaatctattgttagatttcctaatattacaaacttgtgcccttttggtgaagtttttaacgccaccagatttgcatctgtttatgcttggaacaggaagagaatcagcaactgtgttgctgattattctgtcctatataattccgcatcattttccacttttaagtgttatggagtgtctcctactaaattaaatgatctctgctttactaatgtctatgcagattcatttgtaattagaggtgatgaagtcagacaaatcgctccagggcaaactggaaagattgctgattataattataaattaccagatgattttacaggctgcgttatagcttggaattctaacaatcttgattctaaggttggtggtaattataattaccggtatagattgtttaggaagtctaatctcaaaccttttgagagagatatttcaactgaaatctatcaggccggtagcaaaccttgtaatggtgttgaaggttttaattgttactttcctttacaatcatatggtttccaacccactaatggtgttggttaccaaccatacagagtagtagtactttcttttgaacttctacatgcaccagcaactgtttgtggacctaaaaagtctactaatttggttaaaaacaaatgtgtcaatttcaacttcaatggtttaacaggcacaggtgttcttactgagtctaacaaaaagtttctgcctttccaacaatttggcagagacattgctgacactactgatgctgtccgtgatccacagacacttgagattcttgacattacaccatgttcttttggtggtgtcagtgttataacaccaggaacaaatacttctaaccaggttgctgttctttatcagggtgttaactgcacagaagtccctgttgctattcatgcagatcaacttactcctacttggcgtgtttattctacaggttctaatgtttttcaaacacgtgcaggctgtttaataggggctgaacatgtcaacaactcatatgagtgtgacatacccattggtgcaggtatatgcgctagttatcagactcagactaattctcgtcggcgggcacgtagtgtagctagtcaatccatcattgcctacactatgtcacttggtgcagaaaattcagttgcttactctaataactctattgccatacccacaaattttactattagtgttaccacagaaattctaccagtgtctatgaccaagacatcagtagattgtacaatgtacatttgtggtgattcaactgaatgcagcaatcttttgttgcaatatggcagtttttgtacacaattaaaccgtgctttaactggaatagctgttgaacaagacaaaaacacccaagaagtttttgcacaagtcaaacaaatttacaaaacaccaccaattaaagattttggtggttttaatttttcacaaatattaccagatccatcaaaaccaagcaagaggtcatttattgaagatctacttttcaacaaagtgacacttgcagatgctggcttcatcaaacaatatggtgattgccttggtgatattgctgctagagacctcatttgtgcacaaaagtttaacggccttactgttttgccacctttgctcacagatgaaatgattgctcaatacacttctgcactgttagcgggtacaatcacttctggttggacctttggtgcaggtgctgcattacaaataccatttgctatgcaaatggcttataggtttaatggtattggagttacacagaatgttctctatgagaaccaaaaattgattgccaaccaatttaatagtgctattggcaaaattcaagactcactttcttccacagcaagtgcacttggaaaacttcaaaatgtggtcaaccaaaatgcacaagctttaaacacgcttgttaaacaacttagctccaattttggtgcaatttcaagtgttttaaatgatatcctttcacgtcttgacaaagttgaggctgaagtgcaaattgataggttgatcacaggcagacttcaaagtttgcagacatatgtgactcaacaattaattagagctgcagaaatcagagcttctgctaatcttgctgctactaaaatgtcagagtgtgtacttggacaatcaaaaagagttgatttttgtggaaagggctatcatcttatgtccttccctcagtcagcacctcatggtgtagtcttcttgcatgtgacttatgtccctgcacaagaaaagaacttcacaactgctcctgccatttgtcatgatggaaaagcacactttcctcgtgaaggtgtctttgtttcaaatggcacacactggtttgtaacacaaaggaatttttatgaaccacaaatcattactacagacaacacatttgtgtctggtaactgtgatgttgtaataggaattgtcaacaacacagtttatgatcctttgcaacctgaattagactcattcaaggaggagttagataaatattttaagaatcatacatcaccagatgttgatttaggtgacatctctggcattaatgcttcagttgtaaacattcaaaaagaaattgaccgcctcaatgaggttgccaagaatttaaatgaatctctcatcgatctccaagaacttggaaagtatgagcagtatataaaatggccatggtacatttggctaggttttatagctggcttgattgccatagtaatggtgacaattatgctttgctgtatgaccagttgctgtagttgtctcaagggctgttgttcttgtggatcctgctgcaaatttgatgaagacgactctgagccattgctcaaaggagtcaaattacattacacataaacgaacttatggatttgtttatgagaatcttcacaattggaactgtaactttgaagcaaggtgaaatcaaggatgctactcctttagattttgttcgcgctactgcaacgataccgatacaagcctcactccctttcggatggcttattgttggcgttgcacttcttgctgtttttcagagcgcttccaaaatcataaccctcaaaaagagatggcaactagcactctccaagggtgttcactttgtttgcaacttgctgttgttgtttgtaacagtttactcacaccttttgctcgttgctgctggccttgaagccccttttctctatctttatgctttagtctacttcttgcagagtataaactttgtaagaataataatgaggctttggctttgctggaaatgccgttccaaaaacccattactttatgatgccaactattttctttgctggcatactaattgttacgactattgtataccttacaatagtgtaacttcttcaattgtcattacttcaggtgatggcacaacaagtcctatttctgaacatgactaccagattggtggttatactgaaaaatgggaatctggagtaaaagactgtgttgtattacacagttacttcacttcagactattaccagctgtactcaactcaattgagtacagacactggtgttgaacatgttaccttcttcatctacaataaaattgttgatgagcctgaagaacatgtccaaattcacacaatcgacggttcatccggagttgttaatccagtaatggaaccaatttatgatgaaccgacgacgactactagcgtgcctttgtaagcacaagctgatgagtacgaacttatgtactcattcgtttcggaagagacaggtacgttaatagttaatagcgtacttctttttcttgctttcgtggtattcttgctagttacactagccatccttactgcgcttcgattgtgtgcgtactgctgcaatattgttaacgtgagtcttgtaaaaccttctttttacgtttactctcgtgttaaaaatctgaattcttctagagttcctgatcttctggtctaaacgaactaaatattatattagtttttctgtttggaactttaattttagccatggcagattccaacggtactattaccgttgaagagcttaaaaagctccttgaacaatggaacctagtaataggtttcctattccttacatggatttgtcttctacaatttgcctatgccaacaggaataggtttttgtatataattaagttaattttcctctggctgttatggccagtaactttagcttgttttgtgcttgctgctgtttacagaataaattggatcaccggtggaattgctaccgcaatggcttgtcttgtaggcttgatgtggctcagctacttcattgcttctttcagactgtttgcgcgtacgcgttccatgtggtcattcaatccagaaactaacattcttctcaacgtgccactccatggcactattctgaccagaccgcttctagaaagtgaactcgtaatcggagctgtgatccttcgtggacatcttcgtattgctggacaccatctaggacgctgtgacatcaaggacctgcctaaagaaatcactgttgctacatcacgaacgctttcttattacaaattgggagcttcgcagcgtgtagcaggtgactcaggttttgctgcatacagtcgctacaggattggcaactataaattaaacacagaccattccagtagcagtgacaatattgctttgcttgtacagtaagtgacaacagatgtttcatctcgttgactttcaggttactatagcagagatattactaattattatgaggacttttaaagtttccatttggaatcttgattacatcataaacctcataattaaaaatttatctaagtcactaactgagaataattattctcaattagatgaagagcaaccaatggagattgattaaacgaacatgaaaattattcttttcttggcactgataacactcgctacttgtgagctttatcactaccaagagtgtgttagaggtacaacagtacttttaaaagaaccttgctcttctggaacatacgagggcaattcaccatttcatcctctagctgataacaaatttgcactgacttgctttagcactcaatttgcttttgcttgtcctgacggcgtaaaacacgtctatcagttacgtgccagatcagcttcacctaaactgttcatcagacaagaggaagttcaagaactttactctccaatttttcttattgttgcggcaatagtgtttataacactttgcttcacactcaaaagaaagatagaatgattgaactttcattaattgacttctatttgtgctttttagcctttctgctattccttgttttaattatgcttattatcttttggttctcacttgaactgcaagatcataatgaaacttgtcacgcctaaacgaacatgaaatttcttgttttcttaggaatcatcacaactgtagctgcatttcaccaagaatgtagtttacagtcatgtactcaacatcaaccatatgtagttgatgacccgtgtcctattcacttctattctaaatggtatattagagtaggagctagaaaatcagcacctttaattgaattgtgcgtggatgaggctggttctaaatcacccattcagtacatcgatatcggtaattatacagtttcctgtttaccttttacaattaattgccaggaacctaaattgggtagtcttgtagtgcgttgttcgttctatgaagactttttagagtatcatgacgttcgtgttgttttaatctaaacgaacaaactaaatgtctgataatggaccccaaaatcagcgaaatgcaccccgcattacgtttggtggaccctcagattcaactggcagtaaccagaatggagaacgcagtggggcgcgatcaaaacaacgtcggccccaaggtttacccaataatactgcgtcttggttcaccgctctcactcaacatggcaaggaaggccttaaattccctcgaggacaaggcgttccaattaacaccaatagcagtccagatgaccaaattggctactaccgaagagctaccagacgaattcgtggtggtgacggtaaaatgaaagatctcagtccaagatggtatttctactacctaggaactgggccagaagctggacttccctatggtgctaacaaagacggcatcatatgggttgcaactgagggagccttgaatacaccaaaagatcacattggcacccgcaatcctgctaacaatgctgcaatcgtgctacaacttcctcaaggaacaacattgccaaaaggcttctacgcagaagggagcagaggcggcagtcaagcctcttctcgttcctcatcacgtagtcgcaacagttcaagaaattcaactccaggcagcagtatgggaacttctcctgctagaatggctggcaatggcggtgatgctgctcttgctttgctgctgcttgacagattgaaccagcttgagagcaaaatgtctggtaaaggccaacaacaacaaggccaaactgtcactaagaaatctgctgctgaggcttctaagaagcctcggcaaaaacgtactgccactaaagcatacaatgtaacacaagctttcggcagacgtggtccagaacaaacccaaggaaattttggggaccaggaactaatcagacaaggaactgattacaaacattggccgcaaattgcacaatttgcccccagcgcttcagcgttcttcggaatgtcgcgcattggcatggaagtcacaccttcgggaacgtggttgacctacacaggtgccatcaaattggatgacaaagatccaaatttcaaagatcaagtcattttgctgaataagcatattgacgcatacaaaacattcccaccaacagagcctaaaaaggacaaaaagaagaaggcttatgaaactcaagccttaccgcagagacagaagaaacagcaaactgtgactcttcttcctgctgcagatttggatgatttctccaaacaattgcaacaatccatgagcagtgctgactcaactcaggcctaaactcatgcagaccacacaaggcagatgggctatataaacgttttcgcttttccgtttacgatatatagtctactcttgtgcagaatgaattctcgtaactacatagcacaagtagatgtagttaactttaatctcacatagcaatctttaatcagtgtgtaacattagggaggacttgaaagagccaccacattttcaccgaggccactcggagtacgatcgagtgtacagtgaacaatgctagggagagctgcctatatggaagagccctaatgtgtaaaattaattttagtagtgctatccccatgtgatttnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn From b7627132bb10a14132d37b539e4a09695e9090ba Mon Sep 17 00:00:00 2001 From: Maryam Date: Fri, 10 Jul 2026 13:40:01 -0700 Subject: [PATCH 36/52] reference index file --- bygul/tests/data/reference.fasta.fai | 1 + 1 file changed, 1 insertion(+) create mode 100644 bygul/tests/data/reference.fasta.fai diff --git a/bygul/tests/data/reference.fasta.fai b/bygul/tests/data/reference.fasta.fai new file mode 100644 index 0000000..3d18214 --- /dev/null +++ b/bygul/tests/data/reference.fasta.fai @@ -0,0 +1 @@ +NC_045512.2 29903 13 70 71 From 2405dcc7a1b1401167e92ab0f8a7c601b3f8a9b7 Mon Sep 17 00:00:00 2001 From: Maryam Date: Fri, 10 Jul 2026 13:41:06 -0700 Subject: [PATCH 37/52] lint --- bygul/_cli.py | 1 + 1 file changed, 1 insertion(+) diff --git a/bygul/_cli.py b/bygul/_cli.py index d69a7c2..0dc2f8a 100644 --- a/bygul/_cli.py +++ b/bygul/_cli.py @@ -7,6 +7,7 @@ from concurrent.futures import ProcessPoolExecutor, as_completed from collections import defaultdict + @click.group(context_settings={"show_default": True}) @click.version_option("3.2.0") def cli(): From 6d46bcbb211fd9a6cff1aebb7f7e7d626e30c2c0 Mon Sep 17 00:00:00 2001 From: Maryam Date: Fri, 10 Jul 2026 13:41:40 -0700 Subject: [PATCH 38/52] add click argument --outdir for check primer function --- bygul/_cli.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/bygul/_cli.py b/bygul/_cli.py index 0dc2f8a..2db146d 100644 --- a/bygul/_cli.py +++ b/bygul/_cli.py @@ -248,7 +248,16 @@ def simulate_proportions( show_default=True, help="Maximum number of mismatches allowed in primer region", ) -def check_primers(genomes, primers, reference, maxmismatch): +@click.option( + "--outdir", + default=".", + type=click.Path(exists=False), + help="Output directory", + show_default=True, +) +def check_primers(genomes, primers, + reference, maxmismatch, + outdir): from bygul.utils import ( assess_genome_quality_from_fasta, preprocess_primers, From ef59ca7478d8d67705159541c9364b40759e42b0 Mon Sep 17 00:00:00 2001 From: Maryam Date: Fri, 10 Jul 2026 13:42:18 -0700 Subject: [PATCH 39/52] remove unnecessary imports and only import the worker function in check primers --- bygul/_cli.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/bygul/_cli.py b/bygul/_cli.py index 2db146d..028a6ac 100644 --- a/bygul/_cli.py +++ b/bygul/_cli.py @@ -259,10 +259,8 @@ def check_primers(genomes, primers, reference, maxmismatch, outdir): from bygul.utils import ( - assess_genome_quality_from_fasta, preprocess_primers, - find_closest_primer_match, - create_valid_primer_combinations, + process_primer_check_worker, ) genome_seqs = list(SeqIO.parse(genomes, "fasta")) assess_genome_quality_from_fasta(genome_seqs) From bd959c10b24c12d0e7f2305dd5219accea82bf3f Mon Sep 17 00:00:00 2001 From: Maryam Date: Fri, 10 Jul 2026 13:43:35 -0700 Subject: [PATCH 40/52] adding the multiprocessing function --- bygul/_cli.py | 75 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 45 insertions(+), 30 deletions(-) diff --git a/bygul/_cli.py b/bygul/_cli.py index 028a6ac..297ff3a 100644 --- a/bygul/_cli.py +++ b/bygul/_cli.py @@ -262,42 +262,57 @@ def check_primers(genomes, primers, preprocess_primers, process_primer_check_worker, ) - genome_seqs = list(SeqIO.parse(genomes, "fasta")) - assess_genome_quality_from_fasta(genome_seqs) # read the reference sequence reference = next(SeqIO.parse(reference, "fasta")) - primer_df = preprocess_primers(primers, reference) - print("Reading and preprocessing the primer file...") - all_results = [] + genome_map = defaultdict(list) + for record in SeqIO.parse(genomes, "fasta"): + sample = record.id.split("_")[0] + genome_map[sample].append(record) + df_primers_template = preprocess_primers(primers, reference) + task_args = [ + (name, genome_map[name], + df_primers_template, maxmismatch, outdir) + for name in genome_map.keys() + ] + worker_func = process_primer_check_worker + + # Run Parallel Pool Execution + print("Spinning up parallel execution for " + f"{len(genome_map.keys())} samples...") + + dfs = [] + with ProcessPoolExecutor() as executor: + futures = { + executor.submit(worker_func, arg): arg[0] + for arg in task_args + } + + with tqdm(total=len(genome_map.keys()), + desc="Simulation progress...") as pbar: + for future in as_completed(futures): + sample_name = futures[future] + try: + status, result = future.result() + + if status == "success": + print(f"Successfully processed sample {sample_name}") + dfs.append(result) # result is the DataFrame + + elif status == "warning": + print(f"\n[{sample_name}] {result}") - # Parse ALL sequences in the multifasta - for genome_record in genome_seqs: - genome_id = genome_record.id - genome_seq = str(genome_record.seq) - df = find_closest_primer_match(primer_df, genome_seq, - maxmismatch) - all_amplicons = create_valid_primer_combinations(df) - all_amplicons = all_amplicons.fillna(0) - all_amplicons["amplicon_length"] = np.where( - (all_amplicons["primer_start"] != 0) - & (all_amplicons["primer_end"] != 0), - all_amplicons["primer_end"] - - all_amplicons["primer_start"] - + all_amplicons["primer_seq_y"].str.len(), - 0, - ) - all_amplicons["genome_id"] = genome_id + except Exception as e: + print(f"\nError processing sample {sample_name}: {e}") - all_results.append(all_amplicons) - # Combine results from all fasta entries - if all_results: - final_df = pd.concat(all_results, ignore_index=True) + finally: + pbar.update(1) + + # Concatenate all successful DataFrames + if dfs: + final_df = pd.concat(dfs, ignore_index=True) else: final_df = pd.DataFrame() - final_df.to_csv( - os.path.join("amplicon_stats.csv"), - index=False, - ) + final_df.to_csv(os.path.join(outdir, "amplicon_stats.csv"), index=False) if __name__ == "__main__": From 756b7bd993612fbddb723c6099d4fc719ba944bc Mon Sep 17 00:00:00 2001 From: Maryam Date: Mon, 13 Jul 2026 10:12:12 -0700 Subject: [PATCH 41/52] adding worker function for the primer-check parallalization --- bygul/utils.py | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/bygul/utils.py b/bygul/utils.py index 8180aef..8d98c6c 100644 --- a/bygul/utils.py +++ b/bygul/utils.py @@ -748,6 +748,50 @@ def write_fasta_group(group, amplicon_number, output_dir): ) +def process_primer_check_worker(args): + (name, genome_seqs, df_primers_template, + maxmismatch, outdir) = args + + sample_amplicons_list = [] + for genome_seq in genome_seqs: + # Print information about the quality of the provided file + assess_genome_quality_from_fasta(genome_seq) + contig_df = find_closest_primer_match( + df_primers_template.copy(), + str(genome_seq.seq), + maxmismatch + ) + all_amplicons = create_valid_primer_combinations(contig_df) + all_amplicons = all_amplicons.fillna(0) + + all_amplicons["amplicon_length"] = np.where( + (all_amplicons["primer_start"] != 0) & + (all_amplicons["primer_end"] != 0), + all_amplicons["primer_end"] - + all_amplicons["primer_start"] + + all_amplicons["primer_seq_y"].str.len(), + 0, + ) + + all_amplicons["amplicon_sequence"] = all_amplicons.apply( + lambda row: make_amplicon( + row["primer_start"], + row["primer_end"], + row["primer_seq_y"], + genome_seq.seq, + ), axis=1, + ) + all_amplicons["contig_id"] = genome_seq.id + sample_amplicons_list.append(all_amplicons) + + if not sample_amplicons_list: + return ("warning", + f"Warning: No sequences found in {name}") + + full_sample_df = pd.concat(sample_amplicons_list, ignore_index=True) + return ("success", full_sample_df) + + def process_amplicon_worker(args): """Worker for the 'amplicon' simulation mode.""" (name, genome_seqs, cnt, df_primers_template, maxmismatch, outdir, From 7e460e6fa0dc37e9441d84eec33bf33b6e7fd1dc Mon Sep 17 00:00:00 2001 From: Maryam Date: Mon, 13 Jul 2026 10:18:55 -0700 Subject: [PATCH 42/52] updating sample proportion processing --- bygul/utils.py | 61 +++++++++++++++++++++++++++++--------------------- 1 file changed, 35 insertions(+), 26 deletions(-) diff --git a/bygul/utils.py b/bygul/utils.py index 8d98c6c..839e82c 100644 --- a/bygul/utils.py +++ b/bygul/utils.py @@ -20,24 +20,39 @@ def process_sample_proportions( csv, ): """ - Processes sample proportions for read simulation. - - Parameters: - proportions (str or list): Either "NA", a comma-separated string, - or a list of proportions from a CSV. + Process sample proportions for read simulation. + + Parameters + ---------- + proportions : str | list + Either: + - "NA" to generate/default proportions, + - a comma-separated string of proportions, + - or a list of proportions (from a CSV). + sample_names : list[str] + Names of the samples. + outdir : str + Output directory. + csv : str + Indicates whether proportions came from a CSV ("NA" if not). + + Returns + ------- + list[float] + Validated sample proportions. """ + # Proportions supplied from CSV if csv != "NA": proportions = [float(p) for p in proportions] + # No proportions supplied elif proportions == "NA": if len(sample_names) == 1: print( - "Only one sample provided. " - "Using 1.0 as the sample proportion." + "Only one sample provided. Using 1.0 as the sample proportion." ) proportions = [1.0] - else: print( "Read simulation proportions not provided. " @@ -46,30 +61,24 @@ def process_sample_proportions( proportions = generate_random_values(len(sample_names)) - with open( - os.path.join(outdir, "sample_proportions.txt"), - "w", - ) as file: + output_file = os.path.join(outdir, "sample_proportions.txt") + with open(output_file, "w") as f: for name, proportion in zip(sample_names, proportions): - file.write(f"{name}: {proportion}\n") + f.write(f"{name}: {proportion}\n") + # Comma-separated proportions from CLI else: - proportions = [float(x) for x in proportions.split(",")] + proportions = [float(p) for p in proportions.split(",")] - # Validate lengths - if not ( - len(sample_names) - == len(proportions) - ): - raise Exception( - "Number of samples, proportions, and sample paths should match!" + if len(sample_names) != len(proportions): + print("sample length:", len(sample_names)) + print("proportions length:", len(proportions)) + raise ValueError( + "The number of sample names must match the number of proportions." ) - # Validate proportions sum - if round(sum(proportions), 6) != 1.0: - raise Exception( - "Sum of all proportions should equal 1.0!" - ) + if abs(sum(proportions) - 1.0) > 1e-6: + raise ValueError("Sample proportions must sum to 1.0.") return proportions From cbcdc2b4a2628831423aba9964ef8e354d680fcc Mon Sep 17 00:00:00 2001 From: Maryam Date: Mon, 13 Jul 2026 10:19:08 -0700 Subject: [PATCH 43/52] update tests --- bygul/tests/data/test_cli.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/bygul/tests/data/test_cli.py b/bygul/tests/data/test_cli.py index d2293a9..e33b2cb 100644 --- a/bygul/tests/data/test_cli.py +++ b/bygul/tests/data/test_cli.py @@ -55,6 +55,7 @@ def test_simulation_with_readlength(self): --genomes bygul/tests/data/ATM-2FFMD73N3.fasta \ --primers bygul/tests/data/ARTIC_V4-1.bed -1 130 \ -2 130 --reference bygul/tests/data/reference.fasta --redo" + "--reference bygul/tests/data/reference.fasta --redo" ) self.assertTrue(file_exists(".", "results/reads_1.fastq")) @@ -82,6 +83,7 @@ def test_simulation_with_2genomes_proportions(self): def test_simulation_with_metagenomics(self): os.system( "bygul simulate-proportions " + "--primers bygul/tests/data/ARTIC_V4-1.bed " "--genomes bygul/tests/data/ATM-2FFMD73N3.fasta," "bygul/tests/data/KR-SEARCH-120354.fasta " "--simulation_mode metagenomics " @@ -92,9 +94,11 @@ def test_simulation_with_metagenomics(self): def test_simulation_with_mason(self): os.system( "bygul simulate-proportions " + "--primers bygul/tests/data/ARTIC_V4-1.bed " "--genomes bygul/tests/data/ATM-2FFMD73N3.fasta," "bygul/tests/data/KR-SEARCH-120354.fasta " "--proportions 0.8,0.2 --redo --simulator mason " + "--reference bygul/tests/data/reference.fasta " "--illumina-read-length 200" ) self.assertTrue(file_exists(".", "results/reads_1.fastq")) @@ -102,8 +106,11 @@ def test_simulation_with_mason(self): def test_simulation_with_csv(self): os.system( "bygul simulate-proportions " + "--primers bygul/tests/data/ARTIC_V4-1.bed " "--csv bygul/tests/data/sample_proportions.csv " + "--multifasta bygul/tests/data/sample_genomes.fasta " "--redo --simulator mason " + "--reference bygul/tests/data/reference.fasta " "--illumina-read-length 200" ) self.assertTrue(file_exists(".", "results/reads_1.fastq")) From df5075b422376603bddc167c333dc6ac3827bbbf Mon Sep 17 00:00:00 2001 From: Maryam Date: Mon, 13 Jul 2026 10:20:06 -0700 Subject: [PATCH 44/52] adding missing flags to the test --- bygul/tests/data/test_proportions.py | 1 + 1 file changed, 1 insertion(+) diff --git a/bygul/tests/data/test_proportions.py b/bygul/tests/data/test_proportions.py index 1a8238c..e919231 100644 --- a/bygul/tests/data/test_proportions.py +++ b/bygul/tests/data/test_proportions.py @@ -91,6 +91,7 @@ def test_prop_with_freyja_csv_mason(self): "bygul simulate-proportions " "--csv bygul/tests/data/sample_proportions.csv " "--multifasta bygul/tests/data/sample_genomes.fasta " + "--primers bygul/tests/data/ARTIC_V4-1.bed " "--redo " "--simulator mason " "--illumina-read-length 200" From bfd5e09780f13e7423d7da2361fb15eb2aab7ba6 Mon Sep 17 00:00:00 2001 From: Maryam Date: Mon, 13 Jul 2026 10:20:27 -0700 Subject: [PATCH 45/52] remove the extra contig --- bygul/tests/data/ATM-2FFMD73N3.fasta | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/bygul/tests/data/ATM-2FFMD73N3.fasta b/bygul/tests/data/ATM-2FFMD73N3.fasta index 2ec25fe..a00a33d 100644 --- a/bygul/tests/data/ATM-2FFMD73N3.fasta +++ b/bygul/tests/data/ATM-2FFMD73N3.fasta @@ -497,6 +497,4 @@ GTTAACTTTAATCTCACATAGCAATCTTTAATCAGTGTGTAACATTAGGGAGGACTTGAA AGAGCCACCACATTTTCACCGAGGCCACTCGGAGTACGATCGAGTGTACAGTGAACAATG CTAGGGAGAGCTGCCTATATGGAAGAGCCCTAATGTGTAAAATTAATTTTAGTAGTGCTA TCCCCATGTGATTTTAATAGNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN -NNNNNNNNNN ->hCoV-19/USA/ATM-2FFMD73N3/2 -ATCCCCCCCCCGGT \ No newline at end of file +NNNNNNNNNN \ No newline at end of file From af5ca22d9833bf83bccfec28ea31ecb398476c9f Mon Sep 17 00:00:00 2001 From: Maryam Date: Mon, 13 Jul 2026 10:43:39 -0700 Subject: [PATCH 46/52] adding the reference flag --- bygul/tests/data/test_proportions.py | 1 + 1 file changed, 1 insertion(+) diff --git a/bygul/tests/data/test_proportions.py b/bygul/tests/data/test_proportions.py index e919231..44ec622 100644 --- a/bygul/tests/data/test_proportions.py +++ b/bygul/tests/data/test_proportions.py @@ -92,6 +92,7 @@ def test_prop_with_freyja_csv_mason(self): "--csv bygul/tests/data/sample_proportions.csv " "--multifasta bygul/tests/data/sample_genomes.fasta " "--primers bygul/tests/data/ARTIC_V4-1.bed " + "--reference bygul/tests/data/reference.fasta " "--redo " "--simulator mason " "--illumina-read-length 200" From 0d142e98593c040daada1fe36f8ac77ba6928e91 Mon Sep 17 00:00:00 2001 From: Maryam Date: Mon, 13 Jul 2026 10:51:38 -0700 Subject: [PATCH 47/52] add the required multifasta file for testing --- bygul/tests/data/sample_genomes.fasta | 502 ++++++++++++++++++++++++++ 1 file changed, 502 insertions(+) create mode 100644 bygul/tests/data/sample_genomes.fasta diff --git a/bygul/tests/data/sample_genomes.fasta b/bygul/tests/data/sample_genomes.fasta new file mode 100644 index 0000000..a2128a1 --- /dev/null +++ b/bygul/tests/data/sample_genomes.fasta @@ -0,0 +1,502 @@ +>CA-SEARCH-43254 +nnnaaaggtttatacccttcccaggtaacaaaccaaccaactttcgatctcttgtagatctgttctctaaacgaactttaaaatctgtgtggctgtcactcggctgcatgcttagtgcactcacgcagtataattaataactaattactgtcgttgacaggacacgagtaactcgtctatcttctgcaggctgcttacggtttcgtccgttttgcagccgatcatcagcacatctaggttttgtccgggtgtgaccgaaaggtaagatggagagccttgtccctggtttcaacgagaaaacacacgtccaactcagtttgcctgttttacaggttcgcgacgtgctcgtacgtggctttggagactccgtggaggaggtcttatcagaggcacgtcaacatcttaaagatggcacttgtggcttagtagaagttgaaaaaggcgttttgcctcaacttgaacagccctatgtgttcatcaaacgttcggatgctcgaactgcacctcatggtcatgttatggttgagctggtagcagaactcgaaggcattcagtacggtcgtagtggtgagacacttggtgtccttgtccctcatgtgggcgaaataccagtggcttaccgcaaggttcttcttcgtaagaacggtaataaaggagctggtggccatagttacggcgccgatctaaagtcatttgacttaggcgacgagcttggcactgatccttatgaagattttcaagaaaactggaacactaaacatagcagtggtgttacccgtgaactcatgcgtgagcttaacggaggggcatacactcgctatgtcgataacaacttctgtggccctgatggctaccctcttgagtgcattaaagaccttctagcacgtgctggtaaagcttcatgcactttgtccgaacaactggactttattgacactaagaggggtgtatactgctgccgtgaacatgagcatgaaattgcttggtacacggaacgttctgaaaagagctatgaattgcagacaccttttgaaattaaattggcaaagaaatttgacaccttcaatggggaatgtccaaattttgtatttcccttaaattccataatcaagactattcaaccaagggttgaaaagaaaaagcttgatggctttatgggtagaattcgatctgtctatccagttgcgtcaccaaatgaatgcaaccaaatgtgcctttcaactctcatgaagtgtgatcattgtggtgaaacttcatggcagacgggcgattttgttaaagccacttgcgaattttgtggcactgagaatttgactaaagaaggtgccactacttgtggttacttaccccaaaatgctgttgttaaaatttattgtccagcatgtcacaattcagaagtaggacctgagcatagtcttgccgaataccataatgaatctggcttgaaaaccattcttcgtaagggtggtcgcactattgcctttggaggctgtgtgttctcttatgttggttgccataacaagtgtgcctattgggttccacgtgctagcgctaacataggttgtaaccatacaggtgttgttggagaaggttccgaaggtcttaatgacaaccttcttgaaatactccaaaaagagaaagtcaacatcaatattgttggtgactttaaacttaatgaagagatcgccattattttggcatctttttctgcttccacaagtgcttttgtggaaactgtgaaaggtttggattataaagcattcaaacaaattgttgaatcctgtggtaattttaaagttacaaaaggaaaagctaaaaaaggtgcctggaatattggtgaacagaaatcaatactgagtcctctttatgcatttgcatcagaggctgctcgtgttgtacgatcaattttctcccgcactcttgaaactgctcaaaattctgtgcgtgttttacagaaggccgctataacaatactagatggaatttcacagtattcactgagactcattgatgctatgatgttcacatctgatttggctactaacaatctagttgtaatggcctacattacaggtggtgttgttcagttgacttcgcagtggctaactaacatctttggcactgtttatgaaaaactcaaacccgtccttgattggcttgaagagaagtttaaggaaggtgtagagtttcttagagacggttgggaaattgttaaatttatctcaacctgtgcttgtgaaattgtcggtggacaaattgtcacctgtgcaaaggaaattaaggagagtgttcagacattctttaagcttgtaaataaatttttggctttgtgtgctgactctatcattattggtggagctaaacttaaagccttgaatttaggtgaaacatttgtcacgcactcaaagggattgtacagaaagtgtgttaaatccagagaagaaactggcctactcatgcctctaaaagccccaaaagaaattatcttcttagagggagaaacacttcccacagaagtgttaacagaggaagttgtcttgaaaactggtgatttacaaccattagaacaacctactagtgaagctgttgaagctccattggttggtacaccagtttgtattaacgggcttatgttgctcgaaatcaaagacacagaaaagtactgtgcccttgcacctaatatgatggtaacaaacaataccttcacactcaaaggcggtgcaccaacaaaggttacttttggtgatgacactgtgatagaagtgcaaggttacaagagtgtgaatatcacttttgaacttgatgaaaggattgataaagtacttaatgagaagtgctctgcctatacagttgaactcggtacagaagtaaatgagttcgcctgtgttgtggcagatgctgtcataaaaacgttgcaaccagtatctgaattacttacaccactgggcattgatttagatgagtggagtatggctacatactacttatttgatgagtctggtgagtttaaattggcttcacatatgtattgttctttttaccctccagatgaggatgaagaagaaggtgattgtgaagaagaagagtttgagccatcaactcaatatgagtatggtactgaagatgattaccaaggtaaacctttggaatttggtgccacttctgctgctcttcaacctgaagaagagcaagaagaagattggttagatgatgatagtcaacaaactgttggtcaacaagacggcagtgaggacaatcagacaactactattcaaacaattgttgaggttcaacctcaattagagatggaacttacaccagttgttcagactattgaagtgaatagttttagtggttatttaaaacttactgacaatgtatacattaaaaatgcagacattgtggaagaagctaaaaaggtaaaaccaacagtggttgttaatgcagccaatgtttaccttaaacatggaggaggtgttgcaggagccttaaataaggctactaacaatgccatgcaagttgaatctgatgattacatagctactaatggaccacttaaagtgggtggtagttgtgttttaagcggacacaatcttgctaaacactgtcttcatgttgtcggcccaaatgttaacaaaggtgaagacattcaacttcttaagagtgcttatgaaaattttaatcagcacgaagttctacttgcaccattattatcagctggtatttttggtgctgaccctatacattctttaagagtttgtgtagatactgttcgcacaaatgtctacttagctgtctttgataaaaatctctatgacaaacttgtttcaagctttttggaaatgaagagtgaaaagcaagttgaacaaaagatcgctgagactcctaaagaggaagttaagccatttataactgaaagtaaaccttcagttgaacagagaaaacaagatgataagaaaatcaaagcttgtgttgaagaagttacaacaactctggaagaaactaagttcctcacagaaaacttgttactttatattgacattaatggcaatcttcatccagattctgccactcttgttagtgacattgacatcactttcttaaagaaagatgctccatatatagtgggtgatgttgttcaagagggtgttttaactgctgtggttatacctactaaaaaggctggtggcactactgaaatgctagcgaaagctttgagaaaagtgccaacagacaattatataaccacttacccgggtcagggtttaaatggttacactgtagaggaggcaaagacagtgcttaaaaagtgtaaaagtgccttttacattctaccatctattatctctaatgagaagcaagaaattcttggaactgtttcttggaatttgcgagaaatgcttgcacatgcagaagaaacacgcaaattaatgcctgtctgtgtggaaactaaagccatagtttcaactatacagcgtaaatataagggtattaaaatacaagagggtgtggttgattatggtgctagattttacttttacaccagtaaaacaactgtagcgtcacttatcaacacacttaacgatctaaatgaaactcttgttacaatgccacttggctatgtaacacatggcttaaatttggaagaagctgctcggtatatgagatctctcaaagtgccagctacagtttctgtttcttcacctgatgctgttacagcgtataatggttatcttacttcttcttctaaaacacctgaagaacattttattgaaaccatctcacttgctggttcctataaagattggtcctattctggacaatctacacaactaggtatagaatttcttaagagaggtgataaaagtgtatattacactagtaatcctaccacattccacctagatggtgaagttatcacctttgacaatcttaagacacttctttctttgagagaagtgaggactattaaggtgtttacaacagtagacaacattaacctccacacgcaagttgtggacatgtcaatgacatatggacaacagtttggtccaacttatttggatggagctgatgttactaaaataaaacctcataattcacatgaaggtaaaacattttatgttttacctaatgatgacactctacgtgttgaggcttttgagtactaccacacaactgatcttagttttctgggtaggtacatgtcagcattaaatcacactaaaaagtggaaatacccacaagttaatggtttaacttctattaaatgggcagataacaactgttatcttgccactgcattgttaacactccaacaaatagagttgaagtttaatccacctgctctacaagatgcttattacagagcaagggctggtgaagctgctaacttttgtgcacttatcttagcctactgtaataagacagtaggtgagttaggtgatgttagagaaacaatgagttacttgtttcaacatgccaatttagattcttgcaaaagagtcttgaacgtggtgtgtaaaacttgtggacaacagcagacaacccttaagggtgtagaagctgttatgtacatgggcacgctttcttatgaacaatttaagaaaggtgttcagataccttgtacgtgtggtaaacaagctacaaaatatctagtacaacaggagtcaccttttgttatgatgtcagcaccacctgctcagtatgaacttaagcatggtacatttacttgtgctagtgagtacactggtaattaccagtgtggtcactataaacatataacttctaaagaaactttgtattgcatagacggtgctttacttacaaagtcctcagaatacaaaggtcctattacggatgttttctacaaagaaaacagttacacaacaaccataaaaccagttacttataaattggatggtgttgtttgtacagaaattgaccctaagttggacaattattataagaaagacaattcttatttcacagagcaaccaattgatcttgtaccaaaccaaccatatccaaacgcaagcttcgataattttaagtttgtatgtgataatatcaaatttgctgatgatttaaaccagttaactggttataagaaacctgcttcaagagagcttaaagttacatttttccctgacttaaatggtgatgtggtggctattgattataaacactacacaccctcttttaagaaaggagctaaattgttacataaacctattgtttggcatgttaacaatgcaactaataaagccacgtataaaccaaatacctggtgtatacgttgtctttggagcacaaaaccagttgaaacatcaaattcgtttgatgtactgaagtcagaggacgcgcagggaatggataatcttgcctgcgaagatctaaaaccagtctctgaagaagtagtggaaaatcctaccatacagaaagacgttcttgagtgtaatgtgaaaactaccgaagttgtaggagacattatacttaaaccagcaaataatagtttaaaaattacagaagaggttggccacacagatctaatggctgcttatgtagacaattctagtcttactattaagaaacctaatgaattatctagagtattaggtttgaaaacccttgctactcatggtttagctgctgttaatagtgtcccttgggatactatagctaattatgctaagccttttcttaacaaagttgttagtacaactactaacatagttacacggtgtttaaaccgtgtttgtactaattatatgccttatttctttactttattgctgcaattgtgtacttttactagaagtacaaattctagaattaaagcatctatgccgactactatagcaaagaatactgttaagagtgtcggtaaattttgtctagaggcttcatttaattatttgaagtcacctaatttttctaaactgataaatattataatttggtttttactattaagtgtttgcctaggttctttaatctactcaaccgctgctttaggtgttttaatgtctaatttaggcatgccttcttactgtactggttacagagaaggctatttgaactctactaatgtcactattgcaacctactgtactggttctataccttgtagtgtttgtcttagtggtttagattctttagacacctatccttctttagaaactatacaaattaccatttcatcttttaaatgggatttaactgcttttggcttagttgcagagtggtttttggcatatattcttttcactaggtttttctatgtacttggattggctgcaatcatgcaattgtttttcagctattttgcagtacattttattagtaattcttggcttatgtggttaataattaatcttgtacaaatggccccgatttcagctatggttagaatgtacatcttctttgcatcattttattatgtatggaaaagttatgtgcatgttgtagacggttgtaattcatcaacttgtatgatgtgttacaaacgtaatagagcaacaagagtcgaatgtacaactattgttaatggtgttagaaggtccttttatgtctatgctaatggaggtaaaggcttttgcaaactacacaattggaattgtgttaattgtgatacattctgtgctggtagtacatttattagtgatgaagttgcgagagacttgtcactacagtttaaaagaccaataaatcctactgaccagtcttcttacatcgttgatagtgttacagtgaagaatggttccatccatctttactttgataaagctggtcaaaagacttatgaaagacattctctctctcattttgttaacttagacaacctgagagctaataacactaaaggttcattgcctattaatgttatagtttttgatggtaaatcaaaatgtgaagaatcatctgcaaaatcagcgtctgtttactacagtcagcttatgtgtcaacctatactgttactagatcaggcattagtgtctgatgttggtgatagtgcggaagttgcagttaaaatgtttgatgcttacgttaatacgttttcatcaacttttaacgtaccaatggaaaaactcaaaacactagttgcaactgcagaagctgaacttgcaaagaatgtgtccttagacaatgtcttatctacttttatttcagcagctcggcaagggtttgttgattcagatgtagaaactaaagatgttgttgaatgtcttaaattgtcacatcaatctgacatagaagttactggcgatagttgtaataactatatgctcacctataacaaagttgaaaacatgacaccccgtgaccttggtgcttgtattgactgtagtgcgcgtcatattaatgcgcaggtagcaaaaagtcacaacattgctttgatatggaacgttaaagatttcatgtcattgtctgaacaactacgaaaacaaatacgtagtgctgctaaaaagaataacttaccttttaagttgacatgtgcaactactagacaagttgttaatgttgtaacaacaaagatagcacttaagggtggtaaaattgttaataattggttgaagcagttaattaaagttacacttgtgttcctttttgttgctgctattttctatttaataacacctgttcatgtcatgtctaaacatactgacttttcaagtgaaatcataggatacaaggctattgatggtggtgtcactcgtgacatagcatctacagatacttgttttgctaacaaacatgctgattttgacacatggtttagccagcgtggtggtagttatactaatgacaaagcttgcccattgattgctgcagtcataacaagagaagtgggttttgtcgtgcctggtttgcctggcacgatattacgcacaactaatggtgactttttgcatttcttacctagagtttttagtgcagttggtaacatctgttacacaccatcaaaacttatagagtacactgactttgcaacatcagcttgtgttttggctgctgaatgtacaatttttaaagatgcttctggtaagccagtaccatattgttatgataccaatgtactagaaggttctgttgcttatgaaagtttacgccctgacacacgttatgtgctcatggatggctctattattcaatttcctaacacctaccttgaaggttctgttagagtggtaacaacttttgattctgagtactgtaggcacggcacttgtgaaagatcagaagctggtgtttgtgtatctactagtggtagatgggtacttaacaatgattattacagatctttaccaggagttttctgtggtgtagatgctgtaaatttacttactaatatgtttacaccactaattcaacctattggtgctttggacatatcagcatctatagtagctggtggtattgtagctatcgtagtaacatgccttgcctactattttatgaggtttagaagagcttttggtgaatacagtcatgtagttgcctttaatactttactattccttatgtcattcactgtactctgtttaacaccagtttactcattcttacctggtgtttattctgttatttacttgtacttgacattttatcttactaatgatgtttcttttttagcacatattcagtggatggttatgttcacacctttagtacctttctggataacaattgcttatatcatttgtatttccacaaagcatttctattggttctttagtaattacctaaagagacgtgtagtctttaatggtgtttcctttagtacttttgaagaagctgcgctgtgcacctttttgttaaataaagaaatgtatctaaagttgcgtagtgatgtgctattacctcttacgcaatataatagatacttagttctttataataagtacaagtattttagtggagcaatggatacaactagctacagagaagctgcttgttgtcatctcgcaaaggctctcaatgacttcagtaactcaggttctgatgttctttaccaaccaccacaaacctctatcacctcagctgttttgcagagtggttttagaaaaatggcattcccatctggtaaagttgagggttgtatggtacaagtaacttgtggtacaactacacttaacggtctttggcttgatgacgtagtttactgtccaagacatgtgatctgcacctctgaagacatgcttaaccctaattatgaagatttactcattcgtaagtctaatcataatttcttggtacaggctggtaatgttcaactcagggttattggacattctatgcaaaattgtgtacttaagcttaaggttgatacagccaatcctaagacacctaagtataagtttgttcgcattcaaccaggacagactttttcagtgttagcttgttacaatggttcaccatctggtgtttaccaatgtgctatgaggcccaatttcactattaagggttcattccttaatggttcatgtggtagtgttggttttaacatagattatgactgtgtctctttttgttacatgcaccatatggaattaccaactggagttcatgctggcacagacttagaaggtaacttttatggaccttttgttgacaggcaaacagcacaagcagctggtacggacacaactattacagttaatgttttagcttggttgtacgctgctgttataaatggagacaggtggtttctcaatcgatttaccacaactcttaatgactttaaccttgtggctatgaagtacaattatgaacctctaacacaagaccatgttgacatactaggacctctttctgctcaaactggaattgccgttttagatatgtgtgcttcattaaaagaattactgcaaaatggtatgaatggacgtaccatattgggtagtgctttattagaagatgaatttacaccttttgatgttgttagacaatgctcaggtgttactttccaaagtgcagtgaaaagaacaatcaagggtacacaccactggttgttactcacaattttgacttcacttttagttttagtccagagtactcaatggtctttgttcttttttttgtatgaaaatgcctttttaccttttgctatgggtattattgctatgtctgcttttgcaatgatgtttgtcaaacataagcatgcatttctctgtttgtttttgttaccttctcttgccactgtagcttattttaatatggtctatatgcctgctagttgggtgatgcgtattatgacatggttggatatggttgatactagtttgtctggttttaagctaaaagactgtgttatgtatgcatcagctgtagtgttactaatccttatgacagcaagaactgtgtatgatgatggtgctaggagagtgtggacacttatgaatgtcttgacactcgcttataaagtttattatggtaatgctttagatcaagccatttccatgtgggctcttataatctctgttacttctaactactcaggtgtagttacaattgtcatgtttttggccagaggtattgtttttatgtgtgttgagtattgccctattttcttcataactggtaatacacttcagtgtataatgctagtttattgtttcttaggctatttttgtacttgttactttggcctcttttgtttactcaaccgctactttagactgactcttggtgtttatgattacttagtttctacacaggagtttagatatatgaattcacagggactactcccacccaagaatagcatagatgccttcaaactcaacattaaattgttgggtgttggtggcaaaccttgtatcaaagtagccactgtacagtctaaaatgtcagatgtaaagtgcacatcagtagtcttactctcagttttgcaacaactcagagtagaatcatcatctaaattgtgggctcaatgtgtccagttacacaatgacattctcttagctaaagatactactgaagcctttgaaaaaatggtttcactactttctgttttgctttccatgcagggtgctgtagacataaacaagctttgtgaagaaatgctggacaacagggcaaccttacaagctatagcctcagagtttagttcccttccatcatatgcagcttttgctactgctcaagaagcttatgagcaggctgttgctaatggtgattctgaagttgttcttaaaaagttgaagaagtctttgaatgtggctaaatctgaatttgaccgtgatgcagccatgcaacgtaagttggaaaagatggctgatcaagctatgacccaaatgtataaacaggctagatctgaggacaagagggcaaaagttactagtgctatgcagacaatgcttttcactatgcttagaaagttggataatgatgcactcaacaacattatcaacaatgcaagagatggttgtgttcccttgaacataatacctcttacaacagcagccaaactaatggttgtcataccagactataacacatataaaaatacgtgtgatggtacaacatttacttatgcatcagcattgtgggaaatccaacaggttgtagatgcagatagtaaaattgttcaacttagtgaaattagtatggacaattcacctaatttagcatggcctcttattgtaacagctttaagggccaattctgctgtcaaattacagaataatgagcttagtcctgttgcactacgacagatgtcttgtgctgccggtactacacaaactgcttgcactgatgacaatgcgttagcttactacaacacaacaaagggaggtaggtttgtacttgcactgttatccgatttacaggatttgaaatgggctagattccctaagagtgatggaactggtactatctatacagaactggaaccaccttgtaggtttgttacagacacacctaaaggtcctaaagtgaagtatttatactttattaaaggattaaacaacctaaatagaggtatggtacttggtagtttagctgccacagtacgtttacaagctggtaatgcaacagaagtgcctgccaattcaactgtattatctttctgtgcttttgctgtagatgctgctaaagcttacaaagattatctagctagtgggggacaaccaatcactaattgtgttaagatgttgtgtacacacactggtactggtcaggcaataacagttacaccggaagccaatatggatcaagaatcctttggtggtgcatcgtgttgtctgtactgccgttgccacatagatcatccaaatcctaaaggattttgtgacttaaaaggtaagtatgtacaaatacctacaacttgtgctaatgaccctgtgggttttacacttaaaaacacagtctgtaccgtctgcggtatgtggaaaggttatggctgtagttgtgatcaactccgcgaacccatgcttcagtcagctgatgcacaatcgtttttaaacgggtttgcggtgtaagtgcagcccgtcttacaccgtgcggcacaggcactagtactgatgtcgtatacagggcttttgacatctacaatgataaagtagctggttttgctaaattcctaaaaactaattgttgtcgcttccaagaaaaggacgaagatgacaatttaattgattcttactttgtagttaagagacacactttctctaactaccaacatgaagaaacaatttataatttacttaaggattgtccagctgttgctaaacatgacttctttaagtttagaatagacggtgacatggtaccacatatatcacgtcaacgtcttactaaatacacaatggcagacctcgtctatgctttaaggcattttgatgaaggtaattgtgacacattaaaagaaatacttgtcacatacaattgttgtgatgatgattatttcaataaaaaggactggtatgattttgtagaaaacccagatatattacgcgtatacgccaacttaggtgaacgtgtacgccaagctttgttaaaaacagtacaattctgtgatgccatgcgaaatgctggtattgttggtgtactgacattagataatcaagatctcaatggtaactggtatgatttcggtgatttcatacaaaccacgccaggtagtggagttcctgttgtagattcttattattcattgttaatgcctatattaaccttgaccagggctttaactgcagagtcacatgttgacactgacttaacaaagccttacattaagtgggatttgttaaaatatgacttcacggaagagaggttaaaactctttgaccgttattttaaatattgggatcagacataccacccaaattgtgttaactgtttggatgacagatgcattctgcattgtgcaaactttaatgttttattctctacagtgttcccacttacaagttttggaccactagtgagaaaaatatttgttgatggtgttccatttgtagtttcaactggataccacttcagagagctaggtgttgtacataatcaggatgtaaacttacatagctctagacttagttttaaggaattacttgtgtatgctgctgaccctgctatgcacgctgcttctggtaatctattactagataaacgcactacgtgcttttcagtagctgcacttactaacaatgttgcttttcaaactgtcaaacccggtaattttaacaaagacttctatgactttgctgtgtctaagggtttctttaaggaaggaagttctgttgaattaaaacacttcttctttgctcaggatggtaatgctgctatcagcgattatgactactatcgttataatctaccaacaatgtgtgatatcagacaactactatttgtagttgaagttgttgataagtactttgattgttacgatggtggctgtattaatgctaaccaagtcatcgtcaacaacctagacaaatcagctggttttccatttaataaatggggtaaggctagactttattatgattcaatgagttatgaggatcaagatgcacttttcgcatatacaaaacgtaatgtcatccctactataactcaaatgaatcttaagtatgccattagtgcaaagaatagagctcgcaccgtagctggtgtctctatctgtagtactatgaccaatagacagtttcatcaaaaattattgaaatcaatagccgccactagaggagctactgtagtaattggaacaagcaaattctatggtggttggcacaacatgttaaaaactgtttatagtgatgtagaaaaccctcaccttatgggttgggattatcctaaatgtgatagagccatgcctaacatgcttagaattatggcctcacttgttcttgctcgcaaacatacaacgtgttgtagcttgtcacaccgtttctatagattagctaatgagtgtgctcaagtattgagtgaaatggtcatgtgtggcagttcactatatgttaaaccaggtggaacctcatcaggagatgccacaactgcttatgctaatagtgtttttaacatttgtcaagctgtcacggccaatgttaatgcacttttatctactgatggtaacaaaattgccgataagtatgtccgcaatttacaacacagactttatgagtgtctctatagaaatagagatgttgacacagactttgtgaatgagttttacgcatatttgcgtaaacatttctcaatgatgatactctctgacgatgctgttgtgtgtttcaatagcacttatgcatctcaaggtctagtggctagcataaagaactttaagtcagttctttattatcaaaacaatgtttttatgtctgaagcaaaatgttggactgagactgaccttactaaaggacctcatgaattttgctctcaacatacaatgctagttaaacagggtgatgattatgtgtaccttccttacccagatccatcaagaatcctaggggccggctgttttgtagatgatatcgtaaaaacagatggtacacttatgattgaacggttcgtgtctttagctatagatgcttacccacttactaaacatcctaatcaggagtatgctgatgtctttcatttgtacttacaatacataagaaagctacatgatgagttaacaggacacatgttagacatgtattctgttatgcttactaatgataacacttcaaggtattgggaacctgagttttatgaggctatgtacacaccgcatacagtcttacaggctgttggggcttgtgttctttgcaattcacagacttcattaagatgtggtgcttgcatacgtagaccattcttatgttgtaaatgctgttacgaccatgtcatatcaacatcacataaattagtcttgtctgttaatccgtatgtttgcaatgctccaggttgtgatgtcacagatgtgactcaactttacttaggaggtatgagctattattgtaaatcacataaactacccattagttttccattgtgtgctaatggacaagtttttggtttatataaaaatacatgtgttggtagcgataatgttactgactttaatgcaattgcaacatgtgactggacaaatgctggtgattacattttagctaacacctgtactgaaagactcaagctttttgcagcagaaacgctcaaagctactgaggagacatttaaactgtcttatggtattgctactgtacgtgaagtgctgtctgacagagaattacatctttcatgggaagttggtaaacctagaccaccacttaaccgaaattatgtctttactggttatcgtgtaactaaaaacagtaaagtacaaataggagagtacacctttgaaaaaggtgactatggtgatgctgttgtttaccgaggtacaacaacttacaaattaaatgttggtgattattttgtgctgacatcacatacagtaatgccattaagtgcacctacactagtgccacaagagcactatgttagaattactggcttatacccaacactcaatatctcagatgagttttctagcaatgttgcaaattatcaaaaggttggtatgcaaaagtattctacactccagggaccacctggtactggtaagagtcattttgctattggcctagctctctactacccttctgctcgcatagtgtatacagcttgctctcatgccgctgttgatgcactatgtgagaaggcattaaaatatttgcctatagataaatgtagtagaattatacctgcacgtgctcgtgtagagtgttttgataaattcaaagtgaattcaacattagaacagtatgtcttttgtactgtaaatgcattgcctgagacgacagcagatatagttgtctttgatgaaatttcaatggccacaaattatgatttgagtgttgtcaatgccagattacgtgctaagcactatgtgtacattggcgaccctgctcaattacctgcaccacgcacattgctaactaagggcacactagaaccagaatatttcaattcagtgtgtagacttatgaaaactataggtccagacatgttcctcggaacttgtcggcgttgtcctgctgaaattgttgacactgtgagtgctttggtttatgataataagcttaaagcacataaagacaaatcagctcaatgctttaaaatgttttataagggtgttatcacgcatgatgtttcatctgcaattaacaggccacaaataggcgtggtaagagaattccttacacgtaaccctgcttggagaaaagctgtctttatttcaccttataattcacagaatgctgtagcctcaaagattttgggactaccaactcaaactgttgattcatcacagggctcagaatatgactatgtcatattcactcaaaccactgaaacagctcactcttgtaatgtaaacagatttaatgttgctattaccagagcaaaagtaggcatactttgcataatgtctgatagagacctttatgacaagttgcaatttacaagtcttgaaattccacgtaggaatgtggcaactttacaagctgaaaatgtaacaggactctttaaagattgtagtaaggtaatcactgggttacatcctacacaggcacctacacacctcagtgttgacactaaattcaaaactgaaggtttatgtgttgacatacctggcatacctaaggacatgacctatagaagactcatctctatgatgggttttaaaatgaattatcaagttaatggttaccctaacatgtttatcacccgcgaagaagctataagacatgtacgtgcatggattggcttcgatgttgaggggtgtcatgctactagagaagctgttggtaccaatttacctttacagctaggtttttctacaggtgttaacctagttgctgtacctacaggttatgttgatacacctaataatacagatttttccagagttagtgctaaaccaccgcctggagatcaatttaaacacctcataccacttatgtacaaaggacttccttggaatgtagtgcgtataaagattgtacaaatgttaagtgacacacttaaaaatctctctgacagagtcgtatttgtcttatgggcacatggctttgagttgacatctatgaagtattttgtgaaaataggacctgagcgcacctgttgtctatgtgatagacgtgccacatgcttttccactgcttcagacacttatgcctgttggcatcattctattggatttgattacgtctataatccgtttatgattgatgttcaacaatggggttttacaggtaacctacaaagcaaccatgatctgtattgtcaagtccatggtaatgcacatgtagctagttgtgatgcaatcatgactaggtgtctagctgtccacgagtgctttgttaagcgtgttgactggactattgaatatcctataattggtgatgaactgaagattaatgcggcttgtagaaaggttcaacacatggttgttaaagctgcattattagcagacaaattcccagttcttcacgacattggtaaccctaaagctattaagtgtgtacctcaagctgatgtagaatggaagttctatgatgcacagccttgtagtgacaaagcttataaaatagaagaattattctattcttatgccacacattctgacaaattcacagatggtgtatgcctattttggaattgcaatgtcgatagatatcctgctaattccattgtttgtagatttgacactagagtgctatctaaccttaacttgcctggttgtgatggtggcagtttgtatgtaaataaacatgcattccacacaccagcttttgataaaagtgcttttgttaatttaaaacaattaccatttttctattactctgacagtccatgtgagtctcatggaaaacaagtagtgtcagatatagattatgtaccactaaagtctgctacgtgtataacacgttgcaatttaggtggtgctgtctgtagacatcatgctaatgagtacagattgtatctcgatgcttataacatgatgatctcagctggctttagcttgtgggtttacaaacaatttgatacttataacctctggaacacttttacaagacttcagagtttagaaaatgtggcttttaatgttgtaaataagggacactttgatggacaacagggtgaagtaccagtttctatcattaataacactgtttacacaaaagttgatggtgttgatgtagaattgtttgaaaataaaacaacattacctgttaatgtagcatttgagctttgggctaagcgcaacattaaaccagtaccagaggtgaaaatactcaataatttgggtgtggacattgctgctaatactgtgatctgggactacaaaagagatgctccagcacatatatctactattggtgtttgttctatgactgacatagccaagaaaccaactgaaacgatttgtgcaccactcactgtcttttttgatggtagagttgatggtcaagtagacttatttagaaatgcccgtaatggtgttcttattacagaaggtagtgttaaaggtttacaaccatctgtaggtcccaaacaagctagtcttaatggagtcacattaattggagaagccgtaaaaacacagttcaattattataagaaagttgatggtgttgtccaacaattacctgaaacttactttactcagagtagaaatttacaagaatttaaacccaggagtcaaatggaaattgatttcttagaattagctatggatgaattcattgaacggtataaattagaaggctatgccttcgaacatatcgtttatggagattttagtcatagtcagttaggtggtttacatctactgattggactagctaaacgttttaaggaatcaccttttgaattagaagattttattcctatggacagtacagttaaaaactatttcataacagatgcgcaaacaggttcatctaagtgtgtgtgttctgttattgatttattacttgatgattttgttgaaataataaaatcccaagatttatctgtagtttctaaggttgtcaaagtgactattgactatacagaaatttcatttatgctttggtgtaaagatggccatgtagaaacattttacccaaaattacaatctagtcaagcgtggcaaccgggtgttgctatgcctaatctttacaaaatgcaaagaatgctattagaaaagtgtgaccttcaaaattatggtgatagtgcaacattacctaaaggcataatgatgaatgtcgcaaaatatactcaactgtgtcaatatttaaacacattaacattagctgtaccctataatatgagagttatacattttggtgctggttctgataaaggagttgcaccaggtacagctgttttaagacagtggttgcctacgggtacgctgcttgtcgattcagatcttaatgactttgtctctgatgcagattcaactttgattggtgattgtgcaactgtacatacagctaataaatgggatctcattattagtgatatgtacgaccctaagactaaaaatgttacaaaagaaaatgactctaaagagggttttttcacttacatttgtgggtttatacaacaaaagctagctcttggaggttccgtggctataaagataacagaacattcttggaatgctgatctttataagctcatgggacacttcgcatggtggacagcctttgttactaatgtgaatgcgtcatcatctgaagcatttttaattggatgtaattatcttggcaaaccacgcgaacaaatagatggttatgtcatgcatgcaaattacatattttggaggaatacaaatccaattcagttgtcttcctattctttatttgacatgagtaaatttccccttaaattaaggggtactgctgttatgtctttaaaagaaggtcaaatcaatgatatgattttatctcttcttagtaaaggtagacttataattagagaaaacaacagagttgttatttctagtgatgttcttgttaacaactaaacgaacaatgtttgtttttcttgttttattgccactagtctctagtcagtgtgttaatcttagaaccagaactcaattaccccctgcatacactaattctttcacacgtggtgtttattaccctgacaaagttttcagatcctcagttttacattcaactcaggacttgttcttacctttcttttccaatgttacttggttccatgctatacatgtctctgggaccaatggtactaagaggtttgataaccctgtcctaccatttaatgatggtgtttattttgcttccactgagaagtctaacataataagaggctggatttttggtactactttagattcgaagacccagtccctacttattgttaataacgctactaatgttgttattaaagtctgtgaatttcaattttgtaatgatccatttttggatgtttattaccacaaaaacaacaaaagttggatggaaagtggagtttattctagtgcgaataattgcacttttgaatatgtctctcagccttttcttatggaccttgaaggaaaacagggtaatttcaaaaatcttagggaatttgtgtttaagaatattgatggttattttaaaatatattctaagcacacgcctattaatttagtgcgtgatctccctcagggtttttcggttttagaaccattggtagatttgccaataggtattaacatcactaggtttcaaactttacttgctttacatagaagttatttgactcctggtgattcttcttcaggttggacagctggtgctgcagcttattatgtgggttatcttcaacctaggacttttctattaaaatataatgaaaatggaaccattacagatgctgtagactgtgcacttgaccctctctcagaaacaaagtgtacgttgaaatccttcactgtagaaaaaggaatctatcaaacttctaactttagagtccaaccaacagaatctattgttagatttcctaatattacaaacttgtgcccttttggtgaagtttttaacgccaccagatttgcatctgtttatgcttggaacaggaagagaatcagcaactgtgttgctgattattctgtcctatataattccgcatcattttccacttttaagtgttatggagtgtctcctactaaattaaatgatctctgctttactaatgtctatgcagattcatttgtaattagaggtgatgaagtcagacaaatcgctccagggcaaactggaaagattgctgattataattataaattaccagatgattttacaggctgcgttatagcttggaattctaacaatcttgattctaaggttggtggtaattataattaccggtatagattgtttaggaagtctaatctcaaaccttttgagagagatatttcaactgaaatctatcaggccggtagcaaaccttgtaatggtgttgaaggttttaattgttactttcctttacaatcatatggtttccaacccactaatggtgttggttaccaaccatacagagtagtagtactttcttttgaacttctacatgcaccagcaactgtttgtggacctaaaaagtctactaatttggttaaaaacaaatgtgtcaatttcaacttcaatggtttaacaggcacaggtgttcttactgagtctaacaaaaagtttctgcctttccaacaatttggcagagacattgctgacactactgatgctgtccgtgatccacagacacttgagattcttgacattacaccatgttcttttggtggtgtcagtgttataacaccaggaacaaatacttctaaccaggttgctgttctttatcagggtgttaactgcacagaagtccctgttgctattcatgcagatcaacttactcctacttggcgtgtttattctacaggttctaatgtttttcaaacacgtgcaggctgtttaataggggctgaacatgtcaacaactcatatgagtgtgacatacccattggtgcaggtatatgcgctagttatcagactcagactaattctcgtcggcgggcacgtagtgtagctagtcaatccatcattgcctacactatgtcacttggtgcagaaaattcagttgcttactctaataactctattgccatacccacaaattttactattagtgttaccacagaaattctaccagtgtctatgaccaagacatcagtagattgtacaatgtacatttgtggtgattcaactgaatgcagcaatcttttgttgcaatatggcagtttttgtacacaattaaaccgtgctttaactggaatagctgttgaacaagacaaaaacacccaagaagtttttgcacaagtcaaacaaatttacaaaacaccaccaattaaagattttggtggttttaatttttcacaaatattaccagatccatcaaaaccaagcaagaggtcatttattgaagatctacttttcaacaaagtgacacttgcagatgctggcttcatcaaacaatatggtgattgccttggtgatattgctgctagagacctcatttgtgcacaaaagtttaacggccttactgttttgccacctttgctcacagatgaaatgattgctcaatacacttctgcactgttagcgggtacaatcacttctggttggacctttggtgcaggtgctgcattacaaataccatttgctatgcaaatggcttataggtttaatggtattggagttacacagaatgttctctatgagaaccaaaaattgattgccaaccaatttaatagtgctattggcaaaattcaagactcactttcttccacagcaagtgcacttggaaaacttcaaaatgtggtcaaccaaaatgcacaagctttaaacacgcttgttaaacaacttagctccaattttggtgcaatttcaagtgttttaaatgatatcctttcacgtcttgacaaagttgaggctgaagtgcaaattgataggttgatcacaggcagacttcaaagtttgcagacatatgtgactcaacaattaattagagctgcagaaatcagagcttctgctaatcttgctgctactaaaatgtcagagtgtgtacttggacaatcaaaaagagttgatttttgtggaaagggctatcatcttatgtccttccctcagtcagcacctcatggtgtagtcttcttgcatgtgacttatgtccctgcacaagaaaagaacttcacaactgctcctgccatttgtcatgatggaaaagcacactttcctcgtgaaggtgtctttgtttcaaatggcacacactggtttgtaacacaaaggaatttttatgaaccacaaatcattactacagacaacacatttgtgtctggtaactgtgatgttgtaataggaattgtcaacaacacagtttatgatcctttgcaacctgaattagactcattcaaggaggagttagataaatattttaagaatcatacatcaccagatgttgatttaggtgacatctctggcattaatgcttcagttgtaaacattcaaaaagaaattgaccgcctcaatgaggttgccaagaatttaaatgaatctctcatcgatctccaagaacttggaaagtatgagcagtatataaaatggccatggtacatttggctaggttttatagctggcttgattgccatagtaatggtgacaattatgctttgctgtatgaccagttgctgtagttgtctcaagggctgttgttcttgtggatcctgctgcaaatttgatgaagacgactctgagccattgctcaaaggagtcaaattacattacacataaacgaacttatggatttgtttatgagaatcttcacaattggaactgtaactttgaagcaaggtgaaatcaaggatgctactcctttagattttgttcgcgctactgcaacgataccgatacaagcctcactccctttcggatggcttattgttggcgttgcacttcttgctgtttttcagagcgcttccaaaatcataaccctcaaaaagagatggcaactagcactctccaagggtgttcactttgtttgcaacttgctgttgttgtttgtaacagtttactcacaccttttgctcgttgctgctggccttgaagccccttttctctatctttatgctttagtctacttcttgcagagtataaactttgtaagaataataatgaggctttggctttgctggaaatgccgttccaaaaacccattactttatgatgccaactattttctttgctggcatactaattgttacgactattgtataccttacaatagtgtaacttcttcaattgtcattacttcaggtgatggcacaacaagtcctatttctgaacatgactaccagattggtggttatactgaaaaatgggaatctggagtaaaagactgtgttgtattacacagttacttcacttcagactattaccagctgtactcaactcaattgagtacagacactggtgttgaacatgttaccttcttcatctacaataaaattgttgatgagcctgaagaacatgtccaaattcacacaatcgacggttcatccggagttgttaatccagtaatggaaccaatttatgatgaaccgacgacgactactagcgtgcctttgtaagcacaagctgatgagtacgaacttatgtactcattcgtttcggaagagacaggtacgttaatagttaatagcgtacttctttttcttgctttcgtggtattcttgctagttacactagccatccttactgcgcttcgattgtgtgcgtactgctgcaatattgttaacgtgagtcttgtaaaaccttctttttacgtttactctcgtgttaaaaatctgaattcttctagagttcctgatcttctggtctaaacgaactaaatattatattagtttttctgtttggaactttaattttagccatggcagattccaacggtactattaccgttgaagagcttaaaaagctccttgaacaatggaacctagtaataggtttcctattccttacatggatttgtcttctacaatttgcctatgccaacaggaataggtttttgtatataattaagttaattttcctctggctgttatggccagtaactttagcttgttttgtgcttgctgctgtttacagaataaattggatcaccggtggaattgctaccgcaatggcttgtcttgtaggcttgatgtggctcagctacttcattgcttctttcagactgtttgcgcgtacgcgttccatgtggtcattcaatccagaaactaacattcttctcaacgtgccactccatggcactattctgaccagaccgcttctagaaagtgaactcgtaatcggagctgtgatccttcgtggacatcttcgtattgctggacaccatctaggacgctgtgacatcaaggacctgcctaaagaaatcactgttgctacatcacgaacgctttcttattacaaattgggagcttcgcagcgtgtagcaggtgactcaggttttgctgcatacagtcgctacaggattggcaactataaattaaacacagaccattccagtagcagtgacaatattgctttgcttgtacagtaagtgacaacagatgtttcatctcgttgactttcaggttactatagcagagatattactaattattatgaggacttttaaagtttccatttggaatcttgattacatcataaacctcataattaaaaatttatctaagtcactaactgagaataattattctcaattagatgaagagcaaccaatggagattgattaaacgaacatgaaaattattcttttcttggcactgataacactcgctacttgtgagctttatcactaccaagagtgtgttagaggtacaacagtacttttaaaagaaccttgctcttctggaacatacgagggcaattcaccatttcatcctctagctgataacaaatttgcactgacttgctttagcactcaatttgcttttgcttgtcctgacggcgtaaaacacgtctatcagttacgtgccagatcagcttcacctaaactgttcatcagacaagaggaagttcaagaactttactctccaatttttcttattgttgcggcaatagtgtttataacactttgcttcacactcaaaagaaagatagaatgattgaactttcattaattgacttctatttgtgctttttagcctttctgctattccttgttttaattatgcttattatcttttggttctcacttgaactgcaagatcataatgaaacttgtcacgcctaaacgaacatgaaatttcttgttttcttaggaatcatcacaactgtagctgcatttcaccaagaatgtagtttacagtcatgtactcaacatcaaccatatgtagttgatgacccgtgtcctattcacttctattctaaatggtatattagagtaggagctagaaaatcagcacctttaattgaattgtgcgtggatgaggctggttctaaatcacccattcagtacatcgatatcggtaattatacagtttcctgtttaccttttacaattaattgccaggaacctaaattgggtagtcttgtagtgcgttgttcgttctatgaagactttttagagtatcatgacgttcgtgttgttttaatctaaacgaacaaactaaatgtctgataatggaccccaaaatcagcgaaatgcaccccgcattacgtttggtggaccctcagattcaactggcagtaaccagaatggagaacgcagtggggcgcgatcaaaacaacgtcggccccaaggtttacccaataatactgcgtcttggttcaccgctctcactcaacatggcaaggaaggccttaaattccctcgaggacaaggcgttccaattaacaccaatagcagtccagatgaccaaattggctactaccgaagagctaccagacgaattcgtggtggtgacggtaaaatgaaagatctcagtccaagatggtatttctactacctaggaactgggccagaagctggacttccctatggtgctaacaaagacggcatcatatgggttgcaactgagggagccttgaatacaccaaaagatcacattggcacccgcaatcctgctaacaatgctgcaatcgtgctacaacttcctcaaggaacaacattgccaaaaggcttctacgcagaagggagcagaggcggcagtcaagcctcttctcgttcctcatcacgtagtcgcaacagttcaagaaattcaactccaggcagcagtatgggaacttctcctgctagaatggctggcaatggcggtgatgctgctcttgctttgctgctgcttgacagattgaaccagcttgagagcaaaatgtctggtaaaggccaacaacaacaaggccaaactgtcactaagaaatctgctgctgaggcttctaagaagcctcggcaaaaacgtactgccactaaagcatacaatgtaacacaagctttcggcagacgtggtccagaacaaacccaaggaaattttggggaccaggaactaatcagacaaggaactgattacaaacattggccgcaaattgcacaatttgcccccagcgcttcagcgttcttcggaatgtcgcgcattggcatggaagtcacaccttcgggaacgtggttgacctacacaggtgccatcaaattggatgacaaagatccaaatttcaaagatcaagtcattttgctgaataagcatattgacgcatacaaaacattcccaccaacagagcctaaaaaggacaaaaagaagaaggcttatgaaactcaagccttaccgcagagacagaagaaacagcaaactgtgactcttcttcctgctgcagatttggatgatttctccaaacaattgcaacaatccatgagcagtgctgactcaactcaggcctaaactcatgcagaccacacaaggcagatgggctatataaacgttttcgcttttccgtttacgatatatagtctactcttgtgcagaatgaattctcgtaactacatagcacaagtagatgtagttaactttaatctcacatagcaatctttaatcagtgtgtaacattagggaggacttgaaagagccaccacattttcaccgaggccactcggagtacgatcgagtgtacagtgaacaatgctagggagagctgcctatatggaagagccctaatgtgtaaaattaattttagtagtgctatccccatgtgatttnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn +>ATM-2FFMD73N3 +NNNNNNNNNNNNNNNNNNNNNNNNNAACAAACCAACCAACTTTCGATCTCTTGTAGATCT +GTTCTCTAAACGAACTTTAAAATCTGTGTGGCTGTCACTCGGCTGCATGCTTAGTGCACT +CACGCAGTATAATTAATAACTAATTACTGTCGTTGACAGGACACGAGTAACTCGTCTATC +TTCTGCAGGCTGTTTACGGTTTCGTCCGTTTTGCAGCCGATCATCAGCACATCTAGGTTT +TGTCCGGGTGTGACCGAAAGGTAAGATGGAGAGCCTTGTCCCTGGTTTCAACGAGAAAAC +ACACGTCCAACTCAGTTTGCCTGTTTTACAGGTTCGCGACGTGCTCGTACGTGGCTTTGG +AGACTCCGTGGAGGAGGTCTTATCAGAGGCACGTCAACATCTTAAAGATGGCACTTGTGG +CTTAGTAGAAGTTGAAAAAGGCGTTTTGCCTCAACTTGAACAGCCCTATGTGTTCATCAA +ACGTTCGGATGCTCGAACTGCACCTCATGGTCATGTTATGTTTGAGCTGGTAGCAGAACT +CGAAGGCATTCAGTACGGTCGTAGTGGTGAGACACTTGGTGTCCTTGTCCCTCATGTGGG +CGAAATACCAGTGGCTTACCGCAAGGTTCTTCTTCGTAAGAACGGTAATAAAGGAGCTGG +TGGCCATAGTTACGGCGCCGATCTAAAGTCATTTGACTTAGGCGACGAGCTTGGCACTGA +TCCTTATGAAGATTTTCAAGAAAACTGGAACACTAAACATAGCAGTGGTGTTACCCGTGA +ACTCATGCGTGAGCTTAACGGAGGGGCATACACTCGCTATGTCGATAACAACTTCTGTGG +CCCTGATGGCTACCCTCTTGAGTGCATTAAAGACCTTCTAGCACGTGCTGGTAAAGCTTC +ATGCACTTTGTCCGAACAACTGGACTTTATTGACACTAAGAGGGGTGTATACTGCTGCCG +TGAACATGAGCATGAAATTGCTTGGTACACGGAACGTTCTGAAAAGAGCTATGAATTGCA +GACACCTTTTGAAATTAAATTGGCAAAGAAATTTGACACCTTCAATGGGGAATGCCCAAA +TTTTGTATTTCCCTTAAATTCCATAATCAAGACTATTCAACCAAGGGTTGAAAAGAAAAA +GCTTGATGGCTTTATGGGTAGAATTCGATCTGTCTATCCAGTTGCGTCACTAAATGAATG +CAACCAAATGTGCCTTTCAACTCTCATGAAGTGTGATCATTGTGGTGAAACTTCATGGCA +GACGGGTGATTTTGTTAAAGCCACTTGCGAATTTTGTGGCACTGAGAATTTGACTAAAGA +AGGTGCCACTACTTGTGGTTACTTACCCCAAAATGCTGTTGTTAAAATTTATTGTCCAGC +ATGTCACAATTCAGAAGTAGGACCTGAGCATAGTCTTGCCGAATACCATAATGAATCTGG +CTTGAAAACCATTCTTCGTAAGGGTGGTCGCACTATTGCCTTTGGAGGCTGTGTGTTCTC +TTATGTTGGTTGCCATAACAAGTGTGCCTATTGGGTTCCACGTGCTAGCGCTAACATAGG +TTGTAACCATACAGGTGTTGTTGGAGAAGGTTCCGAAGGTCTTAATGACAACCTTCTTGA +AATACTCCAAAAAGAGAAAGTCAACATCAATATTGTTGGTGACTTTAAACTTAATGAAGA +GATCGCCATTATTTTGGCATCTTTTTCTGCTTCCACAAGTGCTTTTGTGGAAACTGTGAA +AGGTTTGGATTATAAAGCATTCAAACAAATTGTTGAATCCTGTGGTAATTTTAAAGTTAC +AAAAGGAAAAGCTAAAAAAGGTGCCTGGAATATTGGTGAACAGAAATCAATACTGAGTCC +TCTTTATGCATTTGCATCAGAGGCTGCTCGTGTTGTACGATCAATTTTCTCCCGCACTCT +TGAAACTGCTCAAAATTCTGTGCGTGTTTTACAGAAGGCCGCTATAACAATACTAGATGG +AATTTCACAGTATTCACTGAGACTCATTGATGCTATGATGTTCACATCTGATTTGGCTAC +TAACAATCTAGTTGTAATGGCCTACATTACAGGTGGTGTTGTTCAGTTGACTTCGCAGTG +GCTAACTAACATCTTTGGCACTGTTTATGAAAAACTCAAACCCGTCCTTGATTGGCTTGA +AGAGAAGTTTAAGGAAGGTGTAGAGTTTCTTAGAGACGGTTGGGAAATTGTTAAATTTAT +CTCAACCTGTGCTTGTGAAATTGTCGGTGGACAAATTGTCACCTGTGCAAAGGAAATTAA +GGAGAGTGTTCAGACATTCTTTAAGCTTGTAAATAAATTTTTGGCTTTGTGTGCTGACTC +TATCATTATTGGTGGAGCTAAACTTAAAGCCTTGAATTTAGGTGAAACATTTGTCACGCA +CTCAAAGGGATTGTACAGAAAGTGTGTTAAATCCAGAGAAGAAACTGGCCTACTCATGCC +TCTAAAAGCCCCAAAAGAAATTATCTTCTTAGAGGGAGAAACACTTCCCACAGAAGTGTT +AACAGAGGAAGTTGTCTTGAAAACTGGTGATTTACAACCATTAGAACAACCTACTAGTGA +AGCTGTTGAAGCTCCATTGGTTGGTACACCAGTTTGTATTAACGGGCTTATGTTGCTCGA +AATCAAAGACACAGAAAAGTACTGTGCCCTTGCACCTAATATGATGGTAACAAACAATAC +CTTCACACTCAAAGGCGGTGCACCAACAAAGGTTACTTTTGGTGATGACACTGTGATAGA +AGTGCAAGGTTACAAGAGTGTGAATATCACTTTTGAACTTGATGAAAGGATTGATAAAGT +ACTTAATGAGAAGTGCTCTGCCTATACAGTTGAACTCGGTACAGAAGTAAATGAGTTCGC +CTGTGTTGTGGCAGATGCTGTCATAAAAACTTTGCAACCAGTATCTGAATTACTTACACC +ACTGGGCATTGATTTAGATGAGTGGAGTATGGCTACATACTACTTATTTGATGAGTCTGG +TGAGTTTAAATTGGCTTCACATATGTATTGTTCTTTTTACCCTCCAGATGAGGATGAAGA +AGAAGGTGATTGTGAAGAAGAAGAGTTTGAGCCATCAACTCAATATGAGTATGGTACTGA +AGATGATTACCAAGGTAAACCTTTGGAATTTGGTGCCACTTCTGCTGCTCTTCAACCTGA +AGAAGAGCAAGAAGAAGATTGGTTAGATGATGATAGTCAACAAACTGTTGGTCAACAAGA +CGGCAGTGAGGACAATCAGACAACTACTATTCAAACAATTGTTGAGGTTCAACCTCAATT +AGAGATGGAACTTACACCAGTTGTTCAGACTATTGAAGTGAATAGTTTTAGTGGTTATTT +AAAACTTACTGACAATGTATACATTAAAAATGCAGACATTGTGGAAGAAGCTAAAAAGGT +AAAACCAACAGTGGTTGTTAATGCAGCCAATGTTTACCTTAAACATGGAGGAGGTGTTGC +AGGAGCCTTAAATAAGGCTACTAACAATGCCATGCAAGTTGAATCTGATGATTACATAGC +TACTAATGGACCACTTAAAGTGGGTGGTAGTTGTGTTTTAAGCGGACACAATCTTGCTAA +ACACTGTCTTCATGTTGTCGGCCCAAATGTTAACAAAGGTGAAGACATTCAACTTCTTAA +GAGTGCTTATGAAAATTTTAATCAGCACGAAGTTCTACTTGCACCATTATTATCAGCTGG +TATTTTTGGTGCTGACCCTATACATTCTTTAAGAGTTTGTGTAGATACTGTTCGCACAAA +TGTCTACTTAGCTGTCTTTGATAAAAATCTCTATGACAAACTTGTTTCAAGCTTTTTGGA +AATGAAGAGTGAAAAGCAAGTTGAACAAAAGATCGCTGAGATTCCTAAAGAGGAAGTTAA +GCCATTTATAACTGAAAGTAAACCTTCAGTTGAACAGAGAAAACAAGATGATAAGAAAAT +CAAAGCTTGTGTTGAAGAAGTTACAACAACTCTGGAAGAAACTAAGTTCCTCACAGAAAA +CTTGTTACTTTATATTGACATTAATGGCAATCTTCATCCAGATTCTGCCACTCTTGTTAG +TGATATTGACATCACTTTCTTAAAGAAAGATGCTCCATATATAGTGGGTGATGTTGTTCA +AGAGGGTGTTTTAACTGCTGTGGTTATACCTACTAAAAAGGCTGGTGGCACTACTGAAAT +GCTAGCGAAAGCTTTGAGAAAAGTGCCAACAGACAATTATATAACCACTTACCCGGGTCA +GGGTTTAAATGGTTACACTGTAGAGGAGGCAAAGACAGTGCTTAAAAAGTGTAAAAGTGC +CTTTTACATTCTACCATCTATTATCTCTAATGAGAAGCAAGAAATTCTTGGAACTGTTTC +TTGGAATTTGCGAGAAATGCTTGCACATGCAGAAGAAACACGCAAATTAATGCCTGTCTG +TGTGGAAACTAAAGCCATAGTTTCAACTATACAGCGTAAATATAAGGGTATTAAAATACA +AGAGGGTGTGGTTGATTATGGTGCTAGATTTTACTTTTACACCAGTAAAACAACTGTAGC +GTCACTTATCAACACACTTAACGATCTAAATGAAACTCTTGTTACAATGCCACTTGGCTA +TGTAACACATGGCTTAAATTTGGAAGAAGCTGCTCGGTATATGAGATCTCTCAAAGTGCC +AGCTACAGTTTCTGTTTCTTCACCTGATGCTGTTACAGCGTATAATGGTTATCTTACTTC +TTCTTCTAAAACACCTGAAGAACATTTTATTGAAACCATCTCACTTGCTGGTTCCTATAA +AGATTGGTCCTATTCTGGACAATCTACACAACTAGGTATAGAATTTCTTAAGAGAGGTGA +TAAAAGTGTATATTACACTAGTAATCCTACCACATTCCACCTAGATGGTGAAGTTATCAC +CTTTGACAATCTTAAGACACTTCTTTCTTTGAGAGAAGTGAGGACTATTAAGGTGTTTAC +AACAGTAGACAACATTAACCTCCACACGCAAGTTGTGGACATGTCAATGACATATGGACA +ACAGTTTGGTCCAACTTATTTGGATGGAGCTGATGTTACTAAAATAAAACCTCATAATTC +ACATGAAGGTAAAACATTTTATGTTTTACCTAATGATGACACTCTACGTGTTGAGGCTTT +TGGGTACTACCACACAACTGATCTTAGTTTTCTGGGTAGGTACATGTCAGCATTAAATCA +CACTAAAAAGTGGAAATACCCACAAGTTAATGGTTTAACTTCTATTAAATGGGCAGATAA +CAACTGTTATCTTGCCACTGCATTGTTAACACTCCAACAAATAGAGTTGAAGTTTAATCC +ACCTGCTCTACAAGATGCTTATTACAGAGCAAGGGCTGGTGAAGCTGCTAACTTTTGTGC +ACTTATCTTAGCCTACTGTAATAAGACAGTAGGTGAGTTAGGTGATGTTAGAGAAACAAT +GAGTTACTTGTTTCAACATGCCAATTTAGATTCTTGCAAAAGAGTCTTGAACGTGGTGTG +TAAAACTTGTGGACAACAGCAGACAACCCTTAAGGGTGTAGAAGCTGTTATGTACATGGG +CACACTTTCTTATGAACAATTTAAGAAAGGTGTTCAGATACCTTGTACGTGTGGTAAACA +AGCTACAAAATATCTAGTACAACAGGAGTCACCTTTTGTTATGATGTCAGCACCACCTGC +TCAGTATGAACTTAAGCATGGTACATTTACTTGTGCTAGTGAGTACACTGGTAATTACCA +GTGTGGTCACTATAAACATATAACTTCTAAAGAAACTTTGTATTGCATAGACGGTGCTTT +ACTTACAAAGTCCTCAGAATACAAAGGTCCTATTACGGATGTTTTCTACAAAGAAAACAG +TTACATAACAACCATAAAACCAGTTACTTATAAATTGGATGGTGTTGTTTGTACAGAAAT +TGACCCTAAGTTGGACAATTATTATAAGAAAGACAATTCTTATTTCACAGAGCAACCAAT +TGATCTTGTACCAAACCAACCATATCCAAACGCAAGCTTCGATAATTTTAAGTTTGTATG +TGATAATATCAAATTTGCTGATGATTTAAACCAGTTAACTGGTTATAAGAAACCTGCTTC +AAGAGAGCTTAAAGTTACATTTTTCCCTGACTTAAATGGTGATGTGGTGGCTATTGATTA +TAAACACTACACACCCTCTTTTAAGAAAGGAGCTAAATTGTTACATAAACCTATTGTTTG +GCATGTTAACAATGCAACTAATAAAGCCACGTATAAACCAAATACCTGGTGTATACGTTG +TCTTTGGAGCACAAAACCAGTTGAAACATCAAATTCGTTTGATGTACTGAAGTCAGAGGA +CGCGCAGGGAATGGATAATCTTGCCTGCGAAGATCTAAAACCAGTCTCTGAAGAAGTAGT +GGAAAATCCTACCATACAGAAAGACGTTCTTGAGTGTAATGTGAAAACTACCGAAGTTGT +AGGAGACATTATACTTAAACCAGCAAATAATAGTTTAAAAATTACAGAAGAGGTTGGCCA +CACAGATCTAATGGCTGCTTATGTAGACAATTCTAGTCTTACTATTAAGAAACCTAATGA +ATTATCTAGAGTATTAGGTTTGAAAACCCTTGCTACTCATGGTTTAGCTGCTGTTAATAG +TGTCCCTTGGGATACTATAGCTAATTATGCTAAGCCTTTTCTTAACAAAGTTGTTAGTAC +AACTACTAACATAGTTACACGGTGTTTAAACCGTGTTTGTACTAATTATATGCCTTATTT +CTTTACTTTATTGCTACAATTGTGTACTTTTACTAGAAGTACAAATTCTAGAATTAAAGC +ATCTATGCCGACTACTATAGCAAAGAATACTGTTAAGAGTGTCGGTAAATTTTGTCTAGA +GGCTTCATTTAATTATTTGAAGTCACCTAATTTTTCTAAACTGATAAATATTATAATTTG +GTTTTTACTATTAAGTGTTTGCCTAGGTTCTTTAATCTACTCAACCGCTGCTTTAGGTGT +TTTAATGTCTAATTTAGGCATGCCTTCTTACTGTACTGGTTACAGAGAAGGCTATTTGAA +CTCTACTAATGTCACTATTGCAACCTACTGTACTGGTTCTATACCTTGTAGTGTTTGTCT +TAGTGGTTTAGATTCTTTAGACACCTATCCTTCTTTAGAAACTATACAAATTACCATTTC +ATCTTTTAAATGGGATTTAACTGCTTTTGGCTTAGTTGCAGAGTGGTTTTTGGCATATAT +TCTTTTCACTAGGTTTTTCTATGTACTTGGATTGGCTGCAATCATGCAATTGTTTTTCAG +CTATTTTGCAGTACATTTTATTAGTAATTCTTGGCTTATGTGGTTAATAATTAATCTTGT +ACAAATGGCCCCGATTTCAGCTATGGTTAGAATGTACATCTTCTTTGCATCATTTTATTA +TGTATGGAAAAGTTATGTGCATGTTGTAGACGGTTGTAATTCATCAACTTGTATGATGTG +TTACAAACGTAATAGAGCAACAAGAGTCGAATGTACAACTATTGTTAATGGTGTTAGAAG +GTCCTTTTATGTCTATGCTAATGGAGGTAAAGGCTTTTGCAAACTACACAATTGGAATTG +TGTTAATTGTGATACATTCTGTGCTGGTAGTACATTTATTAGTGATGAAGTTGCGAGAGA +CTTGTCACTACAGTTTAAAAGACCAATAAATCCTACTGACCAGTCTTCTTACATCGTTGA +TAGTGTTACAGTGAAGAATGGTTCCATCCATCTTTACTTTGATAAAGCTGGTCAAAAGAC +TTATGAAAGACATTCTCTCTCTCATTTTGTTAACTTAGACAACCTGAGAGCTAATAACAC +TAAAGGTTCATTGCCTATTAATGTTATAGTTTTTGATGGTAAATCAAAATGTGAAGAATC +ATCTGCAAAATCAGCGTCTGTTTACTACAGTCAGCTTATGTGTCAACCTATACTGTTACT +AGATCAGGCATTAGTGTCTGATGTTGGTGATAGTGCGGAAGTTGCAGTTAAAATGTTTGA +TGCTTACGTTAATACGTTTTCATCAACTTTTAACGTACCAATGGAAAAACTCAAAACACT +AGTTGCAACTGCAGAAGCTGAACTTGCAAAGAATGTGTCCTTAGACAATGTCTTATCTAC +TTTTATTTCAGCAGCTCGGCAAGGGTTTGTTGATTCAGATGTAGAAACTAAAGATGTTGT +TGAATGTCTTAAATTGTCACATCAATCTGACATAGAAGTTACTGGCGATAGTTGTAATAA +CTATATGCTCACCTATAACAAAGTTGAAAACATGACACCCCGTGACCTTGGTGCTTGTAT +TGACTGTAGTGCGCGTCATATTAATGCGCAGGTAGCAAAAAGTCACAACATTGCTTTGAT +ATGGAACGTTAAAGATTTCATGTCATTGTCTGAACAACTACGAAAACAAATACGTAGTGC +TGCTAAAAAGAATAACTTACCTTTTAAGTTGACATGTGCAACTACTAGACAAGTTGTTAA +TGTTGTAACAACAAAGATAGCACTTAAGGGTGGTAAAATTGTTAATAATTGGTTGAAGCA +GTTAATTAAAGTTACACTTGTGTTCCTTTTTGTTGCTGCTATTTTCTATTTAATAACACC +TGTTCATGTCATGTCTAAACATACTGACTTTTCAAGTGAAATCATAGGATACAAGGCTAT +TGATGGTGGTGTCACTCGTGACATAGCATCTACAGATACTTGTTTTGCTAACAAACATGC +TGATTTTGACACATGGTTTAGCCAGCGTGGTGGTAGTTATACTAATGACAAAGCTTGCCC +ATTGATTGCTGCAGTCATAACAAGAGAAGTGGGTTTTGTCGTGCCTGGTTTGCCTGGCAC +GATATTACGCACAACTAATGGTGACTTTTTGCATTTCTTACCTAGAGTTTTTAGTGCAGT +TGGTAACATCTGTTACACACCATCAAAACTTATAGAGTACACTGACTTTGCAACATCAGC +TTGTGTTTTGGCTGCTGAATGTACAATTTTTAAAGATGCTTCTGGTAAGCCAGTACCATA +TTGTTATGATACCAATGTACTAGAAGGTTCTGTTGCTTATGAAAGTTTACGCCCTGACAC +ACGTTATGTGCTCATGGATGGCTCTATTATTCAATTTCCTAACACCTACCTTGAAGGTTC +TGTTAGAGTGGTAACAACTTTTGATTCTGAGTACTGTAGGCACGGCACTTGTGAAAGATC +AGAAGCTGGTGTTTGTGTATCTACTAGTGGTAGATGGGTACTTAACAATGATTATTACAG +ATCTTTACCAGGAGTTTTCTGTGGTGTAGATGCTGTAAATTTACTTACTAATATGTTTAC +ACCACTAATTCAACCTATTGGTGCTTTGGACATATCAGCATCTATAGTAGCTGGTGGTAT +TGTAGCTATCGTAGTAACATGCCTTGCCTACTATTTTATGAGGTTTAGAAGAGCTTTTGG +TGAATACAGTCATGTAGTTGCCTTTAATACTTTACTATTCCTTATGTCATTCACTGTACT +CTGTTTAACACCAGTTTACTCATTCTTACCTGGTGTTTATTCTGTTATTTACTTGTACTT +GACATTTTATCTTACTAATGATGTTTCTTTTTTAGCACATATTCAGTGGATGGTTATGTT +CACACCTTTAGTACCTTTCTGGATAACAATTGCTTATATCATTTGTATTTCCACAAAGCA +TTTCTATTGGTTCTTTAGTAATTACCTAAAGAGACGTGTAGTCTTTAATGGTGTTTCCTT +TAGTACTTTTGAAGAAGCTGCGCTGTGCACCTTTTTGTTAAATAAAGAAATGTATCTAAA +GTTGCGTAGTGATGTGCTATTACCTCTTACGCAATATAATAGATACTTAGTTCTTTATAA +TAAGTACAAGTATTTTAGTGGAGCAATGGATACAACTAGCTACAGAGAAGCTGCTTGTTG +TCATCTCGCAAAGGCTCTCAATGACTTCAGTAACTCAGGTTCTGATGTTCTTTACCAACC +ACCACAAACCTCTATCACCTCAGCTGTTTTGCAGAGTGGTTTTAGAAAAATGGCATTCCC +ATCTGGTAAAGTTGAGGGTTGTATGGTACAAGTAACTTGTGGTACAACTACACTTAACGG +TCTTTGGCTTGATGACGTAGTTTACTGTCCAAGACATGTGATCTGCACCTCTGAAGACAT +GCTTAACCCTAATTATGAAGATTTACTCATTCGTAAGTCTAATCATAATTTCTTGGTACA +GGCTGGTAATGTTCAACTCAGGGTTATTGGACATTCTATGCAAAATTGTGTACTTAAGCT +TAAGGTTGATACAGCCAATCCTAAGACACCTAAGTATAAGTTTGTTCGCATTCAACCAGG +ACAGACTTTTTCAGTGTTAGCTTGTTACAATGGTTCACCATCTGGTGTTTACCAATGTGC +TATGAGGCCCAATTTCACTATTAAGGGTTCATTCCTTAATGGTTCATGTGGTAGTGTTGG +TTTTAACATAGATTATGACTGTGTCTCTTTTTGTTACATGCACCATATGGAATTACCAAC +TGGAGTTCATGCTGGCACAGACTTAGAAGGTAACTTTTATGGACCTTTTGTTGACAGGCA +AACAGCACAAGCAGCTGGTACGGACACAACTATTACAGTTAATGTTTTAGCTTGGTTGTA +CGCTGCTGTTATAAATGGAGACAGGTGGTTTCTCAATCGATTTACCACAACTCTTAATGA +CTTTAACCTTGTGGCTATGAAGTACAATTATGAACCTCTAACACAAGACCATGTTGACAT +ACTAGGACCTCTTTCTGCTCAAACTGGAATTGCCGTTTTAGATATGTGTGCTTCATTAAA +AGAATTACTGCAAAATGGTATGAATGGACGTACCATATTGGGTAGTGCTTTATTAGAAGA +TGAATTTACACCTTTTGATGTTGTTAGACAATGCTCAGGTGTTACTTTCCAAAGTGCAGT +GAAAAGAACAATCAAGGGTACACACCACTGGTTGTTACTCACAATTTTGACTTCACTTTT +AGTTTTAGTCCAGAGTACTCAATGGTCTTTGTTCTTTTTTTTGTATGAAAATGCCTTTTT +ACCTTTTGCTATGGGTATTATTGCTATGTCTGCTTTTGCAATGATGTTTGTCAAACATAA +GCATGCATTTCTCTGTTTGTTTTTGTTACCTTCTCTTGCCACTGTAGCTTATTTTAATAT +GGTCTATATGCCTGCTAGTTGGGTGATGCGTATTATGACATGGTTGGATATGGTTGATAC +TAGTTTGTCTGGTTTTAAGCTAAAAGACTGTGTTATGTATGCATCAGCTGTAGTGTTACT +AATCCTTATGACAGCAAGAACTGTGTATGATGATGGTGCTAGGAGAGTGTGGACACTTAT +GAATGTCTTGACACTCGCTTATAAAGTTTATTATGGTAATGCTTTAGATCAAGCCATTTC +CATGTGGGCTCTTATAATCTCTGTTACTTCTAACTACTCAGGTGTAGTTACAACTGTCAT +GTTTTTGGCCAGAGGTATTGTTTTTATGTGTGTTGAGTATTGCCCTATTTTCTTCATAAC +TGGTAATACACTTCAGTGTATAATGCTAGTTTATTGTTTCTTAGGCTATTTTTGTACTTG +TTACTTTGGCCTCTTTTGTTTACTCAACCGCTACTTTAGACTGACTCTTGGTGTTTATGA +TTACTTAGTTTCTACACAGGAGTTTAGATATATGAATTCACAGGGACTACTCCCACCCAA +GAATAGCATAGATGCCTTCAAACTCAACATTAAATTGTTGGGTGTTGGTGGCAAACCTTG +TATCAAAGTAGCCACTGTACAGTCTAAAATGTCAGATGTAAAGTGCACATCAGTAGTCTT +ACTCTCAGTTTTGCAACAACTCAGAGTAGAATCATCATCTAAATTGTGGGCTCAATGTGT +CCAGTTACACAATGACATTCTCTTAGCTAAAGATACTACTGAAGCCTTTGAAAAAATGGT +TTCACTACTTTCTGTTTTGCTTTCCATGCAGGGTGCTGTAGACATAAACAAGCTTTGTGA +AGAAATGCTGGACAACAGGGCAACCTTACAAGCTATAGCCTCAGAGTTTAGTTCCCTTCC +ATCATATGCAGCTTTTGCTACTGCTCAAGAAGCTTATGAGCAGGCTGTTGCTAATGGTGA +TTCTGAAGTTGTTCTTAAAAAGTTGAAGAAGTCTTTGAATGTGGCTAAATCTGAATTTGA +CCGTGATGCAGCCATGCAACGTAAGTTGGAAAAGATGGCTGATCAAGCTATGACCCAAAT +GTATAAACAGGCTAGATCTGAGGACAAGAGGGCAAAAGTTACTAGTGCTATGCAGACAAT +GCTTTTCACTATGCTTAGAAAGTTGGATAATGATGCACTCAACAACATTATCAACAATGC +AAGAGATGGTTGTGTTCCCTTGAACATAATACCTCTTACAACAGCAGCCAAACTAATGGT +TGTCATACCAGACTATAACACATATAAAAATACGTGTGATGGTACAACATTTACTTATGC +ATCAGCATTGTGGGAAATCCAACAGGTTGTAGATGCAGATAGTAAAATTGTTCAACTTAG +TGAAATTAGTATGGACAATTCACCTAATTTAGCATGGCCTCTTATTGTAACAGCTTTAAG +GGCCAATTCTGCTGTCAAATTACAGAATAATGAGCTTAGTCCTGTTGCACTACGACAGAT +GTCTTGTGCTGCCGGTACTACACAAACTGCTTGCACTGATGACAATGCGTTAGCTTACTA +CAACACAACAAAGGGAGGTAGGTTTGTACTTGCACTGTTATCCGATTTACAGGATTTGAA +ATGGGCTAGATTCCCTAAGAGTGATGGAACTGGTACTATCTATACAGAACTGGAACCACC +TTGTAGGTTTGTTACAGACACACCTAAAGGTCCTAAAGTGAAGTACTTATACTTTATTAA +AGGATTAAACAACCTAAATAGAGGTATGGTACTTGGTAGTTTAGCTGCCACAGTACGTCT +ACAAGCTGGTAATGCAACAGAAGTGCCTGCCAATTCAACTGTATTATCTTTCTGTGCTTT +TGCTGTAGATGCTGCTAAAGCTTACAAAGATTATCTAGCTAGTGGGGGACAACCAATCAC +TAATTGTGTTAAGATGTTGTGTACACACACTGGTACTGGTCAGGCAATAACAGTTACACC +GGAAGCCAATATGGATCAAGAATCCTTTGGTGGTGCATCGTGTTGTCTGTACTGCCGTTG +CCACATAGATCATCCAAATCCTAAAGGATTTTGTGACTTAAAAGGTAAGTATGTACAAAT +ACCTACAACTTGTGCTAATGACCCTGTGGGTTTTACACTTAAAAACACAGTCTGTACCGT +CTGCGGTATGTGGAAAGGTTATGGCTGTAGTTGTGATCAACTCCGCGAACCCATGCTTCA +GTCAGCTGATGCACAATCGTTTTTAAACGGGTTTGCGGTGTAAGTGCAGCCCGTCTTACA +CCGTGCGGCACAGGCACTAGTACTGATGTCGTATACAGGGCTTTTGACATCTACAATGAT +AAAGTAGCTGGTTTTGCTAAATTCCTAAAAACTAATTGTTGTCGCTTCCAAGAAAAGGAC +GAAGATGACAATTTAATTGATTCTTACTTTGTAGTTAAGAGACACACTTTCTCTAACTAC +CAACATGAAGAAACAATTTATAATTTACTTAAGGATTGTCCAGCTGTTGCTAAACATGAC +TTCTTTAAGTTTAGAATAGACGGTGACATGGTACCACATATATCACGTCAACGTCTTACT +AAATACACAATGGCAGACCTCGTCTATGCTTTAAGGCATTTTGATGAAGGTAATTGTGAC +ACATTAAAAGAAATACTTGTCACATACAATTGTTGTGATGATGATTATTTCAATAAAAAG +GACTGGTATGATTTTGTAGAAAACCCAGATATATTACGCGTATACGCCAACTTAGGTGAA +CGTGTACGCCAAGCTTTGTTAAAAACAGTACAATTCTGTGATGCCATGCGAAATGCTGGT +ATTGTTGGTGTACTGACATTAGATAATCAAGATCTCAATGGTAACTGGTATGATTTCGGT +GATTTCATACAAACCACGCCAGGTAGTGGAGTTCCTGTTGTAGATTCTTATTATTCATTG +TTAATGCCTATATTAACCTTGACCAGAGCTTTAACTGCAGAGTCACATGTTGACACTGAC +TTAACAAAGCCTTACATTAAGTGGGATTTGTTAAAATATGACTTCACGGAAGAGAGGTTA +AAACTCTTTGACCGTTATTTTAAATATTGGGATCAGACATACCACCCAAATTGTGTTAAC +TGTTTGGATGACAGATGCATTCTGCATTGTGCAAACTTTAATGTTTTATTCTCTACAGTG +TTCCCACTTACAAGTTTTGGACCACTAGTGAGAAAAATATTTGTTGATGGTGTTCCATTT +GTAGTTTCAACTGGATACCACTTCAGAGAGCTAGGTGTTGTACATAATCAGGATGTAAAC +TTACATAGCTCTAGACTTAGTTTTAAGGAATTACTTGTGTATGCTGCTGACCCTGCTATG +CACGCTGCTTCTGGTAATCTATTACTAGATAAACGCACTACGTGCTTTTCAGTAGCTGCA +CTTACTAACAATGTTGCTTTTCAAACTGTCAAACCCGGTAATTTTAACAAAGACTTCTAT +GACTTTGCTGTGTCTAAGGGTTTCTTTAAGGAAGGAAGTTCTGTTGAATTAAAACACTTC +TTCTTTGCTCAGGATGGTAATGCTGCTATCAGCGATTATGACTACTATCGTTATAATCTA +CCAACAATGTGTGATATCAGACAACTACTATTTGTAGTTGAAGTTGTTGATAAGTACTTT +GATTGTTACGATGGTGGCTGTATTAATGCTAACCAAGTCATCGTCAACAACCTAGACAAA +TCAGCTGGTTTTCCATTTAATAAATGGGGTAAGGCTAGACTTTATTATGATTCAATGAGT +TATGAGGATCAAGATGCACTTTTCGCATATACAAAACGTAATGTCATCCCTACTATAACT +CAAATGAATCTTAAGTATGCCATTAGTGCAAAGAATAGAGCTCGCACCGTAGCTGGTGTC +TCTATCTGTAGTACTATGACCAATAGACAGTTTCATCAAAAATTATTGAAATCAATAGCC +GCCACTAGAGGAGCTACTGTAGTAATTGGAACAAGCAAATTCTATGGTGGTTGGCACAAT +ATGTTAAAAACTGTTTATAGTGATGTAGAAAACCCTCACCTTATGGGTTGGGATTATCCT +AAATGTGATAGAGCCATGCCTAACATGCTTAGAATTATGGCCTCACTTGTTCTTGCTCGC +AAACATACAACGTGTTGTAGCTTGTCACACCGTTTCTATAGATTAGCTAATGAGTGTGCT +CAAGTATTGAGTGAAATGGTCATGTGTGGCAGTTCACTATATGTTAAACCAGGTGGAACC +TCATCAGGAGATGCCACAACTGCTTATGCTAATAGTGTTTTTAACATTTGTCAAGCTGTC +ACGGCCAATGTTAATGCACTTTTATCTACTGATGGTAACAAAATTGCCGATAAGTATGTC +CGCAATTTACAACACAGACTTTATGAGTGTCTCTATAGAAATAGAGATGTTGACACAGAC +TTTGTGAATGAGTTTTACGCATATTTGCGTAAACATTTCTCAATGATGATACTCTCTGAC +GATGCTGTTGTGTGTTTCAATAGCACTTATGCATCTCAAGGTCTAGTGGCTAGCATAAAG +AACTTTAAGTCAGTTCTTTATTATCAAAACAATGTTTTTATGTCTGAAGCAAAATGTTGG +ACTGAGACTGACCTTACTAAAGGACCTCATGAATTTTGCTCTCAACATACAATGCTAGTT +AAACAGGGTGATGATTATGTGTACCTTCCTTACCCAGATCCATCAAGAATCCTAGGGGCC +GGCTGTTTTGTAGATGATATCGTAAAAACAGATGGTACACTTATGATTGAACGGTTCGTG +TCTTTAGCTATAGATGCTTACCCACTTACTAAACATCCTAATCAGGAGTATGCTGATGTC +TTTCATTTGTACTTACAATACATAAGAAAGCTACATGATGAGTTAACAGGACACATGTTA +GACATGTATTCTGTTATGCTTACTAATGATAACACTTCAAGGTATTGGGAACCTGAGTTT +TATGAGGCTATGTACACACCGCATACAGTCTTACAGGCTGTTGGGGCTTGTGTTCTTTGC +AATTCACAGACTTCATTAAGATGTGGTGCTTGCATACGTAGACCATTCTTATGTTGTAAA +TGCTGTTACGACCATGTCATATCAACATCACATAAATTAGTCTTGTCTGTTAATCCGTAT +GTTTGCAATGCTCCAGGTTGTGATGTCACAGATGTGACTCAACTTTACTTAGGAGGTATG +AGCTATTATTGTAAATCACATAAACTACCCATTAGTTTTCCATTGTGTGCTAATGGACAA +GTTTTTGGTTTATATAAAAATACATGTGTTGGTAGCGATAATGTTACTGACTTTAATGCA +ATTGCAACATGTGACTGGACAAATGCTGGTGATTACATTTTAGCTAACACCTGTACTGAA +AGACTCAAGCTTTTTGCAGCAGAAACGCTCAAAGCTACTGAGGAGACATTTAAACTGTCT +TATGGTATTGCTACTGTACGTGAAGTGCTGTCTGACAGAGAATTACATCTTTCATGGGAA +GTTGGTAAACCTAGACCACCACTTAACCGAAATTATGTCTTTACTGGTTATCGTGTAACT +AAAAACAGTAAAGTACAAATAGGAGAGTACACCTTTGAAAAAGGTGACTATGGTGATGCT +GTTGTTTACCGAGGTACAACAACTTACAAATTAAATGTTGGTGATTATTTTGTGCTGACA +TCACATACAGTAATGCCATTAAGTGCACCTACACTAGTGCCACAAGAGCACTATGTTAGA +ATTACTGGCTTATACCCAACACTCAATATCTCAGATGAGTTTTCTAGCAATGTTGCAAAT +TATCAAAAGGTTGGTATGCAAAAGTATTCTACACTCCAGGGACCACCTGGTACTGGTAAG +AGTCATTTTGCTATTGGCCTAACTCTCTACTACCCTTCTGCTCGCATAGTGTATACAGCT +TGCTCTCATGCCGCTGTTGATGCACTATGTGAGAAGGCATTAAAATATTTGCCTATAGAT +AAATGTAGTAGAATTATACCTGCACGTGCTCGTGTAGAGTGTTTTGATAAATTCAAAGTG +AATTCAACATTAGAACAGTATGTCTTTTGTACTGTAAATGCATTGCCTGAGACGACAGCA +GATATAGTTGTCTTTGATGAAATTTCAATGGCCACAAATTATGATTTGAGTGTTGTCAAT +GCCAGATTACGTGCTAAGCACTATGTGTACATTGGCGACCCTGCTCAATTACCTGCACCA +CGCACATTGCTAACTAAGGGCACACTAGAACCAGAATATTTCAATTCAGTGTGTAGACTT +ATGAAAACTATAGGTCCAGACATGTTCCTCGGAACTTGTCGGCGTTGTCCTGCTGAAATT +GTTGACACTGTGAGTGCTTTGGTTTATGATAATAAGCTTAAAGCACATAAAGACAAATCA +GCTCAATGCTTTAAAATGTTTTATAAGGGTGTTATCACGCATGATGTTTCATCTGCAATT +AACAGGCCACAAATAGGCGTGGTAAGAGAATTCCTTACACGTAACCCTGCTTGGAGAAAA +GCTGTCTTTATTTCACCTTATAATTCACAGAATGCTGTAGCCTCAAAGATTTTGGGACTA +CCAACTCAAACTGTTGATTCATCACAGGGCTCAGAATATGACTATGTCATATTCACTCAA +ACCACTGAAACAGCTCACTCTTGTAATGTAAACAGATTTAATGTTGCTATTACCAGAGCA +AAAGTAGGCATACTTTGCATAATGTCTGATAGAGACCTTTATGACAAGTTGCAATTTACA +AGTCTTGAAATTCCACGTAGGAATGTGGCAACTTTACAAGCTGAAAATGTAACAGGACTC +TTTAAAGATTGTAGTAAGGTAATCACTGGGTTACATCCTACACAGGCACCTACACACCTC +AGTGTTGACACTAAATTCAAAACTGAAGGTTTATGTGTTGACATACCTGGCATACTTAAG +GACATGACCTATAGAAGACTCATCTCTATGATGGGTTTTAAAATGAATTATCAAGTTAAT +GGTTACCCTAACATGTTTATCACCCGCGAAGAAGCTATAAGACATGTACGTGCATGGATT +GGCTTCGATGTCGAGGGGTGTCATGCTACTAGAGAAGCTGTTGGTACCAATTTACCTTTA +CAGCTAGGTTTTTCTACAGGTGTTAACCTAGTTGCTGTACCTACAGGTTATGTTGATACA +CCTAATAATACAGATTTTTCCAGAGTTAGTGCTAAACCACCGCCTGGAGATCAATTTAAA +CACCTCATACCACTTATGTACAAAGGACTTCCTTGGAATGTAGTGCGTATAAAGATTGTA +CAAATGTTAAGTGACACACTTAAAAATCTCTCTGACAGAGTCGTATTTGTCTTATGGGCA +CATGGCTTTGAGTTGACATCTATGAAGTATTTTGTGAAAATAGGACCTGAGCGCACCTGT +TGTCTATGTGATAGACGTGCCACATGCTTTTCCACTGCTTCAGACACTTATGCCTGTTGG +CATCATTCTATTGGATTTGATTACGTCTATAATCCGTTTATGATTGATGTTCAACAATGG +GGTTTTACAGGTAACCTACAAAGCAACCATGATCTGTATTGTCAAGTCCATGGTAATGCA +CATGTAGCTAGTTGTGATGCAATCATGACTAGGTGTCTAGCTGTCCACGAGTGCTTTGTT +AAGCGTGTTGACTGGACTATTGAATATCCTATAATTGGTGATGAACTGAAGATTAATGCG +GCTTGTAGAAAGGTTCAACACATGGTTGTTAAAGCTGCATTATTAGCAGACAAATTCCCA +GTTCTTCACGACATTGGTAACCCTAAAGCTATTAAGTGTGTACCTCTAGCTGATGTAGAA +TGGAAGTTCTATGATGCACAGCCTTGTAGTGACAAAGCTTATAAAATAGAAGAATTATTC +TATTCTTATGCCACACATTTTGACAAATTCACAGATGGTGTATGCCTATTTTGGAATTGC +AATGTCGATAGATATCCTGCTAATTCCATTGTTTGTAGATTTGACACTAGAGTGCTATCT +AACCTTAACTTGCCTGGTTGTGATGGTGGCAGTTTGTATGTAAATAAACATGCATTCCAC +ACACCAGCTTTTGATAAAAGTGCTTTTGTTAATTTAAAACAATTACCATTTTTCTATTAC +TCTGACAGTCCATGTGAGTCTCATGGAAAACAAGTAGTGTCAGATATAGATTATGTACCA +CTAAAGTCTGCTACGTGTATAACACGTTGCAATTTAGGTGGTGCTGTCTGTAGACATCAT +GCTAATGAGTACAGATTGTATCTCGATGCTTATAACATGATGATCTCAGCTGGCTTTAGC +TTGTGGGTTTACAAACAATTTGATACTTATAACCTCTGGAACACTTTTACAAGACTTCAG +AGTTTAGAAAATGTGGCTTTTAATGTTGTAAATAAGAGACACTTTGATGGACAACAGGGT +GAAGTACCAGTTTCTATCATTAATAACACTGTTTACACAAAAGTTGATGGTGTTGATGTA +GAATTGTTTGAAAATAAAACAACATTACCTGTTAATGTAGCATTTGAGCTTTGGGCTAAG +CGCAACATTAAACCAGTACCAGAGGTGAAAATACTCAATAATTTGGGTGTGGACATTGCT +GCTAATACTGTGATCTGGGACTACAAAAGAGATGCTCCAGCACATATATCTACTATTGGT +GTTTGTTCTATGACTGACATAGCCAAGAAACCAACTGAAACGATTTGTGCACCACTCACT +GTCTTTTTTGATGGTAGAGTTGATGGTCAAGTAGACTTATTTAGAAATGCCCGTAATGGT +GTTCTTATTACAGAAGGTAGTGTTAAAGGTTTACAACCATCTGTAGGTCCCAAACAAGCT +AGTCTTAATGGAGTCACATTAATTGGAGAAGCCGTAAAAACACAGTTCAATTATTATAAG +AAAGTTGATGGTGTTGTCCAACAATTACCTGAAACTTACTTTACTCAGAGTAGAAATTTA +CAAGAATTTAAACCCAGGAGTCAAATGGAAATTGATTTCTTGGAATTAGCTATGGATGAA +TTCATTGAACGGTATAAATTAGAAGGCTATGCCTTCGAACATATCGTTTATGGAGATTTT +AGTCATAGTCAGTTAGGTGGTTTACATCTACTGATTGGACTAGCTAAACGTTTTAAGGAA +TCACCTTTTGAATTAGAAGATTTTATTCCTATGGACAGTACAGTTAAAAACTATTTCATA +ACAGATGCGCAAACAGGTTCATCTAAGTGTGTGTGTTCTGTTATTGATTTATTACTTGAT +GATTTTGTTGAAATAATAAAATCCCAAGATTTATCTGTAGTTTCTAAGGTTGTCAAAGTG +ACTATTGACTATACAGAAATTTCATTTATGCTTTGGTGTAAAGATGGCCATGTAGAAACA +TTTTACCCAAAATTACAATCTAGTCAAGCGTGGCAACCGGGTGTTGCTATGCCTAATCTT +TACAAAATGCAAAGAATGCTATTAGAAAAGTGTGACCTTCAAAATTATGGTGATAGTGCA +ACATTACCTAAAGGCATAATGATGAATGTCGCAAAATATACTCAACTGTGTCAATATTTA +AACACATTAACATTAGCTGTACCCTATAATATGAGAGTTATACATTTTGGTGCTGGTTCT +GATAAAGGAGTTGCACCAGGTACAGCTGTTTTAAGACAGTGGTTGCCTACGGGTACGCTG +CTTGTCGATTCAGATCTTAATGACTTTGTCTCTGATGCAGATTCAACTTTGATTGGTGAT +TGTGCAACTGTACATACAGCTAATAAATGGGATCTCATTATTAGTGATATGTACGACCCT +AAGACTAAAAATGTTACAAAAGAAAATGACTCTAAAGAGGGTTTTTTCACTTACATTTGT +GGGTTTATACAACAAAAGCTAGCTCTTGGAGGTTCCGTGGCTATAAAGATAACAGAACAT +TCTTGGAATGCTGATCTTTATAAGCTCATGGGACACTTCGCATGGTGGACAGCCTTTGTT +ACTAATGTGAATGCGTCATCATCTGAAGCATTTTTAATTGGATGTAATTATCTTGGCAAA +CCACGCGAACAAATAGATGGTTATGTCATGCATGCAAATTACATATTTTGGAGGAATGCA +AATCCAATTCAGTTGTCTTCCTATTCTTTATTTGACATGAGTAAATTTCCCCTTAAATTA +AGGGGTACTGCTGTTATGTCTTTAAAAGAAGGTCAAATCAATGATATGATTTTATCTCTT +CTTAGTAAAGGTAGACTTATAATTAGAGAAAACAACAGAGTTGTTATTTCTAGTGATGTT +CTTGTTAACAACTAAACGAACAATGTTTGTTTTTCTTGTTTTATTGCCACTAGTCTCTAG +TCAGTGTGTTAATCTTAGAACCAGAACTCAATTACCCCCTGCATACACTAATTCTTTCAC +ACGTGGTGTTTATTACCCTGACAAAGTTTTCAGATCCTCAGTTTTACATTCAACTCAGGA +CTTGTTCTTACCTTTCTTTTCCAATGTTACTTGGTTCCATGCTATACATGTCTCTGGGAC +CAATGGTACTAAGAGGTTTGATAACCCTGTCCTACCATTTAATGATGGTGTTTATTTTGC +TTCCACTGAGAAGTCTAACATAATAAGAGGCTGGATTTTTGGTACTACTTTAGATTCGAA +GACCCAGTCCCTACTTATTGTTAATAACGCTACTAATGTTGTTATTAAAGTCTGTGAATT +TCAATTTTGTAATGATCCATTTTTGGATGTTTATTACCACAAAAACAACAAAAGTTGGAT +GGAAAGTGGAGTTTATTCTAGTGCGAATAATTGCACTTTTGAATATGTCTCTCAGCCTTT +TCTTATGGACCTTGAAGGAAAACAGGGTAATTTCAAAAATCTTAGGGAATTTGTGTTTAA +GAATATTGATGGTTATTTTAAAATATATTCTAAGCACACGCCTATTAATTTAGTGCGTGA +TCTCCCTCAGGGTTTTTCGGCTTTAGAACCATTGGTAGATTTGCCAATAGGTATTAACAT +CACTAGGTTTCAAACTTTACTTGCTTTACATAGAAGTTATTTGACTCCTGGTGATTCTTC +TTCAGGTTGGACAGCTGGTGCTGCAGCTTATTATGTGGGTTATCTTCAACCTAGGACTTT +TCTATTAAAATATAATGAAAATGGAACCATTACAGATGCTGTAGACTGTGCACTTGACCC +TCTCTCAGAAACAAAGTGTACGTTGAAATCCTTCACTGTAGAAAAAGGAATCTATCAAAC +TTCTAACTTTAGAGTCCAACCAACAGAATCTATTGTTAGATTTCCTAATATTACAAACTT +GTGCCCTTTTGGTGAAGTTTTTAACGCCACCAGATTTGCATCTGTTTATGCTTGGAACAG +GAAGAGAATCAGCAACTGTGTTGCTGATTATTCTGTCCTATATAATTCCGCATCATTTTC +CACTTTTAAGTGTTATGGAGTGTCTCCTACTAAATTAAATGATCTCTGCTTTACTAATGT +CTATGCAGATTCATTTGTAATTAGAGGTGATGAAGTCAGACAAATCGCTCCAGGGCAAAC +TGGAAAGATTGCTGATTATAATTATAAATTACCAGATGATTTTACAGGCTGCGTTATAGC +TTGGAATTCTAACAATCTTGATTCTAAGGTTGGTGGTAATTATAATTACCGGTATAGATT +GTTTAGGAAGTCTAATCTCAAACCTTTTGAGAGAGATATTTCAACTGAAATCTATCAGGC +CGGTAGCAAACCTTGTAATGGTGTTGAAGGTTTTAATTGTTACTTTCCTTTACAATCATA +TGGTTTCCAACCCACTAATGGTGTTGGTTACCAACCATACAGAGTAGTAGTACTTTCTTT +TGAACTTCTACATGCACCAGCAACTGTTTGTGGACCTAAAAAGTCTACTAATTTGGTTAA +AAACAAATGTGTCAATTTCAACTTCAATGGTTTAACAGGCACAGGTGTTCTTACTGAGTC +TAACAAAAAGTTTCTGCCTTTCCAACAATTTGGCAGAGACATTGCTGACACTACTGATGC +TGTCCGTGATCCACAGACACTTGAGATTCTTGACATTACACCATGTTCTTTTGGTGGTGT +CAGTGTTATAACACCAGGAACAAATACTTCTAACCAGGTTGCTGTTCTTTATCAGGGTGT +TAACTGCACAGAAGTCCCTGTTGCTATTCATGCAGATCAACTTACTCCTACTTGGCGTGT +TTATTCTACAGGTTCTAATGTTTTTCAAACACGTGCAGGCTGTTTAATAGGGGCTGAACA +TGTCAACAACTCATATGAGTGTGACATACCCATTGGTGCAGGTATATGCGCTAGTTATCA +GACTCAGACTAATTCTCGTCGGCGGGCACGTAGTGTAGCTAGTCAATCCATCATTGCCTA +CACTATGTCACTTGGTGCAGAAAATTCAGTTGCTTACTCTAATAACTCTATTGCCATACC +CACAAATTTTACTATTAGTGTTACCACAGAAATTCTACCAGTGTCTATGACCAAGACATC +AGTAGATTGTACAATGTACATTTGTGGTGATTCAACTGAATGCAGCAATCTTTTGTTGCA +ATATGGCAGTTTTTGTACACAATTAAACCGTGCTTTAACTGGAATAGCTGTTGAACAAGA +CAAAAACACCCAAGAAGTTTTTGCACAAGTCAAACAAATTTACAAAACACCACCAATTAA +AGATTTTGGTGGTTTTAATTTTTCACAAATATTACCAGATCCATCAAAACCAAGCAAGAG +GTCATTTATTGAAGATCTACTTTTCAACAAAGTGACACTTGCAGATGCTGGCTTCATCAA +ACAATATGGTGATTGCCTTGGTGATATTGCTGCTAGAGACCTCATTTGTGCACAAAAGTT +TAACGGCCTTACTGTTTTGCCACCTTTGCTCACAGATGAAATGATTGCTCAATACACTTC +TGCACTGTTAGCGGGTACAATCACTTCTGGTTGGACCTTTGGTGCAGGTGCTGCATTACA +AATACCATTTGCTATGCAAATGGCTTATAGGTTTAATGGTATTGGAGTTACACAGAATGT +TCTCTATGAGAACCAAAAATTGATTGCCAACCAATTTAATAGTGCTATTGGCAAAATTCA +AGACTCACTTTCTTCCACAGCAAGTGCACTTGGAAAACTTCAAAATGTGGTCAACCAAAA +TGCACAAGCTTTAAACACGCTTGTTAAACAACTTAGCTCCAATTTTGGTGCAATTTCAAG +TGTTTTAAATGATATCCTTTCACGTCTTGACAAAGTTGAGGCTGAAGTGCAAATTGATAG +GTTGATCACAGGCAGACTTCAAAGTTTGCAGACATATGTGACTCAACAATTAATTAGAGC +TGCAGAAATCAGAGCTTCTGCTAATCTTGCTGCTACTAAAATGTCAGAGTGTGTACTTGG +ACAATCAAAAAGAGTTGATTTTTGTGGAAAGGGCTATCATCTTATGTCCTTCCCTCAGTC +AGCACCTCATGGTGTAGTCTTCTTGCATGTGACTTATGTCCCTGCACAAGAAAAGAACTT +CACAACTGCTCCTGCCATTTGTCATGATGGAAAAGCACACTTTCCTCGTGAAGGTGTCTT +TGTTTCAAATGGCACACACTGGTTTGTAACACAAAGGAATTTTTATGAACCACAAATCAT +TACTACAGACAACACATTTGTGTCTGGTAACTGTGATGTTGTAATAGGAATTGTCAACAA +CACAGTTTATGATCCTTTGCAACCAGAATTAGACTCATTCAAGGAGGAGTTAGATAAATA +TTTTAAGAATCATACATCACCAGATGTTGATTTAGGTGACATCTCTGGCATTAATGCTTC +AGTTGTAAACATTCAAAAAGAAATTGACCGCCTCAATGAGGTTGCCAAGAATTTAAATGA +ATCTCTCATCGATCTCCAAGAACTTGGAAAGTATGAGCAGTATATAAAATGGCCATGGTA +CATTTGGCTAGGTTTTATAGCTGGCTTGATTGCCATAGTAATGGTGACAATTATGCTTTG +CTGTATGACCAGTTGCTGTAGTTGTCTCAAGGGCTGTTGTTCTTGTGGATCCTGCTGCAA +ATTTGATGAAGACGACTCTGAGCCAGTGCTCAAAGGAGTCAAATTACATTACACATAAAC +GAACTTATGGATTTGTTTATGAGAATCTTCACAATTGGAACTGTAACTTTGAAGCAAGGT +GAAATCAAGGATGCTACTCCTTTAGATTTTGTTCGCGCTACTGCAACGATACCGATACAA +GCCTCACTCCCTTTCGGATGGCTTATTGTTGGCGTTGCACTTCTTGCTGTTTTTCAGAGC +GCTTCCAAAATCATAACCCTCAAAAAGAGATGGCAACTAGCACTCTCCAAGGGTGTTCAC +TTTGTTTGCAACTTGCTGTTGTTGTTTGTAACAGTTTACTCACACCTTTTGCTCGTTGCT +GCTGGCCTTGAAGCCCCTTTTCTCTATCTTTATGCTTTAGTCTACTTCTTGCAGAGTATA +AACTTTGTAAGAATAATAATTAGGCTTTGGCTTTGCTGGAAATGCCGTTCCAAAAACCCG +TTACTTTATGATGCCAACTATTTTCTTTGCTGGCATACTAATTGTTACGACTATTGTATA +CCTTACAATAGTGTAACTTCTTCAATTGTCATTACTTCAGGTGATGGCACAACAAGTCCT +ATTTCTGAACATGACTACCAGATTGGTGGTTATACTGAAAAATGGGAATCTGGAGTAAAA +GACTGTGTTGTATTACACAGTTACTTCACTTCAGACTATTACCAGCTGTACTCAACTCAA +TTGAGTACAGACACTGGTGTTGAACATGTTACCTTCTTCATCTACAATAAAATTGTTGAT +GAGCCTGAAGAACATGTCCAAATTCACACAATCGACGGTTCATCCGGAGTTGTTAATCCA +GTAATGGAACCAATTTATGATGAACCGACGACGACTACTAGCGTGCCTTTGTAAGCACAA +GCTGATGAGTACGAACTTATGTACTCATTCGTTTCGGAAGAGACAGGTACGTTAATAGTT +AATAGCGTACTTCTTTTTCTTGCTTTCGTGGTATTCTTGCTAGTTACACTAGCCATCCTT +ACTGCGCTTCGATTGTGTGCGTACTGCTGCAATATTGTTAACGTGAGTCTTGTAAAACCT +TCTTTTTACGTTTACTCTCGTGTTAAAAATCTGAATTCTTCTAGAGTTCCTGATCTTCTG +GTCTAAACGAACTAAATATTATATTAGTTTTTCTGTTTGGAACTTTAATTTTAGCCATGG +CAGATTCCAACGGTACTATTACCGTTGAAGAGCTTAAAAAGCTCCTTGAACAATGGAACC +TAGTAATAGGTTTCCTATTCCTTACATGGATTTGTCTTCTACAATTTGCCTATGCCAACA +GGAATAGGTTTTTGTATATAATTAAGTTAATTTTCCTCTGGCTGTTATGGCCAGTAACTT +TAGCTTGTTTTGTGCTTGCTGCTGTTTACAGAATAAATTGGATCACCGGTGGAATTGCTA +CCGCAATGGCTTGTCTTGTAGGCTTGATGTGGCTCAGCTACTTCATTGCTTCTTTCAGAC +TGTTTGCGCGTACGCGTTCCATGTGGTCATTCAATCCAGAAACTAACATTCTTCTCAACG +TGCCACTCCATGGCACTATTCTGACCAGACCGCTTCTAGAAAGTGAACTCGTAATCGGAG +CTGTGATCCTTCGTGGACATCTTCGTATTGCTGGACACCATCTAGGACGCTGTGACATCA +AGGACCTGCCTAAAGAAATCACTGTTGCTACATCACGAACGCTTTCTTATTACAAATTGG +GAGCTTCGCAGCGTGTAGCAGGTGACTCAGGTTTTGCTGCATACAGTCGCTACAGGATTG +GCAACTATAAATTAAACACAGACCATTCCAGTAGCAGTGACAATATTGCTTTGCTTGTAC +AGTAAGTGACAACAGATGTTTCATCTCGTTGACTTTCAGGTTACTATAGCAGAGATATTA +CTAATTATTATGAGGACTTTTAAAGTTTCCATTTGGAATCTTGATTACATCATAAACCTC +ATAATTAAAAATTTATCTAAGTCACTAACTGAGAATAAATATTCTCAATTAGATGAAGAG +CAACCAATGGAGATTGATTAAACGAACATGAAAATTATTCTTTTCTTGGCACTGATAACA +CTCGCTACTTGTGAGCTTTATCACTACCAAGAGTGTGTTAGAGGTACAACAGTACTTTTA +AAAGAACCTTGCTCTTCTGGAACATACGAGGGCAATTCACCATTTCATCCTCTAGCTGAT +AACAAATTTGCACTGACTTGCTTTAGCACTCAATTTGCTTTTGCTTGTCCTGACGGCGTA +AAACACGTCTATCAGTTACGTGCCAGATCAGCTTCACCTAAACTGTTCATCAGACAAGAG +GAAGTTCAAGAACTTTACTCTCCAATTTTTCTTATTGTTGCGGCAATAGTGTTTATAACA +CTTTGCTTCACATTCAAAAGAAAGATAGAATGATTGAACTTTCATTAATTGACTTCTATT +TGTGCTTTTTAGCCTTTCTGCTATTCCTTGTTTTAATTATGCTTATTATCTTTTGGTTCT +CACTTGAACTGCAAGATCATAATGAAACTTGTCACGCCTAAACGAACATGAAATTTCTTG +TTTTCTTAGGAATCATCACAACTGTAGCTGCATTTCACCAAGAATGTAGTTTACAGTCAT +GTACTCAACATCAACCATATGTAGTTGATGACCTGTGTCCTATTCACTTCTATTCTAAAT +GGTATATTAGAGTAGGAGCTAGAAAATCAGCACCTTTAATTGAATTGTGCGTGGATGAGG +CTGGTTCTAAATCACCCATTCAGTACATCGATATCGGTAATTATACAGTTTCCTGTTTAC +CTTTTACAATTAATTGCCAGGAACCTAAATTGGGTAGTCTTGTAGTGCGTTGTTCGTTCT +ATGAAGACTTTTTAGAGTATCATGACGTTCGTGTTGTTTTAATCTAAACGAACAAACTAA +ATGTCTGATAATGGACCCCAAAATCAGCGAAATGCACCCCGCATTACGTTTGGTGGACCC +TCAGATTCAACTGGCAGTAACCAGAATGGAGAACGCAGTGGGGCGCGATCAAAACAACGT +CGGCCCCAAGGTTTACCCAATAATACTGCGTCTTGGTTCACCGCTCTCACTCAACATGGC +AAGGAAGGCCTTAAATTCCCTCGAGGACAAGGCGTTCCAATTAACACCAATAGCAGTCCA +GATGACCAAATTGGCTACTACCGAAGAGCTACCAGACGAATTCGTGGTGGTGACGGTAAA +ATGAAAGATCTCAGTCCAAGATGGTATTTCTACTACCTAGGAACTGGGCCAGAAGCTGGA +CTTCCCTATGGTGCTAACAAAGACGGCATCATATGGGTTGCAACTGAGGGAGCCTTGAAT +ACACCAAAAGATCACATTGGCACCCGCAATCCTGCTAACAATGCTGCAATCGTGCTACAA +CTTCCTCAAGGAACAACATTGCCAAAAGGCTTCTACGCAGAAGGGAGCAGAGGCGGCAGT +CAAGCCTCTTCTCGTTCCTCATCACGTAGTCGCAACAGTTCAAGAAATTCAACTCCAGGC +AGCAGTATGGGAACTTCTCCTGCTAGAATGGCTGGCAATGGCGGTGATGCTGCTCTTGCT +TTGCTGCTGCTTGACAGATTGAACCAGCTTGAGAGCAAAATGTCTGGTAAAGGCCAACAA +CAACAAGGCCAAACTGTCACTAAGAAATCTGCTGCTGAGGCTTCTAAGAAGCCTCGGCAA +AAACGTACTGCCACTAAAGCATACAATGTAACACAAGCTTTCGGCAGACGTGGTCCAGAA +CAAACCCAAGGAAATTTTGGGGACCAGGAACTAATCAGACAAGGAACTGATTACAAACAT +TGGCCGCAAATTGCACAATTTGCCCCCAGCGCTTCAGCGTTCTTCGGAATGTCGCGCATT +GGCATGGAAGTCACACCTTCGGGAACGTGGTTGACCTACACAGGTGCCATCAAATTGGAT +GACAAAGATCCAAATTTCAAAGATCAAGTCATTTTGCTGAATAAGCATATTGACGCATAC +AAAACATTCCCACCAACAGAGCCTAAAAAGGACAAAAAGAAGAAGGCTTATGAAACTCAA +GCCTTACCGCAGAGACAGAAGAAACAGCAAACTGTGACTCTTCTTCCTGCTGCAGATTTG +GATGATTTCTCCAAACAATTGCAACAATCCATGAGCAGTGCTGACTCAACTCAGGCCTAA +ACTCATGCAGACCACACAAGGCAGATGGGCTATATAAACGTTTTCGCTTTTCCGTTTACG +ATATATAGTCTACTCTTGTGCAGAATGAATTCTCGTAACTACATAGCACAAGTAGATGTA +GTTAACTTTAATCTCACATAGCAATCTTTAATCAGTGTGTAACATTAGGGAGGACTTGAA +AGAGCCACCACATTTTCACCGAGGCCACTCGGAGTACGATCGAGTGTACAGTGAACAATG +CTAGGGAGAGCTGCCTATATGGAAGAGCCCTAATGTGTAAAATTAATTTTAGTAGTGCTA +TCCCCATGTGATTTTAATAGNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN +NNNNNNNNNN From 1a677b71b6e7879a1cd07b92d638913df7161313 Mon Sep 17 00:00:00 2001 From: Maryam Date: Mon, 13 Jul 2026 11:26:44 -0700 Subject: [PATCH 48/52] updating test commands --- bygul/tests/data/test_proportions.py | 29 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/bygul/tests/data/test_proportions.py b/bygul/tests/data/test_proportions.py index 44ec622..ea542eb 100644 --- a/bygul/tests/data/test_proportions.py +++ b/bygul/tests/data/test_proportions.py @@ -66,13 +66,13 @@ def test_prop_with_freyja_manual_proportions(self): # Test explicit --proportions workflow self.assertEqual( os.system( - "bygul simulate-proportions " - "--genomes bygul/tests/data/BCN-SEARCH-105346.fasta," - "bygul/tests/data/CA-SEARCH-43254.fasta " - "--primers bygul/tests/data/ARTIC_V4-1.bed " - "--reference bygul/tests/data/reference.fasta " - "--proportions 0.8,0.2 " - "--redo" + "bygul simulate-proportions " + "--primers bygul/tests/data/ARTIC_V4-1.bed " + "--genomes bygul/tests/data/ATM-2FFMD73N3.fasta," + "bygul/tests/data/KR-SEARCH-120354.fasta " + "--proportions 0.8,0.2 --redo --simulator mason " + "--reference bygul/tests/data/reference.fasta " + "--illumina-read-length 200" ), 0, ) @@ -88,14 +88,13 @@ def test_prop_with_freyja_csv_mason(self): # Test CSV + multifasta workflow self.assertEqual( os.system( - "bygul simulate-proportions " - "--csv bygul/tests/data/sample_proportions.csv " - "--multifasta bygul/tests/data/sample_genomes.fasta " - "--primers bygul/tests/data/ARTIC_V4-1.bed " - "--reference bygul/tests/data/reference.fasta " - "--redo " - "--simulator mason " - "--illumina-read-length 200" + "bygul simulate-proportions " + "--primers bygul/tests/data/ARTIC_V4-1.bed " + "--csv bygul/tests/data/sample_proportions.csv " + "--multifasta bygul/tests/data/sample_genomes.fasta " + "--redo --simulator mason " + "--reference bygul/tests/data/reference.fasta " + "--illumina-read-length 200" ), 0, ) From 59e73b92503cfd0a06322d0fb18b02f09e786aff Mon Sep 17 00:00:00 2001 From: Maryam Date: Mon, 13 Jul 2026 13:50:46 -0700 Subject: [PATCH 49/52] reverting the test proportions --- bygul/tests/data/test_proportions.py | 144 +++++++-------------------- 1 file changed, 35 insertions(+), 109 deletions(-) diff --git a/bygul/tests/data/test_proportions.py b/bygul/tests/data/test_proportions.py index ea542eb..eb4e307 100644 --- a/bygul/tests/data/test_proportions.py +++ b/bygul/tests/data/test_proportions.py @@ -5,125 +5,51 @@ class ProportionTests(unittest.TestCase): - def run_freyja(self): - ref = "bygul/tests/data/reference.fasta" - - self.assertEqual( - os.system( - f"minimap2 -ax sr {ref} " - "results/reads_1.fastq results/reads_2.fastq | " - "samtools sort -o results/merged.bam" - ), - 0, - ) - - self.assertEqual( - os.system("samtools index results/merged.bam"), - 0, - ) - - self.assertEqual( - os.system( - "freyja variants results/merged.bam " - "--variants results/variants.tsv " - f"--depths results/depths.tsv --ref {ref}" - ), - 0, - ) - - self.assertEqual( - os.system("freyja update --outdir ."), - 0, - ) - - self.assertEqual( - os.system( - "freyja demix results/variants.tsv " - "results/depths.tsv " - "--output results/demix.tsv " - "--depthcutoff 20 " - "--lineageyml lineages.yml" - ), - 0, - ) - - self.assertTrue(os.path.exists("results/demix.tsv")) - - df = pd.read_csv( - "results/demix.tsv", - sep="\t", - index_col=0, - ) - - abundances = [ - float(x) - for x in df.loc["abundances"].values[0].split() - ] - - return sorted(abundances, reverse=True) - - def test_prop_with_freyja_manual_proportions(self): - # Test explicit --proportions workflow - self.assertEqual( - os.system( + def test_prop_with_freyja(self): + # 1. Run the simulation + os.system( "bygul simulate-proportions " + "--genomes bygul/tests/data/BCN-SEARCH-105346.fasta," + "bygul/tests/data/CA-SEARCH-43254.fasta " "--primers bygul/tests/data/ARTIC_V4-1.bed " - "--genomes bygul/tests/data/ATM-2FFMD73N3.fasta," - "bygul/tests/data/KR-SEARCH-120354.fasta " - "--proportions 0.8,0.2 --redo --simulator mason " "--reference bygul/tests/data/reference.fasta " - "--illumina-read-length 200" - ), - 0, - ) - + "--proportions 0.8,0.2 --redo") self.assertTrue(os.path.exists("results/reads_1.fastq")) - abundances = self.run_freyja() - - self.assertAlmostEqual(abundances[0], 0.8, delta=0.1) - self.assertAlmostEqual(abundances[1], 0.2, delta=0.1) - - def test_prop_with_freyja_csv_mason(self): - # Test CSV + multifasta workflow - self.assertEqual( - os.system( - "bygul simulate-proportions " - "--primers bygul/tests/data/ARTIC_V4-1.bed " - "--csv bygul/tests/data/sample_proportions.csv " - "--multifasta bygul/tests/data/sample_genomes.fasta " - "--redo --simulator mason " - "--reference bygul/tests/data/reference.fasta " - "--illumina-read-length 200" - ), - 0, - ) - - self.assertTrue(os.path.exists("results/reads_1.fastq")) + # 2. Run Freyja Pipeline (Align -> Variants -> Demix) + # Note: Replace 'minimap2' with your preferred aligner + ref = "bygul/tests/data/reference.fasta" + os.system(f"minimap2 -ax sr {ref} " + "results/reads_1.fastq results/reads_2.fastq | " + "samtools sort -o results/merged.bam") + os.system("samtools index results/merged.bam") + # Freyja commands + os.system("freyja variants results/merged.bam " + "--variants results/variants.tsv " + f"--depths results/depths.tsv --ref {ref}") + os.system("freyja update --outdir .") + os.system("freyja demix results/variants.tsv " + "results/depths.tsv --output " + "results/demix.tsv --depthcutoff 20 " + "--lineageyml lineages.yml") + + # 3. Validate Proportions + self.assertTrue(os.path.exists("results/demix.tsv")) - abundances = self.run_freyja() + # Read the Freyja output + df = pd.read_csv("results/demix.tsv", sep='\t', index_col=0) - expected = pd.read_csv( - "bygul/tests/data/sample_proportions.csv" - ) + # Access the string of numbers in the 'abundances' row + abundances_str = df.loc['abundances'].values[0] - expected_props = sorted( - expected["proportion"].tolist(), - reverse=True, - ) + abundances = [float(x) for x in abundances_str.split()] - self.assertEqual( - len(abundances), - len(expected_props), - ) + top_abundance = abundances[0] + second_abundance = abundances[1] - for observed, expected in zip(abundances, expected_props): - self.assertAlmostEqual( - observed, - expected, - delta=0.1, - ) + self.assertAlmostEqual(top_abundance, 0.8, delta=0.1) + self.assertAlmostEqual(second_abundance, 0.2, delta=0.1) -if __name__ == "__main__": +if __name__ == '__main__': unittest.main() From 70146fcd47f06e9e0b5f593277b6d7c51f63fa42 Mon Sep 17 00:00:00 2001 From: Maryam Date: Mon, 13 Jul 2026 13:51:57 -0700 Subject: [PATCH 50/52] using | pipe sign to distinguish contigs --- bygul/_cli.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/bygul/_cli.py b/bygul/_cli.py index 297ff3a..e6f3177 100644 --- a/bygul/_cli.py +++ b/bygul/_cli.py @@ -156,7 +156,7 @@ def simulate_proportions( sample_names = df["sample_name"].tolist() proportions = df["proportion"].tolist() for record in SeqIO.parse(multifasta, "fasta"): - sample = record.id.split("_")[0] + sample = record.id.split("|")[0] genome_map[sample].append(record) # Ensure every sample in the CSV exists missing = set(sample_names) - set(genome_map) @@ -169,7 +169,7 @@ def simulate_proportions( sample_paths = str(genomes).split(",") for path in sample_paths: for record in SeqIO.parse(path, "fasta"): - sample = record.id.split("_")[0] + sample = record.id.split("|")[0] genome_map[sample].append(record) sample_names = list(genome_map.keys()) # check directory exists- if redo specified make again @@ -200,6 +200,7 @@ def simulate_proportions( extra_simulator_flags) for name, cnt in zip(sample_names, read_cnts) ] + worker_func = process_amplicon_worker else: From 8b50aa8875bfcd0e9ec565811d4c95ff4d63e4f0 Mon Sep 17 00:00:00 2001 From: Maryam Date: Tue, 14 Jul 2026 09:26:05 -0700 Subject: [PATCH 51/52] adjusting gitignore to include vscode generated files --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 163431e..1f48a05 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ __pycache__/ *.so # Distribution / packaging +.vscode/ ._* .Python build/ From 6b804794a55111a18ffabb905b98936717eb2e21 Mon Sep 17 00:00:00 2001 From: Maryam Date: Tue, 14 Jul 2026 14:51:34 -0700 Subject: [PATCH 52/52] prevent program exiting if the find valid combinations function failed --- bygul/utils.py | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/bygul/utils.py b/bygul/utils.py index 839e82c..8968211 100644 --- a/bygul/utils.py +++ b/bygul/utils.py @@ -770,7 +770,14 @@ def process_primer_check_worker(args): str(genome_seq.seq), maxmismatch ) - all_amplicons = create_valid_primer_combinations(contig_df) + try: + all_amplicons = create_valid_primer_combinations(contig_df) + except Exception as e: + print(f"\n[!] SKIPPING {genome_seq.id} " + "due to an error in primer matching:") + print(f" Error details: {e}\n") + continue + all_amplicons = all_amplicons.fillna(0) all_amplicons["amplicon_length"] = np.where( @@ -806,7 +813,6 @@ def process_amplicon_worker(args): (name, genome_seqs, cnt, df_primers_template, maxmismatch, outdir, simulator, wgsim_insert_size, wgsim_read_length, wgsim_error_rate, extra_simulator_flags) = args - sample_amplicons_list = [] for genome_seq in genome_seqs: # Print information about the quality of the provided file @@ -816,7 +822,18 @@ def process_amplicon_worker(args): str(genome_seq.seq), maxmismatch ) - all_amplicons = create_valid_primer_combinations(contig_df) + try: + all_amplicons = create_valid_primer_combinations(contig_df) + except Exception as e: + print(f"\n[!] SKIPPING {genome_seq.id} " + "due to an error in primer matching:") + print(f" Error details: {e}\n") + continue + + if all_amplicons is None or all_amplicons.empty: + print("Warning: No primer matches found " + f"in contig {genome_seq.id}. Skipping to next contig.") + continue all_amplicons = all_amplicons.fillna(0) all_amplicons["amplicon_length"] = np.where( @@ -838,11 +855,10 @@ def process_amplicon_worker(args): ) all_amplicons["contig_id"] = genome_seq.id sample_amplicons_list.append(all_amplicons) - if not sample_amplicons_list: - return ("warning", - f"Warning: No sequences found in {name}") - + print("Failure: Absolutely no amplicons " + f"found across ANY contigs for {name}") + return ("failed", name, None, None) full_sample_df = pd.concat(sample_amplicons_list, ignore_index=True) amp_out_dir = os.path.join(outdir, name, "amplicons") os.makedirs(amp_out_dir, exist_ok=True)