Changes in version 0.99.1 Changes in response to the Bioconductor review. - With three or more populations, each mixed pair's shrinkage weight now uses only that pair's evidence: w = N_ij / (n_i + n_j + N_ij), where n_i, n_j are the unambiguous alleles on the pair's two populations and N_ij its ambiguous heterozygotes. It previously divided by the variant's total evidence over all populations and pairs, so carriers of an unrelated population shrank an i/j split less than its own data warranted. Two-population results are unchanged (split_diploid() and every ancestry_split() call with two populations). - split_diploid() and split_diploid_multi(): the gla and arm_id arguments are replaced by shrink = FALSE. shrink = TRUE applies the same shrinkage toward 1/2 as ancestry_split(); the target is always 1/2, so a target matrix is no longer needed. - A bcftools error while reading the VCF (e.g. a malformed record) now stops with bcftools' message. Previously only a warning was raised and the variants read before the error were silently used. - ancestry_split(verbose = TRUE) now logs the mode and what it does, the ancestries detected in the MSP file, the number of ambiguous heterozygotes split fractionally (dosage mode), the VCF reader used, and how the alternate alleles were divided among the ancestries. - Bug fixes in the matrix input of ancestry_split_dosage(gt_matrix, pt_matrix). The VCF/MSP input and ancestry_split() were not affected. - Genotypes were split with the wrong samples' ancestry calls, because the ancestry matrix was passed to the C code without transposing it. - Variants and regions matched by name are now paired by name; they were paired by position, so a reordered pt_matrix gave wrong results. - Coordinate matching ("chr:pos" within "chr:start-end") now allows several variants per region, which previously gave an error. - counts are now per variant, as documented (they were per sample). - Unnamed matrices of different sizes no longer fail, and inputs with no variant overlap give a clear error. - Exported functions now check their arguments and give informative errors. - New defaults: split_diploid_multi() defaults to the two-population codes of split_diploid(); read_bed_file() finds the .bim and .fam files next to the .bed file; ancestry_split_phased() needs out_path only when write_vcf = TRUE. - ancestry_smmat() now requires ancestry_counts and variant_info together (previously one without the other was silently ignored). - GMMAT and SeqVarTools moved from Suggests to Imports, so ancestry_smmat() works right after installing lantern. SeqVarTools is needed because GMMAT::SMMAT() reads GDS files with it, but GMMAT only Suggests it. - The C code checks for user interrupts. - Long functions were split into smaller helpers and loops vectorized; results are unchanged. - Removed the unused global-ancestry helpers and the internal centromeres_hg38 table. - Vignettes rewritten for readability; the real-data vignette's source is no longer shipped in vignettes/. The Getting started vignette now explains the method's motivation with figures from the LANTERN preprint. - Added citation("lantern") for the preprint (Wang et al. 2026, medRxiv). Response to the Bioconductor review Each reviewer comment (in italics), the response, and where to find the change (paths relative to the package root; line numbers as of 0.99.1). - Please acknowledge the use of LLMs in the package and your ability to provide maintenance of any code generated by LLMs. I used Claude Code (Anthropic) as a coding assistant, for parts of the package infrastructure (the SeqArray VCF reader, the tests, argument checks and refactoring), for drafts of the vignettes, and for the changes in this revision. I have reviewed all of the code, understand it, and will maintain it. The same statement is in the "Use of AI tools" section at the end of the README. See README.md:558. - Vignettes: were LLMs used to write the vignettes? They have an odd tone. Yes, the earlier drafts were written with an LLM. I have rewritten all four vignettes in plainer language and edited them myself. See vignettes/lantern.Rmd, vignettes/toy-vcf-msp-example.Rmd, vignettes/split-intuition.Rmd, vignettes/real-data-chr19.Rmd. - Vignettes: ensure the target audience can follow them; explain the motivation before code, particularly in the toy example. Each section now explains what a step does and why before its code. Getting started opens with a short explanation of admixture and local ancestry and three figures from our preprint: which carriers are ambiguous without phase, how often rare alleles are mis-phased, and the power gained by splitting by ancestry. The toy example explains the VCF and MSP inputs, says what each step is for, and traces one sample's result by hand. See vignettes/lantern.Rmd:18-97 (introduction and figures in vignettes/figures/) and vignettes/toy-vcf-msp-example.Rmd:15-155. - Getting started: what is GENE2? The vignette now explains that the simulated trait depends on alternate alleles in GENE2 only when they are on African haplotypes, shows GENE2's variants and its row of the results, and compares it with a test that does not split by ancestry. See vignettes/lantern.Rmd:231 (GENE2's variants), :257 (its results) and :266 (the comparison). All in all, GENE2 is a good example to show LANTERN's utilitiy, which from the toy_genes.tsv. - Real data: what is 3 313 / 1 442? Thousands separated by a thin space; they now read 3,313 and 1,442. See vignettes/real-data-chr19.Rmd:35 and :67. - Real data: the genes_small paragraph belongs in the next section. Moved to where genes_small is first used. See vignettes/real-data-chr19.Rmd:81. - Real data: "Other ways to adapt this" should show code, and reads LLM-like. Replaced by runnable examples: testing all 1,442 genes with ancestry weights, using another SMMAT test (SKAT-O) through tests and p_col, the raw per-ancestry SMMAT output, and a three-population sketch (not evaluated, as the cohort has two ancestries). See vignettes/real-data-chr19.Rmd:183 (all genes), :212 (SKAT-O and raw output) and :261 (three populations). - Split intuition: format the denominator as an equation. The formulas are now equations, each also computed by hand in a code chunk and compared with the function's output; a table shows how each ancestry and genotype combination is split. See vignettes/split-intuition.Rmd:90 (table), :121 and :132 (two populations), :262 and :270 (K populations), :389 and :396 (shrinkage). - Toy example: cat() calls are not needed. Removed from all vignettes; results are shown by printing objects, with the explanation in the text. See vignettes/toy-vcf-msp-example.Rmd (no cat() remains in any vignette). - Package data: what is the purpose of the centromeres dataset? It was used by an earlier version of the unphased split, which shrank toward each chromosome arm's average ancestry. The package now shrinks toward 1/2 and no longer uses it, so the dataset and its helpers were removed. See the removed R/sysdata.rda and R/global_ancestry.R. - R code: message(), warning(), stop() should not use paste0(). Removed; pieces are passed as separate arguments, or with toString() / sprintf() for vectors. See for example R/ancestry_split.R:1031, :1258 and R/ancestry_smmat.R:35. - R code: break up long functions. Split into helpers. The longest was ancestry_split_dosage() at 320 lines; the longest is now 73 lines, and four are above 50, mostly argument checks and progress messages. Outputs were compared bit for bit before and after the refactor. This uncovered the ancestry_split_dosage() matrix-input bugs listed above. See the helpers at R/ancestry_split.R:769-815 (VCF and MSP parsing), :1001-1051 (ancestry codes), :1195-1309 (ancestry_split_dosage()) and R/ancestry_smmat.R:227-322 (ancestry_smmat()), and the regression tests at tests/testthat/test-ancestry_split.R:1112 and :1128. - R code: function arguments should have defaults if possible. Added where a sensible value exists (split_diploid_multi() codes, read_bed_file() .bim/.fam paths, ancestry_split_phased() out_path, and shrink = FALSE in split_diploid()/split_diploid_multi()). The arguments still without defaults are data inputs (file paths, genotype and ancestry matrices, phenotype table, kinship matrix, formula). See R/ancestry_split.R:111 (split_diploid()), :188 (split_diploid_multi()), :365 (read_bed_file()) and :1376 (ancestry_split_phased()). - R code: function arguments are tested for validity. All exported functions check their arguments before doing any work, with errors that name the argument (file paths, flags, matrix values and dimensions, population codes, and the ancestry_smmat() inputs before the null model is fitted). See R/checks.R:10-63 (shared checks), R/ancestry_smmat.R:227 (ancestry_smmat() inputs) and the tests in tests/testthat/test-checks.R. - R code: no unnecessary for loops. No for loops remain in the R code: loops over samples, variants and tracts are vectorized, and the short loops over populations use lapply()/vapply(). See R/ancestry_split.R:431 (MSP parsing), :743 (ancestry per variant), :827 (variant-to-tract mapping), :1321 (coordinate matching) and R/ancestry_smmat.R:55 (gene weights). - C code: does it check for user interrupts? It does now, every 1,024 variants (every 2^20 genotypes in loops over individual genotypes). The BED reader uses R_ToplevelExec() so it can close its file first. See src/ancestry.c:10-20 (helpers) and the checks at :66, :113, :276 (BED reader), :382, :540 and :616. - Other: remove the draft and .orig files in vignettes/. Removed. The real-data vignette needs cohort data that cannot be shared, so the package keeps only its pre-computed .Rmd; its source is kept outside the package, in the analysis repository. See vignettes/ (four .Rmd files and figures/ only). Changes in version 0.99.0 - Initial Bioconductor submission. - ancestry_split() splits a phased VCF into per-ancestry dosages using RFMix local ancestry, in "haplotype" (phased) or "dosage" (unphased) mode, for any number of populations. Dosage mode shrinks ambiguous mixed-ancestry splits toward 1/2, in proportion to the share of the variant's evidence that is ambiguous. (An earlier development version shrank toward each chromosome arm's global local ancestry; that target and the use_gla argument were removed.) - write_ancestry_gds() / write_dosage_gds() write SeqArray GDS files. - ancestry_smmat() runs ancestry-stratified GMMAT SMMAT gene tests and Cauchy-combines the per-ancestry p-values (cauchy_combine()). - VCFs are read with bcftools when it is on PATH, otherwise with SeqArray. Choose one explicitly with options(lantern.vcf_reader = ...). - Example dataset in inst/extdata (simulated; see inst/scripts/make_toy_data.R).