                       Changes in version 0.99.2                        

Documentation

  - README and the benchmark vignette now report the canonical benchmark
    run of 2026-10-08, measured on the full 25-file public ENCODE
    ATAC-seq corpus (single-file target ENCFF191LNL, 151,057,932
    alignments). The previous tables came from a much smaller early run
    and overstated some results: the GAlignments read speedup was shown
    as 4.03x against a measured 2.50x, and coverage to an in-memory
    RleList as 3.29x against 2.49x. Read-pattern speedups
    are 1.94-2.50x, ATAC fragment-size QC 4.39x end-to-end, and coverage
    to bigWig 1.52x.
  - Added the measured in-reader aggregation results to the README
    (5.47-7.06x over the standard Bioconductor routes; 3.45x over
    megadepth at matched core counts) and the memory characteristic of
    bam_coverage().
  - Documented the rename from BamScale and the distinction from the
    unrelated NCBI command-line tool BAMscale.
  - Installation instructions no longer advertise a Bioconductor install
    that does not yet work; BamScaleR is still under review.
  - Recorded two further limitations: sublinear intra-file thread
    scaling, and that object-free command-line decoders reach higher raw
    throughput.

                       Changes in version 0.99.1                        

Bug fixes

  - bam_coverage() and bam_coverage_bigwig() no longer allocate memory
    proportional to the genome declared in the BAM header. The coverage
    core previously built a dense per-base difference array for every
    header sequence before reading a single alignment, so a BAM carrying
    few alignments still cost several GB whenever its header declared a
    whole genome -- 12.6 GB for a 1.4 MB file with a GRCh38 header --
    and the collapse pass then walked every base of every contig.
    Coverage is now accumulated as per-thread interval endpoints and
    swept per contig, so memory and time scale with the alignments
    actually present rather than with the reference. Contigs without
    alignments cost nothing and still emit the correct full-length zero
    run.
    
    Measured on a 2.5 GB ATAC-seq BAM (195 contigs): peak RSS 14.2 GB
    -> 2.5 GB and 71.0 s -> 14.9 s at 24 threads. On a 1.4 MB BAM with a
    whole-genome header: 12.6 GB -> 0.55 GB (R's baseline) and 17.6 s
    -> 7.8 s. Output is unchanged and remains identical() to
    coverage(readGAlignments()) at every thread count.
    
    Removing the shared difference array also removes the atomic updates
    from the read loop, which is where most of the speedup comes from.

                       Changes in version 0.99.0                        

Package rename

  - The package has been renamed from BamScale to BamScaleR. The former
    name collided with the existing NCBI command-line tool BAMscale
    (https://github.com/ncbi/BAMscale; Pongor et al., Epigenetics &
    Chromatin 2020, doi:10.1186/s13072-020-00343-x), which performs a
    different task: normalised ("scaled") coverage-track generation and
    peak quantification. The rename removes that ambiguity.
  - No user-facing functionality changed. All exported functions keep
    their names, arguments and behaviour.
  - The option BamScale.ga_fastpath is now BamScaleR.ga_fastpath.
  - BamScale was accepted into Bioconductor devel on 4 May 2026 and was
    never part of a numbered Bioconductor release, so no deprecation
    cycle applies. BamScaleR is submitted as its renamed continuation
    and version numbering restarts at 0.99.0, as required for a new
    submission. The entries below document this package's history under
    its former name.

Earlier history (as BamScale)

The entries below document this package's development under its former
name, BamScale, before the rename described above. Those version numbers
belong to the former name's series: BamScale was accepted into
Bioconductor devel on 4 May 2026 and never shipped in a numbered
release, so version numbering restarts at 0.99.0 under the new name. The
code described below is carried forward into BamScaleR unchanged.

BamScale 0.99.14

New features

  - Four new exported functions compute common BAM summaries entirely
    inside the multithreaded C++ reader (per-thread accumulators folded
    in the OpenMP region; only the compact result crosses into R -- no
    per-read R objects are ever materialised), each verified
    byte-identical to its standard Bioconductor equivalent:
      - fragment_sizes(): paired-end fragment-size (insert-size)
        distribution; identical to table(abs(scanBam(isize))).
        drop_mate_unmapped = TRUE (default) replicates scanBam's isize =
        NA rule for mate-unmapped reads.
      - mapq_dist(): mapping-quality distribution; identical to
        table(scanBam(mapq)) (MAPQ 255 reported as 255, where scanBam
        uses NA).
      - bam_coverage(): per-base coverage as a SimpleRleList;
        identical() to
        GenomicAlignments::coverage(readGAlignments(...)), including the
        CIGAR conventions (M/=/X/D cover, N gaps, drop.D.ranges = FALSE)
        and the readGAlignments record filter (only unmapped reads
        dropped).
      - bam_coverage_bigwig(): single-pass BAM -> coverage bigWig
        written natively via the bundled libBigWig -- no R-side coverage
        object, no rtracklayer::export.bw round-trip. Data and zoom
        blocks are compressed across the OpenMP threads (parallel =
        TRUE, default); the parallel output is bit-for-bit identical to
        the serial writer. A compress_level argument exposes the zlib
        deflate level, and verbose = TRUE prints a per-phase timing
        breakdown.
  - bam_read(as = "GAlignments") now uses a dedicated fast path that
    assembles the GAlignments slots in C++: seqnames/strand accumulated
    as run-length pairs inside the parallel region, the cigar column
    materialised through a CHARSXP cache that exploits CIGAR-string
    redundancy, and qname decoded only when actually requested. Output
    is identical() to GenomicAlignments::readGAlignments() (verified
    at 226M-read scale) and to the previous construction path.
    options(BamScale.ga_fastpath = FALSE) restores the old path.
  - Vendored libBigWig 0.4.8 (MIT, Devon Ryan) under src/libBigWig/
    (built with -DNOCURL, local files only). Local modifications, each
    commented in bwWrite.c: a tunable zlib deflate level, deferred
    parallel block compression, and zoom-level construction from
    in-memory coverage runs instead of re-reading the just-written data
    section.

Performance

  - Fixed a quadratic reallocation pattern in the reader's batch merge:
    per-batch exact-capacity reserve() calls copied all accumulated data
    on every batch. Buffers now grow geometrically. This affects every
    read path -- e.g. on a 229M-read BAM at 48 threads, as =
    "GAlignments" dropped from ~300 s to ~56 s, and plain data-frame
    reads improved 2.8-4.4x.
  - CIGAR strings are formatted with an allocation-free digit writer
    instead of per-op std::to_string.
  - Representative large-file results (229M-read ATAC BAM, 48 threads,
    warm cache): bam_coverage() 7.2x vs coverage(readGAlignments());
    bam_coverage_bigwig() 2.6x vs megadepth at equal compression;
    fragment_sizes() 5.4x vs scanBam + table(); as = "GAlignments" 4.8x
    vs readGAlignments() (and faster even single-threaded).

Bug fixes

  - bam_coverage_bigwig() now works on Windows: the output stream is
    opened in binary mode ("w+b"; text mode corrupted the bigWig), and
    zoom levels are always built from the in-memory coverage runs
    instead of re-reading the just-written stream (Windows' C runtime is
    strict about write-to-read transitions on update streams; the
    in-memory path is byte-identical to the re-read path, verified
    at 226M-read scale). Vendored libBigWig diagnostics now go through
    REprintf(), clearing the compiled-code check NOTE.
  - Removed a long-standing ^src/Makevars$ entry from .Rbuildignore
    (dating to the pkgdown setup): built tarballs -- including the
    Bioconductor builds -- shipped without src/Makevars and therefore
    compiled WITHOUT OpenMP (and without the explicit -lz), silently
    running single-threaded when installed from a tarball. In-place R
    CMD INSTALL . was unaffected, which is why local installs always
    threaded. Tarball builds now carry the full build configuration
    (OpenMP, zlib, -DNOCURL, and the bundled libBigWig objects).

Benchmarks

  - run_workflow_benchmark.R gains first-class arms for the four new
    functions (--include-fastcov/-bigwig/-fragsize/-mapq), each behind
    an identical() correctness gate, with an optional megadepth context
    arm (--megadepth-bin).
  - run_server_benchmark.R: BiocParallel PSOCK clusters are now started
    and their namespaces pre-loaded outside the timed region in all
    multi-file arms (previously cluster spin-up was charged to every
    iteration, diluting comparator ratios at high worker counts); added
    --include-single so single and multi scenarios can run in separate
    stages.
  - New reproducible manuscript-benchmark tooling: select_encode_atac.R
    (ENCODE portal selection -> accession/md5 manifest), manifest-driven
    download_atac_data.R with md5 verification, samtools_reference.sh,
    merge_runs.R, and the staged run_manuscript_final.sh orchestrator.

Tests

  - New testthat gates: byte-identity of fragment_sizes() / mapq_dist()
    / bam_coverage() against their Rsamtools/GenomicAlignments
    equivalents; bam_coverage_bigwig() value-identity after re-import
    plus bit-for-bit parallel-vs-serial output; six GAlignments
    fast-path identity gates (threads 1/4, mcols order, use.names,
    flag/mapq filters, fast == slow, gctorture).

BamScale 0.99.13

New features

  - On the compatible read path, seq and qual are now built directly in
    C as DNAStringSet / PhredQuality objects from the packed BAM byte
    buffer (via the IRanges/XVector C-callable constructors), avoiding a
    character round-trip. Output remains byte-identical to
    Rsamtools::scanBam.
  - Added an end-to-end workflow benchmark harness under
    inst/benchmarks/ (coverage → bigWig and ATAC fragment-size QC) and a
    self-contained benchmark-results vignette.

Bug fixes

  - GAlignments output now carries the BAM-header sequence lengths in
    its Seqinfo. Previously the seqlengths were NA, so coverage()
    extended each seqlevel only to its maximum end and export.bw() wrote
    truncated chromosome sizes, diverging from
    GenomicAlignments::readGAlignments.
  - Force-load the C-callable providers (S4Vectors, IRanges, XVector,
    Biostrings) and GenomicAlignments in .onLoad, so the compatible
    seq/qual and GAlignments paths resolve their constructors and
    classes on fresh SnowParam SOCK workers.

Documentation

  - Refreshed the README with the latest benchmark results (read-pattern
    and end-to-end workflow speedups) and Bioconductor installation
    instructions.
