///|
pub(all) enum GenotypeClass {
Missing
Reference
Heterozygous
HomozygousAlternate
MultiAlternate
Invalid
} derive(Debug, Eq)
///|
pub(all) struct GenotypeCall {
alleles : Array[Int?]
phased : Bool
invalid : Bool
} derive(Debug, Eq)
///|
pub fn decode_genotype(raw : String) -> GenotypeCall {
if raw == "" || raw == "." {
{ alleles: [None], phased: false, invalid: false }
} else {
let phased = raw.contains("|")
let separator = if phased { "|" } else { "/" }
let parts = raw
.split(separator[:])
.map(fn(item) { item.to_owned() })
.to_array()
let alleles : Array[Int?] = []
let mut invalid = false
for part in parts {
if part == "." || part == "" {
alleles.push(None)
} else {
match parse_genotype_int(part) {
Some(index) if index >= 0 => alleles.push(Some(index))
_ => {
alleles.push(None)
invalid = true
}
}
}
}
{ alleles, phased, invalid }
}
}
///|
fn parse_genotype_int(raw : String) -> Int? {
if raw == "" {
None
} else {
let mut total = 0
for ch in raw {
if ch < '0' || ch > '9' {
return None
}
total = total * 10 + ch.to_int() - '0'.to_int()
}
Some(total)
}
}
///|
pub fn genotype_is_called(call : GenotypeCall) -> Bool {
!call.invalid &&
call.alleles.length() > 0 &&
call.alleles.all(fn(allele) { allele is Some(_) })
}
///|
pub fn genotype_alt_count(call : GenotypeCall) -> Int {
let mut count = 0
for allele in call.alleles {
match allele {
Some(index) if index > 0 => count = count + 1
_ => ()
}
}
count
}
///|
pub fn genotype_dosage(call : GenotypeCall) -> Int? {
if genotype_is_called(call) {
Some(genotype_alt_count(call))
} else {
None
}
}
///|
pub fn genotype_class(call : GenotypeCall) -> GenotypeClass {
if call.invalid {
GenotypeClass::Invalid
} else if !genotype_is_called(call) {
GenotypeClass::Missing
} else if genotype_alt_count(call) == 0 {
GenotypeClass::Reference
} else if call.alleles.length() == 2 && genotype_alt_count(call) == 1 {
GenotypeClass::Heterozygous
} else if call.alleles.length() == 2 &&
genotype_alt_count(call) == 2 &&
call.alleles[0] == call.alleles[1] {
GenotypeClass::HomozygousAlternate
} else {
GenotypeClass::MultiAlternate
}
}
///|
pub fn format_genotype(call : GenotypeCall) -> String {
let separator = if call.phased { "|" } else { "/" }
call.alleles
.map(fn(allele) {
match allele {
Some(index) => index.to_string()
None => "."
}
})
.join(separator)
}
///|
pub fn genotype_alt_indexes(call : GenotypeCall) -> Array[Int] {
let indexes : Array[Int] = []
for allele in call.alleles {
match allele {
Some(index) if index > 0 => indexes.push(index)
_ => ()
}
}
indexes
}
///|
pub fn genotype_has_allele(call : GenotypeCall, wanted : Int) -> Bool {
for allele in call.alleles {
if allele == Some(wanted) {
return true
}
}
false
}
///|
pub fn genotype_is_hom_ref(call : GenotypeCall) -> Bool {
genotype_class(call) == GenotypeClass::Reference
}
///|
pub fn genotype_is_het(call : GenotypeCall) -> Bool {
genotype_class(call) == GenotypeClass::Heterozygous
}
///|
pub fn genotype_is_hom_alt(call : GenotypeCall) -> Bool {
genotype_class(call) == GenotypeClass::HomozygousAlternate
}
///|
pub fn genotype_is_missing(call : GenotypeCall) -> Bool {
genotype_class(call) == GenotypeClass::Missing
}