Browse Source

first commit

Thomas 1 year ago
commit
d42f363402
4 changed files with 1511 additions and 0 deletions
  1. 1 0
      .gitignore
  2. 716 0
      Cargo.lock
  3. 16 0
      Cargo.toml
  4. 778 0
      src/lib.rs

+ 1 - 0
.gitignore

@@ -0,0 +1 @@
+/target

+ 716 - 0
Cargo.lock

@@ -0,0 +1,716 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "aho-corasick"
+version = "1.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "anyhow"
+version = "1.0.83"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "25bdb32cbbdce2b519a9cd7df3a678443100e265d5e25ca763b7572a5104f5f3"
+
+[[package]]
+name = "autocfg"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
+
+[[package]]
+name = "average"
+version = "0.14.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c309b1c7fca12ebeec3ecba29ea917b3a4cb458ccf504df68bb4d8a0ca565a00"
+dependencies = [
+ "easy-cast",
+ "float-ord",
+ "num-traits",
+]
+
+[[package]]
+name = "bio-types"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9d45749b87f21808051025e9bf714d14ff4627f9d8ca967eade6946ea769aa4a"
+dependencies = [
+ "derive-new",
+ "lazy_static",
+ "regex",
+ "strum_macros",
+ "thiserror",
+]
+
+[[package]]
+name = "byteorder"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
+
+[[package]]
+name = "bzip2-sys"
+version = "0.1.11+1.0.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc"
+dependencies = [
+ "cc",
+ "libc",
+ "pkg-config",
+]
+
+[[package]]
+name = "cc"
+version = "1.0.97"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "099a5357d84c4c61eb35fc8eafa9a79a902c2f76911e5747ced4e032edd8d9b4"
+dependencies = [
+ "jobserver",
+ "libc",
+ "once_cell",
+]
+
+[[package]]
+name = "cmake"
+version = "0.1.50"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a31c789563b815f77f4250caee12365734369f942439b7defd71e18a48197130"
+dependencies = [
+ "cc",
+]
+
+[[package]]
+name = "crossbeam-deque"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d"
+dependencies = [
+ "crossbeam-epoch",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-epoch"
+version = "0.9.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
+dependencies = [
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-utils"
+version = "0.8.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345"
+
+[[package]]
+name = "curl-sys"
+version = "0.4.72+curl-8.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "29cbdc8314c447d11e8fd156dcdd031d9e02a7a976163e396b548c03153bc9ea"
+dependencies = [
+ "cc",
+ "libc",
+ "libz-sys",
+ "openssl-sys",
+ "pkg-config",
+ "vcpkg",
+ "windows-sys",
+]
+
+[[package]]
+name = "custom_derive"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef8ae57c4978a2acd8b869ce6b9ca1dfe817bff704c220209fdef2c0b75a01b9"
+
+[[package]]
+name = "derive-new"
+version = "0.5.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3418329ca0ad70234b9735dc4ceed10af4df60eff9c8e7b06cb5e520d92c3535"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "easy-cast"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "10936778145f3bea71fd9bf61332cce28c28e96a380714f7ab34838b80733fd6"
+dependencies = [
+ "libm",
+]
+
+[[package]]
+name = "either"
+version = "1.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b"
+
+[[package]]
+name = "env_logger"
+version = "0.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580"
+dependencies = [
+ "humantime",
+ "is-terminal",
+ "log",
+ "regex",
+ "termcolor",
+]
+
+[[package]]
+name = "float-ord"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ce81f49ae8a0482e4c55ea62ebbd7e5a686af544c00b9d090bba3ff9be97b3d"
+
+[[package]]
+name = "form_urlencoded"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
+dependencies = [
+ "percent-encoding",
+]
+
+[[package]]
+name = "fs-utils"
+version = "1.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6fc7a9dc005c944c98a935e7fd626faf5bf7e5a609f94bc13e42fc4a02e52593"
+dependencies = [
+ "quick-error",
+]
+
+[[package]]
+name = "glob"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
+
+[[package]]
+name = "heck"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
+
+[[package]]
+name = "hermit-abi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
+
+[[package]]
+name = "hts-sys"
+version = "2.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e9f348d14cb4e50444e39fcd6b00302fe2ed2bc88094142f6278391d349a386d"
+dependencies = [
+ "bzip2-sys",
+ "cc",
+ "curl-sys",
+ "fs-utils",
+ "glob",
+ "libz-sys",
+ "lzma-sys",
+ "openssl-sys",
+]
+
+[[package]]
+name = "humantime"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
+
+[[package]]
+name = "idna"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6"
+dependencies = [
+ "unicode-bidi",
+ "unicode-normalization",
+]
+
+[[package]]
+name = "ieee754"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9007da9cacbd3e6343da136e98b0d2df013f553d35bdec8b518f07bea768e19c"
+
+[[package]]
+name = "is-terminal"
+version = "0.4.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b"
+dependencies = [
+ "hermit-abi",
+ "libc",
+ "windows-sys",
+]
+
+[[package]]
+name = "jobserver"
+version = "0.1.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "lazy_static"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+
+[[package]]
+name = "libc"
+version = "0.2.153"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
+
+[[package]]
+name = "libm"
+version = "0.2.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058"
+
+[[package]]
+name = "libz-sys"
+version = "1.1.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5e143b5e666b2695d28f6bca6497720813f699c9602dd7f5cac91008b8ada7f9"
+dependencies = [
+ "cc",
+ "cmake",
+ "libc",
+ "pkg-config",
+ "vcpkg",
+]
+
+[[package]]
+name = "linear-map"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bfae20f6b19ad527b550c223fddc3077a547fc70cda94b9b566575423fd303ee"
+
+[[package]]
+name = "log"
+version = "0.4.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
+
+[[package]]
+name = "lzma-sys"
+version = "0.1.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5fda04ab3764e6cde78b9974eec4f779acaba7c4e84b36eca3cf77c581b85d27"
+dependencies = [
+ "cc",
+ "libc",
+ "pkg-config",
+]
+
+[[package]]
+name = "memchr"
+version = "2.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d"
+
+[[package]]
+name = "newtype_derive"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac8cd24d9f185bb7223958d8c1ff7a961b74b1953fd05dba7cc568a63b3861ec"
+dependencies = [
+ "rustc_version",
+]
+
+[[package]]
+name = "num-traits"
+version = "0.2.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
+dependencies = [
+ "autocfg",
+ "libm",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
+
+[[package]]
+name = "openssl-src"
+version = "300.2.3+3.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5cff92b6f71555b61bb9315f7c64da3ca43d87531622120fea0195fc761b4843"
+dependencies = [
+ "cc",
+]
+
+[[package]]
+name = "openssl-sys"
+version = "0.9.102"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2"
+dependencies = [
+ "cc",
+ "libc",
+ "openssl-src",
+ "pkg-config",
+ "vcpkg",
+]
+
+[[package]]
+name = "pandora_lib_pileup"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "average",
+ "env_logger",
+ "log",
+ "rayon",
+ "rust-htslib",
+]
+
+[[package]]
+name = "percent-encoding"
+version = "2.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
+
+[[package]]
+name = "pkg-config"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.82"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quick-error"
+version = "1.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
+
+[[package]]
+name = "quote"
+version = "1.0.36"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "rayon"
+version = "1.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa"
+dependencies = [
+ "either",
+ "rayon-core",
+]
+
+[[package]]
+name = "rayon-core"
+version = "1.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
+dependencies = [
+ "crossbeam-deque",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "regex"
+version = "1.10.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-automata",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56"
+
+[[package]]
+name = "rust-htslib"
+version = "0.46.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aec6f9ca4601beb4ae75ff8c99144dd15de5a873f6adf058da299962c760968e"
+dependencies = [
+ "bio-types",
+ "byteorder",
+ "custom_derive",
+ "derive-new",
+ "hts-sys",
+ "ieee754",
+ "lazy_static",
+ "libc",
+ "libz-sys",
+ "linear-map",
+ "newtype_derive",
+ "regex",
+ "thiserror",
+ "url",
+]
+
+[[package]]
+name = "rustc_version"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c5f5376ea5e30ce23c03eb77cbe4962b988deead10910c372b226388b594c084"
+dependencies = [
+ "semver",
+]
+
+[[package]]
+name = "rustversion"
+version = "1.0.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6"
+
+[[package]]
+name = "semver"
+version = "0.1.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d4f410fedcf71af0345d7607d246e7ad15faaadd49d240ee3b24e5dc21a820ac"
+
+[[package]]
+name = "strum_macros"
+version = "0.25.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0"
+dependencies = [
+ "heck",
+ "proc-macro2",
+ "quote",
+ "rustversion",
+ "syn 2.0.64",
+]
+
+[[package]]
+name = "syn"
+version = "1.0.109"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.64"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7ad3dee41f36859875573074334c200d1add8e4a87bb37113ebd31d926b7b11f"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "termcolor"
+version = "1.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
+name = "thiserror"
+version = "1.0.60"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "579e9083ca58dd9dcf91a9923bb9054071b9ebbd800b342194c9feb0ee89fc18"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.60"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2470041c06ec3ac1ab38d0356a6119054dedaea53e12fbefc0de730a1c08524"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.64",
+]
+
+[[package]]
+name = "tinyvec"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
+dependencies = [
+ "tinyvec_macros",
+]
+
+[[package]]
+name = "tinyvec_macros"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
+
+[[package]]
+name = "unicode-bidi"
+version = "0.3.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75"
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
+
+[[package]]
+name = "unicode-normalization"
+version = "0.1.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5"
+dependencies = [
+ "tinyvec",
+]
+
+[[package]]
+name = "url"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633"
+dependencies = [
+ "form_urlencoded",
+ "idna",
+ "percent-encoding",
+]
+
+[[package]]
+name = "vcpkg"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
+
+[[package]]
+name = "winapi-util"
+version = "0.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b"
+dependencies = [
+ "windows-sys",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
+dependencies = [
+ "windows-targets",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb"
+dependencies = [
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_gnullvm",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670"
+
+[[package]]
+name = "windows_i686_gnullvm"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"

+ 16 - 0
Cargo.toml

@@ -0,0 +1,16 @@
+[package]
+name = "pandora_lib_pileup"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+rust-htslib = "0.46.0"
+anyhow = "^1.0.75"
+average = "0.14.1"
+rayon = "1.9.0"
+log = "^0.4.20"
+env_logger = "^0.10.1"
+
+

+ 778 - 0
src/lib.rs

@@ -0,0 +1,778 @@
+use std::collections::HashMap;
+
+use anyhow::{anyhow, Context, Ok, Result};
+use log::info;
+use rayon::prelude::*;
+use rust_htslib::{
+    bam,
+    bam::{ext::BamRecordExtensions, record, Read, Record},
+};
+
+pub fn get_hts_nt_pileup(
+    bam: &mut rust_htslib::bam::IndexedReader,
+    chr: &str,
+    start: i32,
+    with_next_ins: bool,
+) -> Result<Vec<u8>> {
+    let stop = start + 1;
+    let mut bases = Vec::new();
+    bam.fetch((chr, start, stop))?;
+    let mut bam_pileup = Vec::new();
+    for p in bam.pileup() {
+        let pileup = p.context(format!(
+            "Can't pilup bam at position {}:{}-{}",
+            chr, start, stop
+        ))?;
+        let position = pileup.pos() as i32;
+        if position == start {
+            for alignment in pileup.alignments() {
+                match alignment.indel() {
+                    bam::pileup::Indel::Ins(_len) => bam_pileup.push(b'I'),
+                    bam::pileup::Indel::Del(_len) => bam_pileup.push(b'D'),
+                    _ => {
+                        let record = alignment.record();
+                        if record.seq_len() > 0 {
+                            if let Some(b) = hts_base_at(&record, start as u32, with_next_ins)? {
+                                bases.push(b);
+                            }
+                        } else {
+                            if alignment.is_del() {
+                                bases.push(b'D');
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+    Ok(bases)
+}
+
+pub fn hts_base_at(
+    record: &rust_htslib::bam::record::Record,
+    at_pos: u32,
+    with_next_ins: bool,
+) -> Result<Option<u8>> {
+    use rust_htslib::bam::record::Cigar;
+
+    let cigar = record.cigar();
+    let seq = record.seq();
+    let pos = cigar.pos() as u32;
+
+    let mut read_i = 0u32;
+    let at_pos = at_pos - 1;
+    let mut ref_pos = pos;
+    if ref_pos > at_pos {
+        return Ok(None);
+    }
+
+    for (id, op) in cigar.iter().enumerate() {
+        let (add_read, add_ref) = match *op {
+            Cigar::Match(len) | Cigar::Equal(len) | Cigar::Diff(len) => (len, len),
+            Cigar::Ins(len) => (len, 0),
+            Cigar::Del(len) => (0, len),
+            Cigar::RefSkip(len) => (0, len),
+            Cigar::SoftClip(len) => (len, 0),
+            Cigar::HardClip(_) | Cigar::Pad(_) => (0, 0),
+        };
+        // If at the end of the op len and next op is Ins return I
+        if with_next_ins && ref_pos + add_read == at_pos + 1 {
+            if let Some(Cigar::Ins(_)) = cigar.get(id + 1) {
+                return Ok(Some(b'I'));
+            }
+        }
+
+        if ref_pos + add_ref > at_pos {
+            // Handle deletions directly
+            if let Cigar::Del(_) = *op {
+                return Ok(Some(b'D'));
+            } else if let Cigar::RefSkip(_) = op {
+                return Ok(None);
+            } else {
+                let diff = at_pos - ref_pos;
+                let p = read_i + diff;
+                return Ok(Some(seq[p as usize]));
+            }
+        }
+
+        read_i += add_read;
+        ref_pos += add_ref;
+    }
+    Ok(None)
+}
+
+pub fn get_n_start(
+    bam: &mut rust_htslib::bam::IndexedReader,
+    chr: &str,
+    start: i32,
+    stop: i32,
+) -> Result<usize> {
+    bam.fetch((chr, start, stop))?;
+
+    // let mut start_positions = Vec::new();
+    let mut n_start = 0;
+    for read in bam.records() {
+        let record = read.context(format!("eRR"))?;
+        let rstart = record.pos() as i32;
+        if rstart >= start && rstart < stop {
+            if record.mapq() > 40 {
+                n_start += 1;
+            }
+            // start_positions.push(rstart);
+        }
+    }
+
+    Ok(n_start)
+    // Ok(start_positions.len())
+}
+
+pub fn get_n_start_end_qual(
+    bam: &mut rust_htslib::bam::IndexedReader,
+    chr: &str,
+    start: i32,
+    stop: i32,
+    mapq: u8,
+) -> Result<usize> {
+    bam.fetch((chr, start, stop))?;
+
+    // let mut start_positions = Vec::new();
+    let mut n_start = 0;
+    for read in bam.records() {
+        let record = read.context(format!("eRR"))?;
+        let rstart = record.pos() as i32;
+        let rend = record.reference_end() as i32;
+        if rstart >= start && rstart < stop || rend >= start && rend < stop {
+            if record.mapq() >= mapq {
+                n_start += 1;
+            }
+        }
+    }
+
+    Ok(n_start)
+}
+
+pub fn get_start_end_qual(
+    bam: &mut rust_htslib::bam::IndexedReader,
+    chr: &str,
+    start: i32,
+    stop: i32,
+    mapq: u8,
+) -> Result<Vec<i32>> {
+    bam.fetch((chr, start - 1, stop))?;
+    let length = stop - start;
+    let mut results = Vec::new();
+    results.resize(length as usize, 0);
+
+    for read in bam.records() {
+        let record = read.context(format!("Error while parsing record"))?;
+        let rstart = record.pos() as i32;
+        // let rstart = record.reference_start() as i32;
+        let rend = record.reference_end() as i32;
+
+        if rstart >= start && rstart < stop {
+            // println!("start {rstart}");
+            if record.mapq() >= mapq {
+                let index = rstart - start;
+                *results.get_mut(index as usize).unwrap() += 1;
+            }
+        }
+
+        if rend >= start && rend < stop {
+            // println!("{rend}");
+            if record.mapq() >= mapq {
+                let index = rend - start;
+                *results.get_mut(index as usize).unwrap() += 1;
+            }
+        }
+    }
+
+    Ok(results)
+}
+
+pub fn get_start_end_qual_rec(
+    bam: &mut rust_htslib::bam::IndexedReader,
+    chr: &str,
+    start: i32,
+    stop: i32,
+    mapq: u8,
+) -> Result<(Vec<Vec<Record>>, Vec<Vec<Record>>)> {
+    bam.fetch((chr, start - 1, stop))?;
+    let length = stop - start;
+    let mut results_start: Vec<Vec<Record>> = Vec::new();
+    results_start.resize(length as usize, vec![]);
+    let mut results_end: Vec<Vec<Record>> = Vec::new();
+    results_end.resize(length as usize, vec![]);
+
+    for read in bam.records() {
+        let record = read.context(format!("Error while parsing record"))?;
+        let rstart = record.pos() as i32;
+        let rend = record.reference_end() as i32;
+
+        if rstart >= start && rstart < stop {
+            if record.mapq() >= mapq {
+                let index = rstart - start;
+                let u = results_start.get_mut(index as usize).unwrap();
+                u.push(record.clone());
+            }
+        }
+
+        if rend >= start && rend < stop {
+            if record.mapq() >= mapq {
+                let index = rend - start;
+                let u = results_end.get_mut(index as usize).unwrap();
+                u.push(record.clone());
+            }
+        }
+    }
+    Ok((results_start, results_end))
+}
+
+pub fn primary_record(bam_path: &str, record: &Record) -> Option<Record> {
+    let mut bam = rust_htslib::bam::IndexedReader::from_path(bam_path).unwrap();
+    if record.is_supplementary() {
+        let qname = record.qname();
+        let mut positions: Vec<(&str, i32)> = Vec::new();
+        match record.aux(b"SA") {
+            std::result::Result::Ok(v) => match v {
+                record::Aux::String(v) => {
+                    positions = v
+                        .split(";")
+                        .filter(|s| s.len() != 0)
+                        .map(|s| {
+                            let s = s.split(",").take(2).collect::<Vec<&str>>();
+                            let chr = *s.get(0).unwrap();
+                            let position: i32 = s.get(1).unwrap().parse().unwrap();
+                            (chr, position)
+                        })
+                        .collect();
+                }
+                _ => (),
+            },
+            Err(_) => (),
+        };
+        for (chr, start) in positions {
+            bam.fetch((chr, start, start + 1)).unwrap();
+            let records = bam.records();
+            for record in records {
+                let record = record.unwrap();
+                // String::from_utf8(record.qname().to_vec()).unwrap().as_str()
+                if qname == record.qname() {
+                    if !record.is_supplementary() {
+                        return Some(record.clone());
+                    }
+                }
+            }
+        }
+    }
+    None
+}
+
+pub fn range_depths(
+    bam: &mut rust_htslib::bam::IndexedReader,
+    chr: &str,
+    start: i32,
+    stop: i32,
+) -> Result<Vec<u32>> {
+    bam.fetch((chr, start, stop))?;
+
+    let mut depths = vec![0];
+    depths.resize((stop - start) as usize, 0);
+    for p in bam.pileup() {
+        let pileup = p.context(format!("eRR"))?;
+        let rstart = pileup.pos() as i32;
+
+        if rstart >= start && rstart < stop {
+            let v = depths
+                .get_mut((rstart - start) as usize)
+                .context(format!("Errrr {}", rstart - start))?;
+            *v = pileup.depth();
+        } else if rstart >= stop {
+            break;
+        }
+    }
+
+    Ok(depths)
+}
+
+pub fn range_depths_qual(
+    bam: &mut rust_htslib::bam::IndexedReader,
+    chr: &str,
+    start: i32,
+    stop: i32,
+    mapq: u8,
+) -> Result<Vec<usize>> {
+    bam.fetch((chr, start, stop))?;
+
+    let mut depths = vec![0];
+    depths.resize((stop - start) as usize, 0);
+    for p in bam.pileup() {
+        let pileup = p.context(format!("eRR"))?;
+        let rstart = pileup.pos() as i32;
+
+        if rstart >= start && rstart < stop {
+            let v = depths
+                .get_mut((rstart - start) as usize)
+                .context(format!("Errrr {}", rstart - start))?;
+            *v = pileup
+                .alignments()
+                .filter(|e| e.record().mapq() >= mapq)
+                .count();
+        } else if rstart >= stop {
+            break;
+        }
+    }
+    Ok(depths)
+}
+
+pub fn range_len_qual(
+    bam: &mut rust_htslib::bam::IndexedReader,
+    chr: &str,
+    start: i32,
+    stop: i32,
+    mapq: u8,
+) -> Result<Vec<usize>> {
+    bam.fetch((chr, start, stop))?;
+
+    let mut depths = vec![0];
+    depths.resize((stop - start) as usize, 0);
+    for p in bam.pileup() {
+        let pileup = p.context(format!("eRR"))?;
+        let rstart = pileup.pos() as i32;
+
+        if rstart >= start && rstart < stop {
+            let v = depths
+                .get_mut((rstart - start) as usize)
+                .context(format!("Errrr {}", rstart - start))?;
+            *v = pileup
+                .alignments()
+                .filter(|e| e.record().mapq() >= mapq)
+                .map(|e| e.record().seq_len())
+                .sum();
+        } else if rstart >= stop {
+            break;
+        }
+    }
+    Ok(depths)
+}
+
+pub fn swap_by_primary(bam_path: &str, records: Vec<Record>) -> Vec<Record> {
+    let mut res_records = Vec::new();
+    for record in records {
+        if let Some(record) = primary_record(bam_path, &record) {
+            res_records.push(record);
+        } else {
+            res_records.push(record);
+        }
+    }
+    res_records
+}
+
+pub fn scan_sa(
+    bam: &mut rust_htslib::bam::IndexedReader,
+    chr: &str,
+    start: i32,
+    stop: i32,
+    mapq: u8,
+) -> Result<Vec<(Vec<Record>, Vec<Record>)>> {
+    bam.fetch((chr, start - 1, stop))?;
+    let length = stop - start;
+    let mut results_start: Vec<Vec<Record>> = Vec::new();
+    results_start.resize(length as usize, vec![]);
+    let mut results_end: Vec<Vec<Record>> = Vec::new();
+    results_end.resize(length as usize, vec![]);
+
+    for read in bam.records() {
+        let record = read.context(format!("Error while parsing record"))?;
+        let rstart = record.pos() as i32;
+        let rend = record.reference_end() as i32;
+
+        if rstart >= start && rstart < stop {
+            if record.mapq() >= mapq && record.aux(b"SA").is_ok() {
+                let index = rstart - start;
+                let u = results_start.get_mut(index as usize).unwrap();
+                u.push(record.clone());
+            }
+        }
+
+        if rend >= start && rend < stop && record.aux(b"SA").is_ok() {
+            if record.mapq() >= mapq {
+                let index = rend - start;
+                let u = results_end.get_mut(index as usize).unwrap();
+                u.push(record.clone());
+            }
+        }
+    }
+
+    let res: Vec<(Vec<Record>, Vec<Record>)> = results_start
+        .iter()
+        .zip(results_end.iter())
+        .map(|(s, e)| (s.to_owned(), e.to_owned()))
+        .collect();
+    Ok(res)
+}
+
+pub fn records_at_base(
+    bam: &mut rust_htslib::bam::IndexedReader,
+    chr: &str,
+    start: i32,
+    with_next_ins: bool,
+) -> Result<Vec<(Record, u8)>> {
+    let stop = start + 1;
+    let mut bases = Vec::new();
+    bam.fetch((chr, start, stop))?;
+    let mut bam_pileup = Vec::new();
+    for p in bam.pileup() {
+        let pileup = p.context(format!(
+            "Can't pilup bam at position {}:{}-{}",
+            chr, start, stop
+        ))?;
+        let position = pileup.pos() as i32;
+        if position == start {
+            for alignment in pileup.alignments() {
+                match alignment.indel() {
+                    bam::pileup::Indel::Ins(_len) => bam_pileup.push(b'I'),
+                    bam::pileup::Indel::Del(_len) => bam_pileup.push(b'D'),
+                    _ => {
+                        let record = alignment.record();
+                        if record.seq_len() > 0 {
+                            if let Some(b) = hts_base_at(&record, start as u32, with_next_ins)? {
+                                bases.push((record.clone(), b));
+                            }
+                        } else {
+                            if alignment.is_del() {
+                                bases.push((record.clone(), b'D'));
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+    Ok(bases)
+}
+
+pub fn qnames_at_base(
+    bam: &mut rust_htslib::bam::IndexedReader,
+    chr: &str,
+    start: i32,
+    with_next_ins: bool,
+) -> Result<Vec<(String, u8)>> {
+    let stop = start + 1;
+    let mut bases = Vec::new();
+    bam.fetch((chr, start, stop))?;
+    let mut bam_pileup = Vec::new();
+    for p in bam.pileup() {
+        let pileup = p.context(format!(
+            "Can't pilup bam at position {}:{}-{}",
+            chr, start, stop
+        ))?;
+        let position = pileup.pos() as i32;
+        if position == start {
+            for alignment in pileup.alignments() {
+                match alignment.indel() {
+                    bam::pileup::Indel::Ins(_len) => bam_pileup.push(b'I'),
+                    bam::pileup::Indel::Del(_len) => bam_pileup.push(b'D'),
+                    _ => {
+                        let record = alignment.record();
+                        let qname = String::from_utf8(record.qname().to_vec())?;
+                        if record.seq_len() > 0 {
+                            if let Some(b) = hts_base_at(&record, start as u32, with_next_ins)? {
+                                bases.push((qname, b));
+                            }
+                        } else {
+                            if alignment.is_del() {
+                                bases.push((qname, b'D'));
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+    Ok(bases)
+}
+
+fn mean(data: &[f64]) -> Option<f64> {
+    let sum = data.iter().sum::<f64>() as f64;
+    let count = data.len() as f64;
+
+    match count {
+        positive if positive > 0.0 => Some(sum / count),
+        _ => None,
+    }
+}
+
+fn std_deviation(data: &[f64]) -> Option<f64> {
+    match (mean(data), data.len() as f64) {
+        (Some(data_mean), count) if count > 0.0 => {
+            let variance = data
+                .iter()
+                .map(|value| {
+                    let diff = data_mean - (*value as f64);
+                    diff * diff
+                })
+                .sum::<f64>()
+                / count;
+
+            Some(variance.sqrt())
+        }
+        _ => None,
+    }
+}
+
+pub fn get_se_diag_mrd(
+    diag_bam_path: &str,
+    mrd_bam_path: &str,
+    chr: &str,
+    start: i32,
+    stop: i32,
+    mapq: u8,
+) -> Vec<Vec<Record>> {
+    let min_reads = 3;
+    let bin_size = 1_000;
+    let n_bins = stop - start;
+
+    let mut se_raw = vec![(0, 0)];
+    se_raw.resize(n_bins as usize, (0, 0));
+
+    let get_pos = |i: usize| -> i32 { start + (i as i32 * bin_size as i32) };
+    let se_raw: Vec<(i32, i32)> = se_raw
+        .par_chunks(bin_size)
+        .enumerate()
+        .flat_map(|(i, _)| {
+            let mut diag_bam = rust_htslib::bam::IndexedReader::from_path(diag_bam_path)
+                .context(anyhow!("Reading {}", diag_bam_path))
+                .unwrap();
+            let mut mrd_bam = rust_htslib::bam::IndexedReader::from_path(mrd_bam_path)
+                .context(anyhow!("Reading {}", mrd_bam_path))
+                .unwrap();
+
+            let s = get_pos(i);
+            let e = s + bin_size as i32;
+            let d = get_start_end_qual(&mut diag_bam, chr, s, e, mapq).unwrap();
+            let m = get_start_end_qual(&mut mrd_bam, chr, s, e, mapq).unwrap();
+
+            d.iter()
+                .zip(m.iter())
+                .map(|(d, m)| (*d, *m))
+                .collect::<Vec<(i32, i32)>>()
+        })
+        .collect();
+
+    info!("raw {}", se_raw.len());
+
+    let (diag_sum, mrd_sum) = se_raw
+        .iter()
+        .fold((0i32, 0i32), |acc, (d, m)| (acc.0 + d, acc.1 + m));
+
+    let diag = se_raw.iter().map(|(d, _)| *d as f64).collect::<Vec<f64>>();
+    let diag_mean = mean(&diag).unwrap();
+    let diag_std = std_deviation(&diag).unwrap();
+
+    let mrd = se_raw.iter().map(|(_, m)| *m as f64).collect::<Vec<f64>>();
+    let mrd_mean = mean(&mrd).unwrap();
+    let mrd_std = std_deviation(&mrd).unwrap();
+
+    info!(
+        "N/nt diag {} mrd {}",
+        diag_sum as f64 / (stop - start) as f64,
+        mrd_sum as f64 / (stop - start) as f64
+    );
+    info!("Mean diag {diag_mean} mrd {mrd_mean}");
+    info!("std dev diag {diag_std} mrd {mrd_std}");
+
+    let ratio_se: Vec<f64> = diag
+        .iter()
+        .zip(mrd.iter())
+        .map(|(d, m)| (((d / diag_mean) + 1.0) / ((m / mrd_mean) + 1.0)) - 1.0)
+        .collect();
+
+    let r_stddev = std_deviation(&ratio_se).unwrap();
+    info!("ratio mean {} std dev {r_stddev}", mean(&ratio_se).unwrap());
+
+    let all: Vec<_> = se_raw
+        .into_iter()
+        .enumerate()
+        .zip(ratio_se.iter())
+        .filter(|((_i, (d, m)), _r)| *d > 3 && *m == 0)
+        .collect();
+
+    info!("{} locations to assemble", all.len());
+
+    // getting priamry reads from bam at given positions
+    let se_data: Vec<(Vec<Record>, Vec<Record>)> = all
+        .par_chunks(100)
+        .flat_map(|chunks| {
+            // Loading bam reader.
+            let mut diag_bam = rust_htslib::bam::IndexedReader::from_path(diag_bam_path)
+                .context(anyhow!("Reading {}", diag_bam_path))
+                .unwrap();
+            chunks
+                .to_vec()
+                .iter()
+                .map(|((i, (_d, _m)), _r)| {
+                    let pos = *i as i32 + start;
+                    let (mut s, mut e) =
+                        get_start_end_qual_rec(&mut diag_bam, chr, pos, pos + 1, mapq).unwrap();
+                    let s = s.pop().unwrap();
+                    let s = swap_by_primary(diag_bam_path, s);
+                    let e = e.pop().unwrap();
+                    let e = swap_by_primary(diag_bam_path, e);
+                    // info!("{chr}:{pos}\t{r}\t{d} (s:{} e:{:?})\t{m}", s.len(), e.len());
+                    // println!("e {e:?}");
+                    // println!("s {s:?}");
+
+                    (s, e)
+                })
+                .collect::<Vec<(Vec<Record>, Vec<Record>)>>()
+        })
+        .collect();
+
+    // filtering by number of reads
+    // let mut groups: HashMap<String, Vec<Record>> = HashMap::new();
+    let mut res = Vec::new();
+    for (s, e) in se_data {
+        if s.len() >= min_reads {
+            res.push(s.clone());
+            // for r in s {
+            //     let k = format!("{}-{}", r.tid(), r.pos());
+            //     if let Some(vr) = groups.get_mut(&k) {
+            //         vr.push(r);
+            //         vr.dedup();
+            //     } else {
+            //         groups.insert(k, vec![r]);
+            //     }
+            // }
+        }
+        if e.len() >= min_reads {
+            res.push(e.clone());
+            // for r in e {
+            //     let k = format!("{}-{}", r.tid(), r.pos());
+            //     if let Some(vr) = groups.get_mut(&k) {
+            //         vr.push(r);
+            //         vr.dedup();
+            //     } else {
+            //         groups.insert(k, vec![r]);
+            //     }
+            // }
+        }
+    }
+    res
+}
+
+pub fn sa_ratio(
+    diag_bam_path: &str,
+    mrd_bam_path: &str,
+    chr: &str,
+    start: i32,
+    stop: i32,
+    mapq: u8,
+    bin_size: usize,
+) -> Vec<(f32, f32)> {
+    let n_bins = stop - start;
+
+    let get_pos = |i: usize| -> i32 { start + (i as i32 * bin_size as i32) };
+    let mut se_raw = vec![(0, 0)];
+    se_raw.resize(n_bins as usize, (0, 0));
+
+    se_raw
+        .par_chunks(bin_size)
+        .enumerate()
+        .flat_map(|(i, _)| {
+            let mut diag_bam = rust_htslib::bam::IndexedReader::from_path(diag_bam_path)
+                .context(anyhow!("Reading {}", diag_bam_path))
+                .unwrap();
+            let mut mrd_bam = rust_htslib::bam::IndexedReader::from_path(mrd_bam_path)
+                .context(anyhow!("Reading {}", mrd_bam_path))
+                .unwrap();
+
+            let s = get_pos(i);
+            let e = s + bin_size as i32;
+            let d = scan_sa(&mut diag_bam, chr, s, e, mapq).unwrap();
+            let m = scan_sa(&mut mrd_bam, chr, s, e, mapq).unwrap();
+
+            d.iter()
+                .zip(m.iter())
+                .map(|(d, m)| {
+                    let start_diff: f32 = d.0.len() as f32 / m.0.len() as f32;
+                    let end_diff: f32 = d.1.len() as f32 / m.1.len() as f32;
+                    (start_diff, end_diff)
+                })
+                .collect::<Vec<(f32, f32)>>()
+        })
+        .collect()
+}
+
+#[cfg(test)]
+mod tests {
+    use env_logger::Env;
+    use log::info;
+
+    // Note this useful idiom: importing names from outer (for mod tests) scope.
+    use super::*;
+
+    fn init() {
+        let _ = env_logger::Builder::from_env(Env::default().default_filter_or("info"))
+            .is_test(true)
+            .try_init();
+    }
+
+    #[test]
+    fn se_diff() {
+        init();
+        let case = "MERY";
+        let chr = "chr7";
+        let start = 144_103_157;
+        let stop = 144_183_195;
+        let mapq = 50;
+        let r = get_se_diag_mrd(
+            &format!("/data/longreads_basic_pipe/{case}/diag/{case}_diag_hs1.bam"),
+            &format!("/data/longreads_basic_pipe/{case}/mrd/{case}_mrd_hs1.bam"),
+            chr,
+            start,
+            stop,
+            mapq,
+        );
+        println!("{r:?}");
+    }
+
+    #[test]
+    fn test_se() {
+        let bam_path = "/data/longreads_basic_pipe/CAMARA/diag/CAMARA_diag_hs1.bam";
+        let chr = "chr9";
+        let start = 21839796;
+        let mapq = 50;
+
+        let mut bam = rust_htslib::bam::IndexedReader::from_path(bam_path).unwrap();
+
+        // let a = get_start_end_qual(&mut bam, chr, start, start + 1, mapq).unwrap();
+        // println!("{a:?}");
+
+        let res = get_start_end_qual_rec(&mut bam, chr, start, start + 1, mapq).unwrap();
+        let (_, e) = res;
+        let res_records = swap_by_primary(bam_path, e.get(0).unwrap().clone());
+        println!("{res_records:?}");
+        println!("{}", res_records.len());
+        assert!(res_records.len() == 12);
+    }
+
+    #[test]
+    fn sa_r() {
+        init();
+        let case = "MERY";
+        let chr = "chr7";
+        let start = 144_153_157;
+        let stop = 144_153_195;
+        let mapq = 50;
+        let r = sa_ratio(
+            &format!("/data/longreads_basic_pipe/{case}/diag/{case}_diag_hs1.bam"),
+            &format!("/data/longreads_basic_pipe/{case}/mrd/{case}_diag_hs1.bam"),
+            chr,
+            start,
+            stop,
+            mapq,
+            1_000
+        );
+        println!("{r:?}");
+    }
+}