From e29372ff459c26ed7646e6de775c7ac87563b808 Mon Sep 17 00:00:00 2001 From: Keoni Gandall Date: Fri, 31 May 2024 09:05:24 -0700 Subject: [PATCH 1/2] remove CircularLigation and add Ligate --- lib/clone/clone.go | 99 ++++++++++++++------------------------- lib/clone/clone_test.go | 75 ++++++++++++++--------------- lib/clone/example_test.go | 7 ++- 3 files changed, 78 insertions(+), 103 deletions(-) diff --git a/lib/clone/clone.go b/lib/clone/clone.go index de5df79..3499372 100644 --- a/lib/clone/clone.go +++ b/lib/clone/clone.go @@ -53,7 +53,6 @@ import ( "strings" "github.com/koeng101/dnadesign/lib/checks" - "github.com/koeng101/dnadesign/lib/seqhash" "github.com/koeng101/dnadesign/lib/transform" ) @@ -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 } /****************************************************************************** @@ -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 diff --git a/lib/clone/clone_test.go b/lib/clone/clone_test.go index 626ff07..6d23ffb 100644 --- a/lib/clone/clone_test.go +++ b/lib/clone/clone_test.go @@ -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.") } } @@ -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()) @@ -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") } } diff --git a/lib/clone/example_test.go b/lib/clone/example_test.go index 3be43f6..1365abd 100644 --- a/lib/clone/example_test.go +++ b/lib/clone/example_test.go @@ -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 } From 43f27c7af0415e1f970a18bc3010691bf3addd51 Mon Sep 17 00:00:00 2001 From: Keoni Gandall Date: Fri, 31 May 2024 09:09:25 -0700 Subject: [PATCH 2/2] update readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 84fcf77..7267791 100644 --- a/README.md +++ b/README.md @@ -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)