Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

remove CircularLigation and add Ligate #77

Merged
merged 2 commits into from
May 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]
- Greatly simplified the Ligate function [#77](https://github.com/Koeng101/dnadesign/pull/77)
- Updated barcoding functions to handle edge case of hanging-edge barcodes [#74](https://github.com/Koeng101/dnadesign/pull/74)
- Updated megamash to use int instead of uint for minimal Kmer counts (so you can use -1) [#73](https://github.com/Koeng101/dnadesign/pull/73)
- Added bcftools to external [#72](https://github.com/Koeng101/dnadesign/pull/72)
Expand Down
99 changes: 35 additions & 64 deletions lib/clone/clone.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@ import (
"strings"

"github.com/koeng101/dnadesign/lib/checks"
"github.com/koeng101/dnadesign/lib/seqhash"
"github.com/koeng101/dnadesign/lib/transform"
)

Expand Down Expand Up @@ -281,73 +280,46 @@ func CutWithEnzyme(part Part, directional bool, enzyme Enzyme, methylated bool)
return fragments
}

func recurseLigate(seedFragment Fragment, fragmentList []Fragment, usedFragments []Fragment, existingSeqhashes map[string]struct{}) (openConstructs []string, infiniteConstructs []string) {
// Recurse ligate simulates all possible ligations of a series of fragments. Each possible combination begins with a "seed" that fragments from the pool can be added to.
// If the seed ligates to itself, we can call it done with a successful circularization!
if seedFragment.ForwardOverhang == seedFragment.ReverseOverhang {
construct := seedFragment.ForwardOverhang + seedFragment.Sequence
seqhash, _ := seqhash.EncodeHash2(seqhash.Hash2(construct, "DNA", true, true))
if _, ok := existingSeqhashes[seqhash]; ok {
return nil, nil
}
existingSeqhashes[seqhash] = struct{}{}
return []string{construct}, nil
// Ligate simulates ligations. It assumes that fragments can only be ligated
// in a single way (no 2 fragments with the same overhangs), and also assumes
// the first fragment WILL be used in the ligation reaction. This function
// is a massive simplification of the original ligation code which can do more.
// If this does not fulfill your needs, please leave an issue in git.
func Ligate(fragments []Fragment) (string, error) {
if len(fragments) == 0 {
return "", errors.New("no fragments to ligate")
}

// If the seed ligates to another fragment, we can recurse and add that fragment to the seed
for _, newFragment := range fragmentList {
// If the seedFragment's reverse overhang is ligates to a fragment's forward overhang, we can ligate those together and seed another ligation reaction
var newSeed Fragment
var fragmentAttached bool
if seedFragment.ReverseOverhang == newFragment.ForwardOverhang {
fragmentAttached = true
newSeed = Fragment{seedFragment.Sequence + seedFragment.ReverseOverhang + newFragment.Sequence, seedFragment.ForwardOverhang, newFragment.ReverseOverhang}
}
// This checks if we can ligate the next fragment in its reverse direction. We have to be careful though - if our seed has a palindrome, it will ligate to itself
// like [-> <- -> <- -> ...] infinitely. We check for that case here as well.
if (seedFragment.ReverseOverhang == transform.ReverseComplement(newFragment.ReverseOverhang)) && (seedFragment.ReverseOverhang != transform.ReverseComplement(seedFragment.ReverseOverhang)) { // If the second statement isn't there, program will crash on palindromes
fragmentAttached = true
newSeed = Fragment{seedFragment.Sequence + seedFragment.ReverseOverhang + transform.ReverseComplement(newFragment.Sequence), seedFragment.ForwardOverhang, transform.ReverseComplement(newFragment.ForwardOverhang)}
}

// If fragment is actually attached, move to some checks
if fragmentAttached {
// If the newFragment's reverse complement already exists in the used fragment list, we need to cancel the recursion.
for _, usedFragment := range usedFragments {
if usedFragment.Sequence == newFragment.Sequence {
infiniteConstruct := usedFragment.ForwardOverhang + usedFragment.Sequence + usedFragment.ReverseOverhang
seqhash, _ := seqhash.EncodeHash2(seqhash.Hash2(infiniteConstruct, "DNA", false, true))
if _, ok := existingSeqhashes[seqhash]; ok {
return nil, nil
}
existingSeqhashes[seqhash] = struct{}{}
return nil, []string{infiniteConstruct}
}
finalFragment := fragments[0]
used := make(map[int]bool)
used[0] = true
matchFound := true
// iterate until no fragments are found
for matchFound {
matchFound = false
for i, fragment := range fragments {
if !used[i] && finalFragment.ReverseOverhang == fragment.ForwardOverhang {
finalFragment.Sequence += finalFragment.ReverseOverhang + fragment.Sequence
finalFragment.ReverseOverhang = fragment.ReverseOverhang
used[i] = true
matchFound = true
break
}
if !used[i] && finalFragment.ReverseOverhang == transform.ReverseComplement(fragment.ReverseOverhang) {
finalFragment.Sequence += finalFragment.ReverseOverhang + transform.ReverseComplement(fragment.Sequence)
finalFragment.ReverseOverhang = transform.ReverseComplement(fragment.ForwardOverhang)
used[i] = true
matchFound = true
break
}
// If everything is clear, append fragment to usedFragments and recurse.
usedFragments = append(usedFragments, newFragment)
openconstructs, infiniteconstructs := recurseLigate(newSeed, fragmentList, usedFragments, existingSeqhashes)

openConstructs = append(openConstructs, openconstructs...)
infiniteConstructs = append(infiniteConstructs, infiniteconstructs...)
}
}

return openConstructs, infiniteConstructs
}

// CircularLigate simulates ligation of all possible fragment combinations into circular plasmids.
func CircularLigate(fragments []Fragment) ([]string, []string) {
var outputConstructs []string
var outputInfiniteLoopingConstructs []string
existingSeqhashes := make(map[string]struct{})
for _, fragment := range fragments {
openConstructs, infiniteConstructs := recurseLigate(fragment, fragments, []Fragment{}, existingSeqhashes)

outputConstructs = append(outputConstructs, openConstructs...)
outputInfiniteLoopingConstructs = append(outputInfiniteLoopingConstructs, infiniteConstructs...)
// attempt circularization
if finalFragment.ForwardOverhang != finalFragment.ReverseOverhang {
return "", errors.New("does not circularize")
}
return outputConstructs, outputInfiniteLoopingConstructs
return finalFragment.ForwardOverhang + finalFragment.Sequence, nil
}

/******************************************************************************
Expand All @@ -359,14 +331,13 @@ Specific cloning functions begin here.
// GoldenGate simulates a GoldenGate cloning reaction. As of right now, we only
// support BsaI, BbsI, BtgZI, and BsmBI. Set methylated flag to true if there
// is lowercase methylated DNA as part of the sequence.
func GoldenGate(sequences []Part, cuttingEnzyme Enzyme, methylated bool) (openConstructs []string, infiniteLoops []string) {
func GoldenGate(sequences []Part, cuttingEnzyme Enzyme, methylated bool) (string, error) {
var fragments []Fragment
for _, sequence := range sequences {
newFragments := CutWithEnzyme(sequence, true, cuttingEnzyme, methylated)
fragments = append(fragments, newFragments...)
}
openconstructs, infiniteloops := CircularLigate(fragments)
return openconstructs, infiniteloops
return Ligate(fragments)
}

// GetBaseRestrictionEnzymes return a basic slice of common enzymes used in Golden Gate Assembly. Eventually, we want to get the data for this map from ftp://ftp.neb.com/pub/rebase
Expand Down
75 changes: 38 additions & 37 deletions lib/clone/clone_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,16 +140,14 @@ func TestCutWithEnzymeRegression(t *testing.T) {
}

func TestCircularLigate(t *testing.T) {
// The following tests for complementing overhangs. Specific, this line:
// newSeed := Fragment{seedFragment.Sequence + seedFragment.ReverseOverhang + ReverseComplement(newFragment.Sequence), seedFragment.ForwardOverhang, ReverseComplement(newFragment.ForwardOverhang)}
fragment1 := Fragment{"AAAAAA", "GTTG", "CTAT"}
fragment2 := Fragment{"AAAAAA", "CAAC", "ATAG"}
outputConstructs, infiniteLoops := CircularLigate([]Fragment{fragment1, fragment2})
if len(outputConstructs) != 1 {
t.Errorf("Circular ligation with complementing overhangs should only output 1 valid rotated sequence.")
output, err := Ligate([]Fragment{fragment1, fragment2})
if err != nil {
t.Errorf("Got error on ligation: %s", err)
}
if len(infiniteLoops) != 0 {
t.Errorf("Circular ligation should have no loops")
if output != "GTTGAAAAAACTATTTTTTT" {
t.Errorf("Ligation with complementing overhangs should only output 1 valid rotated sequence.")
}
}

Expand All @@ -164,34 +162,37 @@ func TestEnzymeManage_GetEnzymeByName_NotFound(t *testing.T) {
}
}

func TestSignalKilledGoldenGate(t *testing.T) {
enzymeManager := NewEnzymeManager(GetBaseRestrictionEnzymes())
// This previously would crash from using too much RAM.
fragment1 := Part{"AAAGCACTCTTAGGCCTCTGGAAGACATGGAGGGTCTCAAGGTGATCAAAGGATCTTCTTGAGATCCTTTTTTTCTGCGCGTAATCTTTTGCCCTGTAAACGAAAAAACCACCTGGGTAGTCTTCGCATTTCTTAATCGGTGCCC", false}
fragment2 := Part{"AAAGCACTCTTAGGCCTCTGGAAGACATTGGGGAGGTGGTTTGATCGAAGGTTAAGTCAGTTGGGGAACTGCTTAACCGTGGTAACTGGCTTTCGCAGAGCACAGCAACCAAATCTGTTAGTCTTCGCATTTCTTAATCGGTGCCC", false}
fragment3 := Part{"AAAGCACTCTTAGGCCTCTGGAAGACATCTGTCCTTCCAGTGTAGCCGGACTTTGGCGCACACTTCAAGAGCAACCGCGTGTTTAGCTAAACAAATCCTCTGCGAACTCCCAGTTACCTAGTCTTCGCATTTCTTAATCGGTGCCC", false}
fragment4 := Part{"AAAGCACTCTTAGGCCTCTGGAAGACATTACCAATGGCTGCTGCCAGTGGCGTTTTACCGTGCTTTTCCGGGTTGGACTCAAGTGAACAGTTACCGGATAAGGCGCAGCAGTCGGGCTTAGTCTTCGCATTTCTTAATCGGTGCCC", false}
fragment5 := Part{"AAAGCACTCTTAGGCCTCTGGAAGACATGGCTGAACGGGGAGTTCTTGCTTACAGCCCAGCTTGGAGCGAACGACCTACACCGAGCCGAGATACCAGTGTGTGAGCTATGAGAAAGCGTAGTCTTCGCATTTCTTAATCGGTGCCC", false}
fragment6 := Part{"AAAGCACTCTTAGGCCTCTGGAAGACATAGCGCCACACTTCCCGTAAGGGAGAAAGGCGGAACAGGTATCCGGTAAACGGCAGGGTCGGAACAGGAGAGCGCAAGAGGGAGCGACCCGTAGTCTTCGCATTTCTTAATCGGTGCCC", false}
fragment7 := Part{"AAAGCACTCTTAGGCCTCTGGAAGACATCCCGCCGGAAACGGTGGGGATCTTTAAGTCCTGTCGGGTTTCGCCCGTACTGTCAGATTCATGGTTGAGCCTCACGGCTCCCACAGATGTAGTCTTCGCATTTCTTAATCGGTGCCC", false}
fragment8 := Part{"AAAGCACTCTTAGGCCTCTGGAAGACATGATGCACCGGAAAAGCGTCTGTTTATGTGAACTCTGGCAGGAGGGCGGAGCCTATGGAAAAACGCCACCGGCGCGGCCCTGCTGTTTTGCCTCACATGTTAGTCTTCGCATTTCTTAATCGGTGCCC", false}
fragment9 := Part{"AAAGCACTCTTAGGCCTCTGGAAGACATATGTTAGTCCCCTGCTTATCCACGGAATCTGTGGGTAACTTTGTATGTGTCCGCAGCGCAAAAAGAGACCCGCTTAGTCTTCGCATTTCTTAATCGGTGCCC", false}
fragments := []Part{popen, fragment1, fragment2, fragment3, fragment4, fragment5, fragment6, fragment7, fragment8, fragment9}

bbsI, err := enzymeManager.GetEnzymeByName("BbsI")
if err != nil {
t.Errorf("Error when getting Enzyme. Got error: %s", err)
}

clones, loopingClones := GoldenGate(fragments, bbsI, false)
if len(clones) != 1 {
t.Errorf("There should be 1 output Got: %d", len(clones))
}
// This should be changed later when we have a better way of informing user of reused overhangs
if len(loopingClones) != 4 {
t.Errorf("Should be only 4 looping sequences. Got: %d", len(loopingClones))
}
}
// This was a previous regression test. However, now ligate only outputs a
// single construct as an output. If we change in the future for multiple
// ligations, we should revive this function.
//func TestSignalKilledGoldenGate(t *testing.T) {
// enzymeManager := NewEnzymeManager(GetBaseRestrictionEnzymes())
// // This previously would crash from using too much RAM.
// fragment1 := Part{"AAAGCACTCTTAGGCCTCTGGAAGACATGGAGGGTCTCAAGGTGATCAAAGGATCTTCTTGAGATCCTTTTTTTCTGCGCGTAATCTTTTGCCCTGTAAACGAAAAAACCACCTGGGTAGTCTTCGCATTTCTTAATCGGTGCCC", false}
// fragment2 := Part{"AAAGCACTCTTAGGCCTCTGGAAGACATTGGGGAGGTGGTTTGATCGAAGGTTAAGTCAGTTGGGGAACTGCTTAACCGTGGTAACTGGCTTTCGCAGAGCACAGCAACCAAATCTGTTAGTCTTCGCATTTCTTAATCGGTGCCC", false}
// fragment3 := Part{"AAAGCACTCTTAGGCCTCTGGAAGACATCTGTCCTTCCAGTGTAGCCGGACTTTGGCGCACACTTCAAGAGCAACCGCGTGTTTAGCTAAACAAATCCTCTGCGAACTCCCAGTTACCTAGTCTTCGCATTTCTTAATCGGTGCCC", false}
// fragment4 := Part{"AAAGCACTCTTAGGCCTCTGGAAGACATTACCAATGGCTGCTGCCAGTGGCGTTTTACCGTGCTTTTCCGGGTTGGACTCAAGTGAACAGTTACCGGATAAGGCGCAGCAGTCGGGCTTAGTCTTCGCATTTCTTAATCGGTGCCC", false}
// fragment5 := Part{"AAAGCACTCTTAGGCCTCTGGAAGACATGGCTGAACGGGGAGTTCTTGCTTACAGCCCAGCTTGGAGCGAACGACCTACACCGAGCCGAGATACCAGTGTGTGAGCTATGAGAAAGCGTAGTCTTCGCATTTCTTAATCGGTGCCC", false}
// fragment6 := Part{"AAAGCACTCTTAGGCCTCTGGAAGACATAGCGCCACACTTCCCGTAAGGGAGAAAGGCGGAACAGGTATCCGGTAAACGGCAGGGTCGGAACAGGAGAGCGCAAGAGGGAGCGACCCGTAGTCTTCGCATTTCTTAATCGGTGCCC", false}
// fragment7 := Part{"AAAGCACTCTTAGGCCTCTGGAAGACATCCCGCCGGAAACGGTGGGGATCTTTAAGTCCTGTCGGGTTTCGCCCGTACTGTCAGATTCATGGTTGAGCCTCACGGCTCCCACAGATGTAGTCTTCGCATTTCTTAATCGGTGCCC", false}
// fragment8 := Part{"AAAGCACTCTTAGGCCTCTGGAAGACATGATGCACCGGAAAAGCGTCTGTTTATGTGAACTCTGGCAGGAGGGCGGAGCCTATGGAAAAACGCCACCGGCGCGGCCCTGCTGTTTTGCCTCACATGTTAGTCTTCGCATTTCTTAATCGGTGCCC", false}
// fragment9 := Part{"AAAGCACTCTTAGGCCTCTGGAAGACATATGTTAGTCCCCTGCTTATCCACGGAATCTGTGGGTAACTTTGTATGTGTCCGCAGCGCAAAAAGAGACCCGCTTAGTCTTCGCATTTCTTAATCGGTGCCC", false}
// fragments := []Part{popen, fragment1, fragment2, fragment3, fragment4, fragment5, fragment6, fragment7, fragment8, fragment9}
//
// bbsI, err := enzymeManager.GetEnzymeByName("BbsI")
// if err != nil {
// t.Errorf("Error when getting Enzyme. Got error: %s", err)
// }
//
// clones, loopingClones := GoldenGate(fragments, bbsI, false)
// if len(clones) != 1 {
// t.Errorf("There should be 1 output Got: %d", len(clones))
// }
// // This should be changed later when we have a better way of informing user of reused overhangs
// if len(loopingClones) != 4 {
// t.Errorf("Should be only 4 looping sequences. Got: %d", len(loopingClones))
// }
//}

func TestPanicGoldenGate(t *testing.T) {
enzymeManager := NewEnzymeManager(GetBaseRestrictionEnzymes())
Expand Down Expand Up @@ -238,8 +239,8 @@ func TestMethylatedGoldenGate(t *testing.T) {
if err != nil {
t.Errorf("Error when getting Enzyme. Got error: %s", err)
}
clones, _ := GoldenGate([]Part{pOpenV3Methylated, frag1, frag2, frag3}, bsai, true)
if len(clones) != 1 {
_, err = GoldenGate([]Part{pOpenV3Methylated, frag1, frag2, frag3}, bsai, true)
if err != nil {
t.Errorf("Should have gotten a single clone")
}
}
Expand Down
7 changes: 5 additions & 2 deletions lib/clone/example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,11 @@ func ExampleGoldenGate() {
if err != nil {
log.Fatalf("Something went wrong when trying to get the enzyme. Got error: %s", err)
}
Clones, _ := clone.GoldenGate([]clone.Part{fragment1, fragment2, popen}, bbsI, false)
plasmid, err := clone.GoldenGate([]clone.Part{fragment1, fragment2, popen}, bbsI, false)
if err != nil {
log.Fatalf("Failed to GoldenGate. Got error: %s", err)
}

fmt.Println(seqhash.RotateSequence(Clones[0]))
fmt.Println(seqhash.RotateSequence(plasmid))
// Output: AAAAAAAGGATCTCAAGAAGGCCTACTATTAGCAACAACGATCCTTTGATCTTTTCTACGGGGTCTGACGCTCAGTGGAACGAAAACTCACGTTAAGGGATTTTGGTCATGAGATTATCAAAAAGGATCTTCACCTAGATCCTTTTAAATTAAAAATGAAGTTTTAAATCAATCTAAAGTATATATGAGTAAACTTGGTCTGACAGTTACCAATGCTTAATCAGTGAGGCACCTATCTCAGCGATCTGTCTATTTCGTTCATCCATAGTTGCCTGACTCCCCGTCGTGTAGATAACTACGATACGGGAGGGCTTACCATCTGGCCCCAGTGCTGCAATGATACCGCGAGAACCACGCTCACCGGCTCCAGATTTATCAGCAATAAACCAGCCAGCCGGAAGGGCCGAGCGCAGAAGTGGTCCTGCAACTTTATCCGCCTCCATCCAGTCTATTAATTGTTGCCGGGAAGCTAGAGTAAGTAGTTCGCCAGTTAATAGTTTGCGCAACGTTGTTGCCATTGCTACAGGCATCGTGGTGTCACGCTCGTCGTTTGGTATGGCTTCATTCAGCTCCGGTTCCCAACGATCAAGGCGAGTTACATGATCCCCCATGTTGTGCAAAAAAGCGGTTAGCTCCTTCGGTCCTCCGATCGTTGTCAGAAGTAAGTTGGCCGCAGTGTTATCACTCATGGTTATGGCAGCACTGCATAATTCTCTTACTGTCATGCCATCCGTAAGATGCTTTTCTGTGACTGGTGAGTACTCAACCAAGTCATTCTGAGAATAGTGTATGCGGCGACCGAGTTGCTCTTGCCCGGCGTCAATACGGGATAATACCGCGCCACATAGCAGAACTTTAAAAGTGCTCATCATTGGAAAACGTTCTTCGGGGCGAAAACTCTCAAGGATCTTACCGCTGTTGAGATCCAGTTCGATGTAACCCACTCGTGCACCCAACTGATCTTCAGCATCTTTTACTTTCACCAGCGTTTCTGGGTGAGCAAAAACAGGAAGGCAAAATGCCGCAAAAAAGGGAATAAGGGCGACACGGAAATGTTGAATACTCATACTCTTCCTTTTTCAATATTATTGAAGCATTTATCAGGGTTATTGTCTCATGAGCGGATACATATTTGAATGTATTTAGAAAAATAAACAAATAGGGGTTCCGCGCACCTGCACCAGTCAGTAAAACGACGGCCAGTAGTCAAAAGCCTCCGACCGGAGGCTTTTGACTTGGTTCAGGTGGAGTGGGAGAAACACGTGGCAAACATTCCGGTCTCAAATGGAAAAGAGCAACGAAACCAACGGCTACCTTGACAGCGCTCAAGCCGGCCCTGCAGCTGGCCCGGGCGCTCCGGGTACCGCCGCGGGTCGTGCACGTCGTTGCGCGGGCTTCCTGCGGCGCCAAGCGCTGGTGCTGCTCACGGTGTCTGGTGTTCTGGCAGGCGCCGGTTTGGGCGCGGCACTGCGTGGGCTCAGCCTGAGCCGCACCCAGGTCACCTACCTGGCCTTCCCCGGCGAGATGCTGCTCCGCATGCTGCGCATGATCATCCTGCCGCTGGTGGTCTGCAGCCTGGTGTCGGGCGCCGCCTCCCTCGATGCCAGCTGCCTCGGGCGTCTGGGCGGTATCGCTGTCGCCTACTTTGGCCTCACCACACTGAGTGCCTCGGCGCTCGCCGTGGCCTTGGCGTTCATCATCAAGCCAGGATCCGGTGCGCAGACCCTTCAGTCCAGCGACCTGGGGCTGGAGGACTCGGGGCCTCCTCCTGTCCCCAAAGAAACGGTGGACTCTTTCCTCGACCTGGCCAGAAACCTGTTTCCCTCCAATCTTGTGGTTGCAGCTTTCCGTACGTATGCAACCGATTATAAAGTCGTGACCCAGAACAGCAGCTCTGGAAATGTAACCCATGAAAAGATCCCCATAGGCACTGAGATAGAAGGGATGAACATTTTAGGATTGGTCCTGTTTGCTCTGGTGTTAGGAGTGGCCTTAAAGAAACTAGGCTCCGAAGGAGAGGACCTCATCCGTTTCTTCAATTCCCTCAACGAGGCGACGATGGTGCTGGTGTCCTGGATTATGTGGTACGTACCTGTGGGCATCATGTTCCTTGTTGGAAGCAAGATCGTGGAAATGAAAGACATCATCGTGCTGGTGACCAGCCTGGGGAAATACATCTTCGCATCTATATTGGGCCACGTCATTCATGGTGGTATCGTCCTGCCGCTGATTTATTTTGTTTTCACACGAAAAAACCCATTCAGATTCCTCCTGGGCCTCCTCGCCCCATTTGCGACAGCATTTGCTACGTGCTCCAGCTCAGCGACCCTTCCCTCTATGATGAAGTGCATTGAAGAGAACAATGGTGTGGACAAGAGGATCTCCAGGTTTATTCTCCCCATCGGGGCCACCGTGAACATGGACGGAGCAGCCATCTTCCAGTGTGTGGCCGCGGTGTTCATTGCGCAACTCAACAACGTAGAGCTCAACGCAGGACAGATTTTCACCATTCTAGTGACTGCCACAGCGTCCAGTGTTGGAGCAGCAGGCGTGCCAGCTGGAGGGGTCCTCACCATTGCCATTATCCTGGAGGCCATTGGGCTGCCTACTCATGATCTGCCTCTGATCCTGGCTGTGGACTGGATTGTGGACCGGACCACCACGGTGGTGAATGTGGAAGGGGATGCCCTGGGTGCAGGCATTCTCCACCACCTGAATCAGAAGGCAACAAAGAAAGGCGAGCAGGAACTTGCTGAGGTGAAAGTGGAAGCCATCCCCAACTGCAAGTCTGAGGAGGAAACCTCGCCCCTGGTGACACACCAGAACCCCGCTGGCCCCGTGGCCAGTGCCCCAGAACTGGAATCCAAGGAGTCGGTTCTGTGAAGAGCTTAGAGACCGACGACTGCCTAAGGACATTCGCTGAGGTGTCAATCGTCGGAGCCGCTGAGCAATAACTAGCATAACCCCTTGGGGCCTCTAAACGGGTCTTGAGGGGTTTTTTGCATGGTCATAGCTGTTTCCTGAGAGCTTGGCAGGTGATGACACACATTAACAAATTTCGTGAGGAGTCTCCAGAAGAATGCCATTAATTTCCATAGGCTCCGCCCCCCTGACGAGCATCACAAAAATCGACGCTCAAGTCAGAGGTGGCGAAACCCGACAGGACTATAAAGATACCAGGCGTTTCCCCCTGGAAGCTCCCTCGTGCGCTCTCCTGTTCCGACCCTGCCGCTTACCGGATACCTGTCCGCCTTTCTCCCTTCGGGAAGCGTGGCGCTTTCTCATAGCTCACGCTGTAGGTATCTCAGTTCGGTGTAGGTCGTTCGCTCCAAGCTGGGCTGTGTGCACGAACCCCCCGTTCAGCCCGACCGCTGCGCCTTATCCGGTAACTATCGTCTTGAGTCCAACCCGGTAAGACACGACTTATCGCCACTGGCAGCAGCCACTGGTAACAGGATTAGCAGAGCGAGGTATGTAGGCGGTGCTACAGAGTTCTTGAAGTGGTGGCCTAACTACGGCTACACTAGAAGAACAGTATTTGGTATCTGCGCTCTGCTGAAGCCAGTTACCTTCGGAAAAAGAGTTGGTAGCTCTTGATCCGGCAAACAAACCACCGCTGGTAGCGGTGGTTTTTTTGTTTGCAAGCAGCAGATTACGCGCAG
}
Loading