{-# LANGUAGE PatternGuards #-}{-# LANGUAGE BangPatterns #-}{-# LANGUAGE RecordWildCards #-}-- | These functions are implementations of RNA secondary structure folding as-- described in "Bompfuenewere et al., 2006, Variations on RNA folding and-- alignment"---- We have all the facilities needed for folding with the RNA parameter of-- Turner 2004 http://rna.urmc.rochester.edu/NNDB/turner04/ but consider only-- "double dangles" which correspond to the ViennaRNA package option "-d2".-- They are a bit easier to implement and are what is used for partition-- function calculations. In addition, it seems unlikely to see a statiscally-- relevant improvement in predication with "-d1" or "-d3".---- All functions work on an algebraic ring structure. This should make it-- easier to implement certain functionality without having to rewrite all the-- functions given here. Try deriving a new 'Ring' instance first and see if it-- /just works/.---- These functions do quite well, performancewise. GHC-HEAD with "-Odph" and-- "-fllvm" takes 14.4s, while the highly optimized viennaRNA package (the yet-- unpublished 2.0 version) takes about 1-2s on a sequence of length 1000.---- NOTE For GHC <= 6.12.3 you should copy the default instances into your-- instance BLA, otherwise the resulting code will be slow. Or you could just-- wait for the new GHC to arrive! The new one produces good code without such-- stuff.---- TODO single nucleotide bulges:-- http://rna.urmc.rochester.edu/NNDB/turner04/bulge.html , check what Vienna-- 2.0 does!moduleBioInf.RNAFold.Functions(FoldFunctions(..),Table,TurnerTables,ringSumL,pair,riap,ringProductL,tabbedInteriorLoopDistances)whereimportqualifiedData.Vector.UnboxedasVUimportControl.Exception(assert)importData.List(foldl')importqualifiedData.MapasMimportBiobase.RNAimportBiobase.Turner.TablesimportBiobase.Types.RingimportBiobase.ViennaimportData.PrimitiveArrayimportData.Primitive.TypesimportDebug.Trace.ToolstypeCell=(Int,Int)typeTablea=PrimArrayCellatypeTurnerTablesa=Turner2004ViennaPairNucleotidea-- | The folding functions. It could happen that we need different folding-- functions with the same type, hence the class-based approach. The default-- instance uses the usual ring methods.class(Showa,Ringa,VU.Unboxa,Prima)=>FoldFunctionsawherestackOpt::TurnerTablesa->Primary->Tablea->Int->Int->astackIdx::TurnerTablesa->Primary->Tablea->Int->Int->[(Cell,a)]hairpinOpt::TurnerTablesa->Primary->Int->Int->ahairpinIdx::TurnerTablesa->Primary->Int->Int->[a]largeInteriorLoopOpt::TurnerTablesa->Primary->Tablea->Int->Int->alargeInteriorLoopIdx::TurnerTablesa->Primary->Tablea->Int->Int->[(Cell,a)]tabbedInteriorLoopOpt::TurnerTablesa->Primary->Tablea->Int->Int->atabbedInteriorLoopIdx::TurnerTablesa->Primary->Tablea->Int->Int->[(Cell,a)]bulgeLOpt::TurnerTablesa->Primary->Tablea->Int->Int->abulgeLIdx::TurnerTablesa->Primary->Tablea->Int->Int->[(Cell,a)]bulgeROpt::TurnerTablesa->Primary->Tablea->Int->Int->abulgeRIdx::TurnerTablesa->Primary->Tablea->Int->Int->[(Cell,a)]interior1xnLOpt::TurnerTablesa->Primary->Tablea->Int->Int->ainterior1xnLIdx::TurnerTablesa->Primary->Tablea->Int->Int->[(Cell,a)]interior1xnROpt::TurnerTablesa->Primary->Tablea->Int->Int->ainterior1xnRIdx::TurnerTablesa->Primary->Tablea->Int->Int->[(Cell,a)]multibranchIJLoopOpt::TurnerTablesa->Primary->Tablea->Int->Int->amultibranchIJLoopIdx::TurnerTablesa->Primary->Tablea->Int->Int->[(Cell,a)]multibranchUnpairedJOpt::TurnerTablesa->Primary->Tablea->Int->Int->amultibranchUnpairedJIdx::TurnerTablesa->Primary->Tablea->Int->Int->[(Cell,a)]multibranchKJHelixOpt::TurnerTablesa->Primary->Tablea->Int->Int->amultibranchKJHelixIdx::TurnerTablesa->Primary->Tablea->Int->Int->[(Int,a)]multibranchAddKJHelixOpt::TurnerTablesa->Primary->Tablea->Tablea->Int->Int->amultibranchAddKJHelixIdx::TurnerTablesa->Primary->Tablea->Tablea->Int->Int->[(Int,a)]multibranchCloseOpt::TurnerTablesa->Primary->Tablea->Tablea->Int->Int->amultibranchCloseIdx::TurnerTablesa->Primary->Tablea->Tablea->Int->Int->[(Int,a)]externalLoopOpt::TurnerTablesa->Primary->Tablea->Int->Int->aexternalLoopIdx::TurnerTablesa->Primary->Tablea->Int->Int->[(Cell,a)]externalAddLoopOpt::TurnerTablesa->Primary->Tablea->Tablea->Int->Int->aexternalAddLoopIdx::TurnerTablesa->Primary->Tablea->Tablea->Int->Int->[(Int,a)]-- return only 'k' index-- | Calculate the ninio asymmetric malus. Can not be written using ring-- functions alone as a 'min' or 'max' functions is required.calcNinio::a->a->Int->a-- | Applies a terminal AU/GU penalty, where required.---- TODO shouldn't this be just: if CG||GC then one else termAU?calcTermAU::a->ViennaPair->a-- ^ Apply terminal AU penalty-- | large hairpin loops >30 require special calculations that involve-- 'floor', rounding and other stuff that can not be handled by the Ring-- class alonecalcLargeLoop::Int->a-- NOTE Copy these functions into your own instance. In most cases, your are-- now done and get optimized loops. Each function that you do not copy uses-- the version here. This can lead to less efficient code.---- NOTE Fixed in GHC 6.13. The default instances should yield superb code!stackOpttrnrinptblij=VU.foldl'(.+.)zero$stackBasetrnrinptblijhairpinOpttrnrinpij=VU.foldl'(.+.)zero$hairpinBasetrnrinpijlargeInteriorLoopOpttrnrinpstrongij=VU.foldl'(.+.)zero$largeInteriorLoopBasetrnrinpstrongijtabbedInteriorLoopOpttrnrinpstrongij=VU.foldl'(.+.)zero$tabbedInteriorLoopBasetrnrinpstrongijbulgeLOpttrnrinpstrongij=VU.foldl'(.+.)zero$bulgeLBasetrnrinpstrongijbulgeROpttrnrinpstrongij=VU.foldl'(.+.)zero$bulgeRBasetrnrinpstrongijinterior1xnLOpttrnrinpstrongij=VU.foldl'(.+.)zero$interior1xnLBasetrnrinpstrongijinterior1xnROpttrnrinpstrongij=VU.foldl'(.+.)zero$interior1xnRBasetrnrinpstrongijmultibranchIJLoopOpttrnrinpstrongij=VU.foldl'(.+.)zero$multibranchIJLoopBasetrnrinpstrongijmultibranchUnpairedJOpttrnrinpmtableij=VU.foldl'(.+.)zero$multibranchUnpairedJBasetrnrinpmtableijmultibranchKJHelixOpttrnrinpstrongij=VU.foldl'(.+.)zero$multibranchKJHelixBasetrnrinpstrongijmultibranchAddKJHelixOpttrnrinptablestrongij=VU.foldl'(.+.)zero$multibranchAddKJHelixBasetrnrinptablestrongijmultibranchCloseOpttrnrinpmm1ij=VU.foldl'(.+.)zero$multibranchCloseBasetrnrinpmm1ijexternalLoopOpttrnrinpstrongij=VU.foldl'(.+.)zero$externalLoopBasetrnrinpstrongijexternalAddLoopOpttrnrinpstrongexternij=VU.foldl'(.+.)zero$externalAddLoopBasetrnrinpstrongexternij-- NOTE copy and instanciate these functions if you have to work with many-- candidate sequences. Otherwise you probably do not need the speed-up from-- these functions. This stuff is for backtracking mostly as you get lists-- of indices with attached scores.---- NOTE With GHC 6.13 we should get optimized code anyways!stackIdxtrnrinptblij=[((i+1,j-1),stackOpttrnrinptblij)]hairpinIdxtrnrinpij=VU.toList$hairpinBasetrnrinpijlargeInteriorLoopIdxtrnrinpstrongij=VU.toList$VU.zip(interiorLoopIndicesij)(largeInteriorLoopBasetrnrinpstrongij)tabbedInteriorLoopIdxtrnrinpstrongij=VU.toList$VU.zip(tabbedInteriorLoopIndicesij)$tabbedInteriorLoopBasetrnrinpstrongijbulgeLIdxtrnrinpstrongij=VU.toList$VU.zip(VU.map(\k->(i+1+k,j-1)).uncurryVU.enumFromN$bulgeLimitij)$bulgeLBasetrnrinpstrongijbulgeRIdxtrnrinpstrongij=VU.toList$VU.zip(VU.map(\k->(i+1,j-1-k)).uncurryVU.enumFromN$bulgeLimitij)$bulgeRBasetrnrinpstrongijinterior1xnLIdxtrnrinpstrongij=VU.toList$VU.zip(VU.map(\k->(i+1+k,j-2))$uncurryVU.enumFromN$iloop1xnLimitij)$interior1xnLBasetrnrinpstrongijinterior1xnRIdxtrnrinpstrongij=VU.toList$VU.zip(VU.map(\k->(i+2,j-1-k))$uncurryVU.enumFromN$iloop1xnLimitij)$interior1xnRBasetrnrinpstrongijmultibranchIJLoopIdxtrnrinpstrongij=VU.toList$VU.zip(VU.singleton(i,j))$multibranchIJLoopBasetrnrinpstrongijmultibranchUnpairedJIdxtrnrinpmtableij=VU.toList$VU.zip(VU.singleton(i,j-1))$multibranchUnpairedJBasetrnrinpmtableijmultibranchKJHelixIdxtrnrinpstrongij=VU.toList$VU.zip(uncurryVU.enumFromN$multibranchKJHelixLimitij)$multibranchKJHelixBasetrnrinpstrongijmultibranchCloseIdxtrnrinpmm1ij=-- TODO this is wrong!VU.toList$VU.zip(uncurryVU.enumFromN$multibranchCloseLimitij)$multibranchCloseBasetrnrinpmm1ijmultibranchAddKJHelixIdxtrnrinptablestrongij=VU.toList$VU.zip(uncurryVU.enumFromN$multibranchAddKJHelixLimitij)$multibranchAddKJHelixBasetrnrinptablestrongijexternalLoopIdxtrnrinpstrongij=VU.toList$VU.singleton((i,j),externalLoopOpttrnrinpstrongij)externalAddLoopIdxtrnrinpstrongexternij=VU.toList$VU.zip(uncurryVU.enumFromN$externalAddLoopLimitij)$externalAddLoopBasetrnrinpstrongexternij-- * We provide a default instance working on rings.-- | Simple hairpin loops. If (i,j) pairs then, first, try to do a lookup of-- the exact sequence of the hairpin in a tabulated list. If this fails then,-- second, try length 3 hairpins and so on.hairpinBase::(FoldFunctionsa)=>TurnerTablesa->Primary->Int->Int->VU.VectorahairpinBaseTurner2004{..}inpij=VU.singletongowherego|pIJ==vpNP||l<3=zero|l<=6,Justv<-s`M.lookup`hairpinLookup=v|l==3=(hairpinL!l)-- .*. tAU -- apparantly not anymore according to NNDB|l>=31=(hairpinL!30).*.(hairpinMM!(pIJ,bI,bJ)).*.(calcLargeLoopl)|otherwise={-traceVal (show (i,j,pIJ,bI,bJ,hairpinL!l,tAU,hairpinMM!(pIJ,bI,bJ))) $ -}(hairpinL!l).*.(hairpinMM!(pIJ,bI,bJ))l=j-i-1s=assert(i>=0&&checkBoundsinpj)$[inp!k|k<-[i..j-1]]bI=inp!(i+1)bJ=inp!(j-1)pIJ=pairinpijtAU=calcTermAUtermAUpIJ{-# INLINE hairpinBase #-}-- | Extend a stem by another pair.stackBase::(FoldFunctionsa)=>TurnerTablesa->Primary->Tablea->Int->Int->VU.VectorastackBaseTurner2004{..}inptblij=VU.singleton$go(i+1,j-1)wherepIJ=pairinpijgo(k,l)|pIJ==vpNP||pKL==vpNP||isZerotE=zero|otherwise=tE.*.(stack!(pIJ,pKL))wherepKL=riapinpkltE=tbl!(k,l){-# INLINE stackBase #-}-- | These are special cases of interior loops. Short iloops, such as 1x2 loops-- are completely tabulated. Look each one up here.---- TODO needs 1-bulges as a special casetabbedInteriorLoopBase::(Showa,FoldFunctionsa)=>TurnerTablesa->Primary->Tablea->Int->Int->VU.VectoratabbedInteriorLoopBaseTurner2004{..}inpstrongij=reswhereres=VU.map(\(k,l)->(strong!(k,l)).*.(ftabbed(k-i-1,j-l-1)(k,l)))iliili=tabbedInteriorLoopIndicesijftabbed(di,dj)(k,l)|ds==0&&dl==1=bulgeL!1.*.stack!(pIJ,pKL)-- no termAU, since the helical stack continues (nndb)|ds==1&&dl==1=iloop1x1!(pIJ,pKL,bI,bJ)|di==1&&dj==2=iloop1x2!(pIJ,pKL,bI,bL,bJ)|di==2&&dj==1=iloop1x2!(pIJ,pKL,bJ,bI,bK)|ds==2&&dl==2=iloop2x2!(pIJ,pKL,bI,bK,bL,bJ)|ds==2&&dl==3=iloop2x3MM!(pIJ,bI,bJ).*.iloop2x3MM!(pKL,bL,bK).*.iloopL!5.*.niniowherepKL=riapinpklbK=inp!(k-1)bL=inp!(l+1)ds=mindidjdl=maxdidjpIJ=pairinpijbI=inp!(i+1)bJ=inp!(j-1){-# INLINE tabbedInteriorLoopBase #-}-- | A large number of iloops up to a total maximum loop size of 30 have to be-- checked. There are about 300 cases for j-i>>30. In addition, this loop does-- not like fusion very much and does many different lookups.---- TODO Any performance increase here should yield substantial improvements for-- the overall performance of folding algorithms.---- TODO performance improvement: Split mismatch calculation into its own table-- in the caller. Callee gets an additional argument.---- TODO didjs needs to go back into a *Limit function.largeInteriorLoopBase::(FoldFunctionsa)=>TurnerTablesa->Primary->Tablea->Int->Int->VU.VectoralargeInteriorLoopBaseTurner2004{..}inpstrongij=reswhereres=VU.map(\(di,dj)->ijmm.*.(strong!(i+di,j-dj)).*.(iloopL!(di+dj-2)).*.-- -2 because di,dj is total IL length +2(iloopMM!(riapinp(i+di)(j-dj),inp!(i+di-1),inp!(j-dj+1))).*.calcNiniomaxNinioninio(abs$di-dj))didjs-- NOTE NO FUSION! :-(-- TODO check this!didjs=interiorLoopDistancesij{-
didjs = traceVal (show (i,j)) $ VU.unfoldr (\(di,dj) ->
if di>maxc
then Nothing
else if di+dj>=nexc
then Just ((di,dj),(di+1,3 ))
else Just ((di,dj),(di ,dj+1))
) (3,5) -}-- constant{-
maxc = min 27 (j-i-16)
nexc = min 30 (j-i-13)
-}ijmm=iloopMM!(pIJ,bI,bJ)pIJ=pairinpijbI=inp!(i+1)bJ=inp!(j-1){-# INLINE largeInteriorLoopBase #-}-- | A bulge on the left side of the inner closing pair. 1-bulges are treated-- as tabulated interior loops.---- (..((...)))-- 01234567890bulgeLBase::(FoldFunctionsa)=>TurnerTablesa->Primary->Tablea->Int->Int->VU.VectorabulgeLBaseTurner2004{..}inpstrongij=reswhereres=VU.map(\k->strong!(i+1+k,j-1).*.bulgeL!k.*.calcTermAUtermAU(riapinp(i+1+k)(j-1)).*.tAUij).uncurryVU.enumFromN$bulgeLimitijtAUij=calcTermAUtermAU$pairinpij{-# INLINE bulgeLBase #-}-- | A bulge on the right side of the inner closing pair. Mirroring bulgeLBase---- ((~)...)bulgeRBase::(FoldFunctionsa)=>TurnerTablesa->Primary->Tablea->Int->Int->VU.VectorabulgeRBaseTurner2004{..}inpstrongij=reswhereres=VU.map(\k->strong!(i+1,j-1-k).*.bulgeL!k.*.calcTermAUtermAU(riapinp(i+1)(j-1-k)).*.tAUij).uncurryVU.enumFromN$bulgeLimitijtAUij=calcTermAUtermAU$pairinpij{-# INLINE bulgeRBase #-}-- | 1xn iloops work a bit like bulges. They are more complicated because we-- have to consider 'ninio' values and terminal mismatches.---- TODO how big an improvement would 'iloopL1xn' be with integrated 'ninio'-- values?---- (...(~).)interior1xnLBaseTurner2004{..}inpstrongij=reswhereres=VU.map(\k->strong!(i+1+k,j-2).*.iloopL!(k+1).*.iloop1xnMM!(riapinp(i+1+k)(j-2),inp!(i+k),inp!(j-1)).*.calcNiniomaxNinioninio(k-1).*.pIJmm).uncurryVU.enumFromN$iloop1xnLimitijpIJmm=iloop1xnMM!(pairinpij,inp!(i+1),inp!(j-1)){-# INLINE interior1xnLBase #-}-- | A mirror to the above.---- (.(~)...)interior1xnRBaseTurner2004{..}inpstrongij=reswhereres=VU.map(\k->strong!(i+2,j-1-k).*.iloopL!(k+1).*.iloop1xnMM!(riapinp(i+2)(j-1-k),inp!(j-k),inp!(i+1)).*.calcNiniomaxNinioninio(k-1).*.pIJmm).uncurryVU.enumFromN$iloop1xnLimitijpIJmm=iloop1xnMM!(pairinpij,inp!(i+1),inp!(j-1)){-# INLINE interior1xnRBase #-}-- | Close a multibranch loop by trying to combine one element of 'm' with one-- of 'm1'.---- <[[...]][[...]]>-- 0123456789012345-- 1multibranchCloseBase::(FoldFunctionsa)=>TurnerTablesa->Primary->Tablea->Tablea->Int->Int->VU.VectoramultibranchCloseBaseTurner2004{..}inpmm1ij=reswhereres=VU.map(\k->m!(i+1,k).*.m1!(k+1,j-1).*.ijmm.*.mbcl).uncurryVU.enumFromN$multibranchCloseLimitijijmm=multiMM!(pIJ,bJ,bI)mbcl=multiOffset.*.multiHelixpIJ=riapinpijbI=inp!(i+1)bJ=inp!(j-1){-# INLINE multibranchCloseBase #-}-- | Adds a multibranch loop at exactly (i,j).multibranchIJLoopBaseTurner2004{..}inpstrongij=reswhereres=VU.singleton$strong!(i,j).*.mbrhlx.*.mbrmmmbrhlx=multiHelixmbrmm=multiMM!(pIJ,bI,bJ)-- TODO correct orientation?pIJ=pairinpijbI=inp!(i-1)bJ=inp!(j+1){-# INLINE multibranchIJLoopBase #-}-- | Trivial function that adds a single unpaired nucleotide within a-- multibranch loop.multibranchUnpairedJBaseTurner2004{..}inpmtableij=reswhereres=VU.singleton$mtable!(i,j-1).*.mbrupmbrup=multiNuc{-# INLINE multibranchUnpairedJBase #-}-- | Adds a multibranch loop at (k,j), with k-i unpaired nucleotides to the-- left of 'k'.multibranchKJHelixBase::(FoldFunctionsa)=>TurnerTablesa->Primary->Tablea->Int->Int->VU.VectoramultibranchKJHelixBaseTurner2004{..}inpstrongij=reswhereres=VU.map(\k->multiNuc.^.(k-i).*.strong!(k,j).*.multiHelix.*.multiMM!(pairinpkj,inp!(k-1),inp!(j+1))).uncurryVU.enumFromN$multibranchKJHelixLimitij{-# INLINE multibranchKJHelixBase #-}-- |multibranchAddKJHelixBaseTurner2004{..}inptablestrongij=reswhereres=VU.map(\k->table!(i,k).*.strong!(k+1,j).*.multiMM!(pairinp(k+1)j,inp!k,inp!(j+1))).uncurryVU.enumFromN$multibranchAddKJHelixLimitij{-# INLINE multibranchAddKJHelixBase #-}-- | [[...]]-- 0123456externalLoopBaseTurner2004{..}inpstrongij=reswhereres=VU.singleton$strong!(i,j).*.mm.*.tAUn=snd$boundsinppIJ=pairinpijbI=inp!(i-1)bJ=inp!(j+1)tAU=calcTermAUtermAUpIJmm|i>0&&j<n=extMM!(pIJ,bI,bJ)|i>0=dangle5!(pIJ,bI)|j<n=dangle3!(pIJ,bJ)|otherwise=one{-# INLINE externalLoopBase #-}-- |externalAddLoopBase::(FoldFunctionsa)=>TurnerTablesa->Primary->Tablea->Tablea->Int->Int->VU.VectoraexternalAddLoopBasetrnr@Turner2004{..}inpstrongexternij=reswhereres=VU.map(\k->externalLoopOpttrnrinpstrongik.*.extern!(k+1,j)).uncurryVU.enumFromN$externalAddLoopLimitij{-# INLINE externalAddLoopBase #-}-- * Helper Functions.-- TODO We definitely need to check that this works!pair::Primary->Int->Int->ViennaPairpairinpij=assert(checkBoundsinpi&&checkBoundsinpj)$toPair(inp`unsafeIndex`i)(inp`unsafeIndex`j){-# INLINE pair #-}riapinpij=assert(i>=0&&j>=0&&checkBoundsinpi&&checkBoundsinpj)$toPair(inp!j)(inp!i){-# INLINE riap #-}ringSum::(Ringa,VU.Unboxa)=>VU.Vectora->aringSumv=VU.foldl'(.+.)zerov{- INLINE ringSum #-}ringSumL::(Ringa,VU.Unboxa)=>[a]->aringSumLv=foldl'(.+.)zerov{-# INLINE ringSumL #-}ringProduct::(Ringa,VU.Unboxa)=>VU.Vectora->aringProductv=VU.foldl'(.*.)onev{-# INLINE ringProduct #-}ringProductL::(Ringa,VU.Unboxa)=>[a]->aringProductLv=foldl'(.*.)onev-- | Explicit index generator for interior loops. Does not create indices for:-- - bulges 0 k-- - 1xn loops 1 k-- - 2x3 loops 2 3, 3 2-- - tabulated 1 1, 1 2, 2 1, 2 2interiorLoopDistancesij=VU.concatMap(\d->VU.map(\d'->(d',d-d'))-- written as a tuple$VU.enumFromN3(d-5))-- for each distance, all possible left/right combinations$VU.enumFromN8(min23(j-i-13))-- diagonal distance or number of unpaired nucleotides -2.{-# INLINE interiorLoopDistances #-}-- WTF ?! the function below is 10.000x slower than the function above!interiorLoopIndices::Int->Int->VU.Vector(Int,Int)interiorLoopIndices!i!j=VU.map(\(k,l)->(i+k,j-l))$interiorLoopDistancesij{-# INLINE interiorLoopIndices #-}---- TODO filter 'ili' to accept only indices that are not too close. DoestabbedInteriorLoopDistances::Int->Int->VU.Vector(Int,Int)tabbedInteriorLoopDistancesij|j-i>=8=VU.fromList[(0,1),(1,0),(1,1),(1,2),(2,1),(2,2),(2,3),(3,2)]|otherwise=VU.empty{-# INLINE tabbedInteriorLoopDistances #-}-- | Yes, therer is the (+1,+1) and yes, this is inconsistent with the other functiontabbedInteriorLoopIndicesij=VU.map(\(di,dj)->(i+di+1,j-dj-1))$tabbedInteriorLoopDistancesij{-# INLINE tabbedInteriorLoopIndices #-}-- * All limits depending on (i,j) should be here.-- | Bulge limitationsbulgeLimit::Int->Int->(Int,Int)bulgeLimitij=(2,min29$j-i-9){-# INLINE bulgeLimit #-}-- | 1xn iloop limitsiloop1xnLimit::Int->Int->(Int,Int)iloop1xnLimitij=(3,min26$j-i-10){-# INLINE iloop1xnLimit #-}-- | closing of a multibranch loopmultibranchCloseLimit::Int->Int->(Int,Int)multibranchCloseLimitij=(i+1,j-i-2)-- (i+7, j-i-14){-# INLINE multibranchCloseLimit #-}-- | add mb helixmultibranchAddKJHelixLimit::Int->Int->(Int,Int)multibranchAddKJHelixLimitij=(i+1,j-i-1){-# INLINE multibranchAddKJHelixLimit #-}-- | mb helixmultibranchKJHelixLimit::Int->Int->(Int,Int)multibranchKJHelixLimitij=(i,j-i){-# INLINE multibranchKJHelixLimit #-}-- | add external loopexternalAddLoopLimit::Int->Int->(Int,Int)externalAddLoopLimitij=(i+5,j-i-5){-# INLINE externalAddLoopLimit #-}