///|
/// mutect_lite_demo — Mutect2 风格肿瘤-正常体细胞变异检出演示:
/// Part A: somatic SNV / germline / pop_af 的 LOD 检出 -> TMB ->
/// somatic VCF -> 突变签名 96 通道衔接;
/// Part B: indel 检出与简化推断的移位伪影说明.
///|
fn ml_demo_pick_base(rng : @src.Rng) -> String {
let i = rng.randint(4)
match i {
0 => "A"
1 => "C"
2 => "G"
_ => "T"
}
}
///|
fn ml_demo_other(base : String) -> String {
match base {
"A" => "T"
"C" => "G"
"G" => "A"
_ => "C"
}
}
///|
fn ml_demo_char_at(s : String, pos : Int32) -> String {
s.unsafe_get(pos).unsafe_to_char().to_string()
}
///|
fn ml_demo_replace(s : String, pos : Int32, b : String) -> String {
let head = if pos > 0 { s[:pos].to_owned() } else { "" }
let tail = if pos + 1 < s.length() { s[pos + 1:].to_owned() } else { "" }
head + b + tail
}
///|
fn ml_demo_insert(s : String, anchor : Int32, ins : String) -> String {
s[:anchor + 1].to_owned() + ins + s[anchor + 1:].to_owned()
}
///|
fn main {
let rng = @src.Rng::new(20261031UL)
let sb = StringBuilder()
for _i = 0; _i < 120; _i = _i + 1 {
sb.write_string(ml_demo_pick_base(rng))
}
let reference = sb.to_string()
println("1. 合成参考: 120bp (确定性 RNG seed=20261031)")
// ---- Part A: SNV / germline / pop_af ----
let snv1_pos : Int32 = 40
let snv1_alt = ml_demo_other(ml_demo_char_at(reference, snv1_pos))
let snv2_pos : Int32 = 90
let snv2_alt = ml_demo_other(ml_demo_char_at(reference, snv2_pos))
let germ_pos : Int32 = 70
let germ_alt = ml_demo_other(ml_demo_char_at(reference, germ_pos))
let pop_pos : Int32 = 30
let pop_alt = ml_demo_other(ml_demo_char_at(reference, pop_pos))
let tumor_reads : Array[@src.GatkRead] = Array(capacity=20)
for i = 0; i < 20; i = i + 1 {
let mut seq = reference
if i < 8 {
seq = ml_demo_replace(seq, snv1_pos, snv1_alt)
}
if i < 6 {
seq = ml_demo_replace(seq, snv2_pos, snv2_alt)
}
if i < 10 {
seq = ml_demo_replace(seq, germ_pos, germ_alt)
}
if i < 6 {
seq = ml_demo_replace(seq, pop_pos, pop_alt)
}
tumor_reads.push(@src.GatkRead::{ name: "t\{i}", seq, pos: 0, })
}
let normal_reads : Array[@src.GatkRead] = Array(capacity=20)
for i = 0; i < 20; i = i + 1 {
let seq = if i < 10 {
ml_demo_replace(reference, germ_pos, germ_alt)
} else {
reference
}
normal_reads.push(@src.GatkRead::{ name: "n\{i}", seq, pos: 0, })
}
let tumor = @src.MutectSample::{ name: "TUMOR", reads: tumor_reads, }
let normal = @src.MutectSample::{ name: "NORMAL", reads: normal_reads, }
println("2. Part A 注入: somatic SNV x2 + germline x1 + pop_af x1")
let pop_af : Map[String, Double] = Map([], capacity=0)
let pop_key = "chr1:" +
(pop_pos + 1).to_string() +
":" +
ml_demo_char_at(reference, pop_pos) +
">" +
pop_alt
pop_af.set(pop_key, 0.05)
let calls = @src.mutect_call("chr1", reference, tumor, normal, pop_af~)
println("3. Part A 检出 (pos ref>alt | tumor_af | lod_t | lod_n | filter):")
for v in calls {
println(
" \{v.pos} \{v.ref_allele}>\{v.alt} | \{v.tumor_af} | \{v.lod_t} | \{v.lod_n} | \{v.filter}",
)
}
let summary = @src.mutect_summary(calls, callable_mb=0.12)
println(
"4. \{summary.n_pass} (SNV=\{summary.n_snv}, indel=\{summary.n_indel}), TMB=\{summary.tmb_per_mb} /Mb",
)
println(@src.mutect_summary_line(summary))
let vcf = @src.mutect_write_vcf(calls, "TUMOR", "NORMAL")
println("5. somatic VCF (数据行, 共 \{vcf.length()} 字节):")
let mut shown = 0
for line in vcf.split("\n") {
if line != "" && line[0:1].to_owned() != "#" && shown < 4 {
println(" " + line.to_owned())
shown = shown + 1
}
}
let muts = @src.mutect_to_somatic_mutations(
reference,
calls,
sample_name="TUMOR",
)
let matrix = @src.build_mutation_matrix(muts, ["TUMOR"])
println(
"6. 突变签名衔接: PASS SNV \{muts.length()} 条进入 96 通道矩阵",
)
println(matrix.spectrum_summary())
// ---- Part B: indel 与移位伪影 ----
println(
"7. Part B: indel 检出(独立样本, 锚点 105 后插入 GGA, 6/20 条)",
)
let ins_anchor : Int32 = 105
let ins_tumor_reads : Array[@src.GatkRead] = Array(capacity=20)
for i = 0; i < 20; i = i + 1 {
let seq = if i < 6 {
ml_demo_insert(reference, ins_anchor, "GGA")
} else {
reference
}
ins_tumor_reads.push(@src.GatkRead::{ name: "t\{i}", seq, pos: 0, })
}
let ins_tumor = @src.MutectSample::{ name: "TUMOR", reads: ins_tumor_reads, }
let ins_calls = @src.mutect_call("chr1", reference, ins_tumor, normal)
println(" 检出 \{ins_calls.length()} 条候选, 其中插入类:")
for v in ins_calls {
if v.ref_allele.length() == 1 && v.alt.length() > 1 {
println(
" \{v.pos} \{v.ref_allele}>\{v.alt} | \{v.tumor_af} | \{v.filter}",
)
}
}
println(
" 注: 其余候选为插入读段下游移位产生的伪影 SNV(简化逐位点推断的",
)
println(
" 已知局限, Mutect2 以局部组装解决; 本模块以 SNV 优先清洗移除部分伪插入)",
)
println(
"演示完成: LOD 检出 -> 过滤 -> TMB -> VCF -> 突变签名闭环",
)
}