///|
pub(all) struct GenomicInterval {
start : Int
end : Int
} derive(Debug, Eq)
///|
pub fn make_interval(start : Int, end : Int) -> GenomicInterval? {
if start <= end && start > 0 {
Some({ start, end })
} else {
None
}
}
///|
pub fn interval_contains(interval : GenomicInterval?, position : Int) -> Bool {
match interval {
Some(value) => position >= value.start && position <= value.end
None => false
}
}
///|
pub fn interval_width(interval : GenomicInterval?) -> Int {
match interval {
Some(value) => value.end - value.start + 1
None => 0
}
}
///|
pub fn interval_overlaps(
left : GenomicInterval?,
right : GenomicInterval?,
) -> Bool {
match (left, right) {
(Some(a), Some(b)) => a.start <= b.end && b.start <= a.end
_ => false
}
}
///|
pub fn interval_intersection(
left : GenomicInterval?,
right : GenomicInterval?,
) -> GenomicInterval? {
match (left, right) {
(Some(a), Some(b)) =>
make_interval(max_int(a.start, b.start), min_int(a.end, b.end))
_ => None
}
}
///|
pub fn interval_merge(
intervals : Array[GenomicInterval?],
) -> Array[GenomicInterval?] {
let valid : Array[GenomicInterval] = []
for interval in intervals {
match interval {
Some(value) => valid.push(value)
None => ()
}
}
let result : Array[GenomicInterval?] = []
for current in valid {
if result.length() == 0 {
result.push(Some(current))
} else {
match result[result.length() - 1] {
Some(previous) if current.start <= previous.end + 1 => {
ignore(result.pop())
result.push(
Some({
start: previous.start,
end: max_int(previous.end, current.end),
}),
)
}
_ => result.push(Some(current))
}
}
}
result
}
///|
pub fn interval_subtract(
source : GenomicInterval?,
removed : GenomicInterval?,
) -> Array[GenomicInterval?] {
match (source, removed) {
(Some(a), Some(b)) if interval_overlaps(Some(a), Some(b)) => {
let pieces : Array[GenomicInterval?] = []
if a.start < b.start {
pieces.push(make_interval(a.start, b.start - 1))
}
if b.end < a.end {
pieces.push(make_interval(b.end + 1, a.end))
}
pieces
}
(Some(a), _) => [Some(a)]
_ => []
}
}
///|
pub fn interval_shift(
interval : GenomicInterval?,
offset : Int,
) -> GenomicInterval? {
match interval {
Some(value) => make_interval(value.start + offset, value.end + offset)
None => None
}
}
///|
pub fn interval_expand(
interval : GenomicInterval?,
padding : Int,
) -> GenomicInterval? {
match interval {
Some(value) =>
make_interval(max_int(1, value.start - padding), value.end + padding)
None => None
}
}
///|
pub fn interval_clip(
interval : GenomicInterval?,
lower : Int,
upper : Int,
) -> GenomicInterval? {
interval_intersection(interval, make_interval(lower, upper))
}
///|
pub fn interval_distance(
left : GenomicInterval?,
right : GenomicInterval?,
) -> Int {
match (left, right) {
(Some(a), Some(b)) if a.end < b.start => b.start - a.end - 1
(Some(a), Some(b)) if b.end < a.start => a.start - b.end - 1
_ => 0
}
}
///|
pub fn format_interval(interval : GenomicInterval?) -> String {
match interval {
Some(value) => "\{value.start}-\{value.end}"
None => "."
}
}
///|
fn min_int(left : Int, right : Int) -> Int {
if left < right {
left
} else {
right
}
}
///|
fn max_int(left : Int, right : Int) -> Int {
if left > right {
left
} else {
right
}
}