name = "bitvec"
language = "rust"
strategies = ["proptest", "quickcheck", "crabcheck", "hegel"]
crate = "bitvec"
base_commit = "7cf6f88738a9e4043b0c97291670ec1cd430219c"

[[tasks]]
mutations = ["split_at_mut_rejects_len_c71ea23_1"]

[tasks.source]
repo = "https://github.com/ferrilab/bitvec"
commits = ["c71ea2344fd4a728263f33d66a9708d93c52dc48"]
commit_subjects = ["fix split_at_mut as well"]
summary = "`BitSlice::split_at_mut` asserted `mid < self.len()`, rejecting the exact-length split that should return `(self, empty)`. The fix bumps the assertion to `mid <= self.len()`, mirroring `slice::split_at_mut`'s inclusive bound."

[tasks.injection]
kind = "patch"
files = ["src/slice/api.rs"]
locations = [{ file = "src/slice/api.rs" }]
patch = "patches/split_at_mut_rejects_len_c71ea23_1.patch"

[tasks.bug]
short_name = "split_at_mut_rejects_len"
invariant = "`split_at_mut(mid)` must accept `mid == self.len()`; the right half is simply empty."
how_triggered = "Reverts the `assert_in_bounds` bound from `0 ..= self.len()` back to `0 .. self.len()`, so splitting at the exact length panics instead of yielding `(self, empty)`."

[[tasks.tasks]]
property = "SplitAtMutAcceptsLen"
witnesses = [
  { test_fn = "witness_split_at_mut_accepts_len_case_empty_right" },
  { test_fn = "witness_split_at_mut_accepts_len_case_zero_len" },
]

[[tasks]]
mutations = ["vec_insert_rejects_end_8e48751_1"]

[tasks.source]
repo = "https://github.com/ferrilab/bitvec"
commits = ["8e48751508526cbe0c656f4dffa60cf4e6e4bfb0"]
commit_subjects = ["vec/api: fix insert for index == len"]
summary = "`BitVec::insert` bounded the index with `0 .. self.len()`, so calling `bv.insert(bv.len(), v)` — the push-equivalent case — panicked. The fix widens the bound to `0 ..= self.len()`, aligning with `Vec::insert` semantics."

[tasks.injection]
kind = "patch"
files = ["src/vec/api.rs"]
locations = [{ file = "src/vec/api.rs" }]
patch = "patches/vec_insert_rejects_end_8e48751_1.patch"

[tasks.bug]
short_name = "vec_insert_rejects_end"
invariant = "`BitVec::insert(self.len(), v)` must be equivalent to `push(v)` — appending a bit at the tail, not panicking."
how_triggered = "Reverts the inclusive upper bound so `assert_in_bounds(index, 0 .. self.len())` rejects the exact-length case, producing a panic on the push-equivalent call."

[[tasks.tasks]]
property = "VecInsertAcceptsEnd"
witnesses = [
  { test_fn = "witness_vec_insert_accepts_end_case_push_true" },
  { test_fn = "witness_vec_insert_accepts_end_case_empty_vec" },
]

[[tasks]]
mutations = ["leading_trailing_homogeneous_b08c4bd_1"]

[tasks.source]
repo = "https://github.com/ferrilab/bitvec"
commits = ["b08c4bdde959c172d5e77fe1746e903688b804e4"]
commit_subjects = ["Fix edge case for {leading,trailing}_{zero,one}"]
summary = "`leading_ones`/`trailing_ones`/`leading_zeros`/`trailing_zeros` called `unwrap_or_default()` on the underlying `find_bit_*` helpers, so an all-same-value slice (where the search finds no boundary bit) returned `0` instead of the slice length. The fix switches to `unwrap_or(self.len())`."

[tasks.injection]
kind = "patch"
files = ["src/slice/api.rs"]
locations = [{ file = "src/slice/api.rs" }]
patch = "patches/leading_trailing_homogeneous_b08c4bd_1.patch"

[tasks.bug]
short_name = "leading_trailing_homogeneous"
invariant = "For an N-bit slice whose bits are all `true` (resp. `false`), `leading_ones() == trailing_ones() == N` (resp. `…_zeros() == N`)."
how_triggered = "The `unwrap_or_else(|| self.len())` / `unwrap_or(len)` fallbacks on the `find_bit_{ones,zeros}` helpers are reverted to `unwrap_or_default()`, so the all-same-value case falls through to `0` instead of the slice length."

[[tasks.tasks]]
property = "LeadingTrailingFallback"
witnesses = [
  { test_fn = "witness_leading_trailing_fallback_case_all_ones" },
  { test_fn = "witness_leading_trailing_fallback_case_all_zeros" },
]

[[tasks]]
mutations = ["bitvec_partial_cmp_reversed_f3d5b43_1"]

[tasks.source]
repo = "https://github.com/ferrilab/bitvec"
commits = ["f3d5b43000f6e9a52a233bf07c295929cce66387"]
commit_subjects = ["Fix BitVec to BitSlice partial_cmp. Fixes #215"]
summary = "The blanket `PartialOrd<Rhs> for BitVec<T, O>` impl delegated via `other.partial_cmp(self.as_bitslice())` but forgot to reverse the result, so comparing a `BitVec` to a `BitSlice` returned the opposite ordering from the direct `BitVec: PartialOrd<BitVec>` impl. The fix adds `.map(|o| o.reverse())`."

[tasks.injection]
kind = "patch"
files = ["src/vec/traits.rs"]
locations = [{ file = "src/vec/traits.rs" }]
patch = "patches/bitvec_partial_cmp_reversed_f3d5b43_1.patch"

[tasks.bug]
short_name = "bitvec_partial_cmp_reversed"
invariant = "`bv.partial_cmp(bv.as_bitslice())` must match `bv.partial_cmp(&bv)` (and transitively, ordering between equal-content `BitVec`s and `BitSlice`s must agree)."
how_triggered = "Drops the `.map(|o| o.reverse())` on the `impl<T, O, Rhs> PartialOrd<Rhs> for BitVec<T, O>` blanket, so delegating through `other.partial_cmp(self.as_bitslice())` returns the inverted ordering relative to the direct `BitVec: PartialOrd<BitVec>` impl."

[[tasks.tasks]]
property = "BitvecPartialCmpMatches"
witnesses = [
  { test_fn = "witness_bitvec_partial_cmp_matches_case_lt" },
  { test_fn = "witness_bitvec_partial_cmp_matches_case_gt" },
]

[[tasks]]
mutations = ["clone_from_bitslice_src_bug_935cad8_1"]

[tasks.source]
repo = "https://github.com/ferrilab/bitvec"
commits = ["935cad8888d0f09837b95000c44f4a56579c7108"]
commit_subjects = ["Fix a bug in BitSlice::clone_from_bitslice"]
summary = "When the coerce fast path did not apply (different `T`/`O` parameters on source and destination), `clone_from_bitslice` fell through to a `self → self` zip that left `dst` unchanged instead of copying bits from `src`. The fix makes the fallback actually read from `src`."

[tasks.injection]
kind = "patch"
files = ["src/slice.rs"]
locations = [{ file = "src/slice.rs" }]
patch = "patches/clone_from_bitslice_src_bug_935cad8_1.patch"

[tasks.bug]
short_name = "clone_from_bitslice_src_bug"
invariant = "After `dst.clone_from_bitslice(&src)` (equal-length slices), every bit of `dst` must equal the corresponding bit of `src`."
how_triggered = "The non-coerce fallback branch is gutted to an identity loop (semantically equivalent to the pre-935cad8 `self->self` zip), so when the `<T, O>` and `<T2, O2>` parameters differ — defeating the coerce fast path — `dst` is left unchanged. The property uses heterogeneous `<u8, Lsb0>` / `<u16, Msb0>` to force the fallback."

[[tasks.tasks]]
property = "CloneFromBitsliceCopiesSrc"
witnesses = [
  { test_fn = "witness_clone_from_bitslice_copies_src_case_flip_all" },
  { test_fn = "witness_clone_from_bitslice_copies_src_case_alternating" },
]

[[tasks]]
mutations = ["octal_fmt_buffer_size_aeef0be_1"]

[tasks.source]
repo = "https://github.com/ferrilab/bitvec"
commits = ["aeef0be672538b8f6694f464bd947084813fb327"]
commit_subjects = ["Ensure correct buffer size during Octal::fmt"]
summary = "`Octal::fmt` preallocated a digit buffer of width `usize::BITS / 3` (floor) instead of `ceil(usize::BITS / 3)`, so each storage-element body chunk lost one octal digit. The fix adds `+ (usize::BITS % 3 != 0) as usize`, rounding up."

[tasks.injection]
kind = "patch"
files = ["src/slice/traits.rs"]
locations = [{ file = "src/slice/traits.rs" }]
patch = "patches/octal_fmt_buffer_size_aeef0be_1.patch"

[tasks.bug]
short_name = "octal_fmt_buffer_size"
invariant = "Formatting a `BitSlice` via the `Octal` trait must emit `ceil(bits / 3)` octal digits per storage-element body chunk. A `BitSlice<u64, _>` of 64 bits must emit 22 digits, not 21."
how_triggered = "The pre-allocated digit buffer width `W = D + (M != 0)` (with `D = usize::BITS / 3`, `M = usize::BITS % 3`) is reverted to `W = D`, dropping the ceiling. The per-chunk zip against the under-sized buffer truncates one octal digit from each 64-bit body chunk."

[[tasks.tasks]]
property = "OctalFmtNoPanic"
witnesses = [
  { test_fn = "witness_octal_fmt_no_panic_case_one_u64" },
  { test_fn = "witness_octal_fmt_no_panic_case_three_u64" },
]
