diff --git a/src/cartesian_power.rs b/src/cartesian_power.rs index 3954a3e4c..0f5d53e60 100644 --- a/src/cartesian_power.rs +++ b/src/cartesian_power.rs @@ -144,12 +144,8 @@ where return Some((indices, items)); } // Keep yielding items list, incrementing indices rightmost first. - for index in indices.iter_mut().rev() { - *index += 1; - if *index < base { - return Some((indices, items)); - } - *index = 0; // Wrap and increment left. + if Self::inbounds_increment(indices, base) { + return Some((indices, items)); } // Iteration is over. // Mark a special index value to not fuse the iterator @@ -160,9 +156,166 @@ where } } + /// Increment indices, returning false in case of overflow. + fn inbounds_increment(indices: &mut [usize], base: usize) -> bool { + for index in indices.iter_mut().rev() { + *index += 1; + if *index < base { + return true; + } + *index = 0; // Wrap and increment left. + } + false + } + + /// Increment indices by n, returning false in case of (saturating) overflow. + fn inbounds_increment_by(n: usize, indices: &mut [usize], base: usize) -> bool { + let mut q = n; + for index in indices.iter_mut().rev() { + let s = *index + q; + q = s / base; + *index = s % base; + if q == 0 { + return true; + } + } + // Saturation requires a second pass to reset all indices. + for index in indices.iter_mut() { + *index = 0; + } + false + } + /// Same as [`increment_indices`], but does n increments at once. + /// The iterator is cycling, but `.nth()` does not 'wrap' + /// and 'saturates' to None instead. fn increment_indices_by_n(&mut self, n: usize) -> Option<(&[usize], &[I::Item])> { - todo!() + let Self { + pow, + iter, + items, + indices, + } = self; + + match (*pow, iter, &mut *items, n) { + // First iteration with degenerated 0th power. + (0, Some(_), items @ None, 0) => { + // Same as .next(). + self.iter = None; + let empty = items.insert(Vec::new()); + Some((indices, empty)) + } + (0, Some(_), None, _) => { + // Saturate. + self.iter = None; + None + } + + // Subsequent degenerated 0th power iteration. + // Same as `.next()`. + (0, None, items @ None, 0) => { + Some((indices, items.insert(Vec::new()))) + } + // Saturate. + (0, None, items, _) => { + *items = None; + None + } + + // First iterations in the general case. + // Possibly this will consume the entire underlying iterator, + // but we need to consume to check. + (pow, Some(it), items @ None, mut remaining) => { + if let Some(first) = it.next() { + // There is at least one element in the iterator, prepare collection + indices. + let items = items.insert(Vec::with_capacity(it.size_hint().0)); + items.push(first); + indices.reserve_exact(pow); + for _ in 0..pow { + indices.push(0); + } + // Collect more. + loop { + if remaining == 0 { + // Stop before collection completion. + indices[pow - 1] = n; // Hasn't wrapped yet. + return Some((indices, items)); + } + if let Some(next) = it.next() { + items.push(next); + remaining -= 1; + continue; + } + // Collection completed, but we need to go further. + self.iter = None; + let base = items.len(); + if Self::inbounds_increment_by(n, indices, base) { + return Some((indices, items)); + } + // Immediate saturation. + indices[0] = base; + return None; + } + } else { + // Degenerated iteration over an empty set. + self.iter = None; + None + } + } + + // Stable iteration in the degenerated case 'base = 0'. + (_, None, None, _) => { + None + } + + // Subsequent iteration in the general case. + // Again, immediate saturation is an option. + (pow, Some(it), Some(items), mut remaining) => { + if let Some(next) = it.next() { + items.push(next); + loop { + if remaining == 0 { + indices[pow - 1] += n + 1; // Hasn't wrapped yet. + return Some((indices, items)); + } + if let Some(next) = it.next() { + items.push(next); + remaining -= 1; + continue; + } + break; + } + } + // Collection completed. + self.iter = None; + let base = items.len(); + if Self::inbounds_increment_by(n + 1, indices, base) { + return Some((indices, items)); + } + // Saturate. + indices[0] = base; + None + } + + // Subsequent iteration in the general case + // after all items have been collected. + (_, None, Some(items), n) => { + let base = items.len(); + let shift = if indices[0] == base { + // Start over for a new round (already counted then). + indices[0] = 0; + 0 + } else { + 1 + }; + if Self::inbounds_increment_by(n + shift, indices, base) { + return Some((indices, items)); + } + // Immediate re-saturation. + indices[0] = base; + None + } + } } } @@ -244,7 +397,9 @@ mod tests { let mut it_exp = expected.iter(); let mut i = 0; loop { - match (it_exp.next(), it_act.next()) { + let act = it_act.next(); + println!(" {:?}", act); + match (it_exp.next(), act) { (Some(exp), Some(act)) => { if act != exp.chars().collect::>() { panic!( @@ -319,66 +474,312 @@ mod tests { #[test] fn nth() { fn check(origin: &str, pow: usize, expected: &[(usize, Option<&str>)]) { + println!("================== ({origin:?}^{pow})"); let mut it = origin.chars().cartesian_power(pow); let mut total_n = Vec::new(); - for r in 1..=3 { - for &(n, exp) in expected { - let act = it.nth(n); - total_n.push(n); - if act != exp.map(|s| s.chars().collect::>()) { - panic!( - "Failed nth({}) iteration (repetition {}) for {:?}^{}. \ - Expected {:?}, got {:?} instead.", - total_n - .iter() - .map(ToString::to_string) - .collect::>() - .join(", "), - r, - origin, - pow, - exp, - act - ); + for &(n, exp) in expected { + let act = it.nth(n); + let act = act.map(|v| v.into_iter().collect::()); + println!( + " → {}", + if let Some(act) = act.as_ref() { + act + } else { + "🗙" } + ); + total_n.push(n); + if act.as_ref().map(String::as_str) != exp { + panic!( + "Failed nth({}) iteration for {:?}^{}. \ + Expected {:?}, got {:?} instead.", + total_n + .iter() + .map(ToString::to_string) + .collect::>() + .join(", "), + origin, + pow, + exp, + act + ); } } } - // Check degenerated cases. - check("", 0, &[(0, Some("")), (0, None)]); - check("", 0, &[(0, Some("")), (1, None)]); - check("", 0, &[(0, Some("")), (2, None)]); - check("", 0, &[(1, None), (0, None)]); - check("", 0, &[(1, None), (1, None)]); - check("", 0, &[(1, None), (2, None)]); - check("", 0, &[(2, None), (0, None)]); - check("", 0, &[(2, None), (1, None)]); - check("", 0, &[(2, None), (2, None)]); - - check("a", 0, &[(0, Some("")), (0, None)]); - check("a", 0, &[(0, Some("")), (1, None)]); - check("a", 0, &[(0, Some("")), (2, None)]); - check("a", 0, &[(1, None), (0, None)]); - check("a", 0, &[(1, None), (1, None)]); - check("a", 0, &[(1, None), (2, None)]); - check("a", 0, &[(2, None), (0, None)]); - check("a", 0, &[(2, None), (1, None)]); - check("a", 0, &[(2, None), (2, None)]); + // Ease test read/write. + // Accept a sequence of ' ' yielded by a call to `.nth(n)`. + macro_rules! check { + ($base:expr, $pow:expr => $( $n:literal $expected:expr )+ ) => { + check($base, $pow, &[$(($n, $expected)),+]); + }; + } + + // Degenerated 0th power. + let o = None; + let e = Some(""); // "e"mpty. + + for base in ["", "a", "ab"] { + check!(base, 0 => 0 e 0 o 0 e 0 o); + check!(base, 0 => 0 e 1 o 0 e 1 o); + check!(base, 0 => 0 e 2 o 1 o 0 e); + check!(base, 0 => 1 o 0 e 0 o 1 o); + check!(base, 0 => 1 o 1 o 0 e 0 o); + check!(base, 0 => 1 o 2 o 0 e 1 o); + check!(base, 0 => 2 o 0 e 1 o 0 e); + check!(base, 0 => 2 o 1 o 2 o 0 e); + check!(base, 0 => 2 o 2 o 0 e 2 o); + } + + // Degenerated 0-base. + for pow in [1, 2, 3] { + check!("", pow => 0 o 0 o 0 o 0 o); + check!("", pow => 1 o 1 o 1 o 1 o); + check!("", pow => 2 o 2 o 2 o 2 o); + check!("", pow => 0 o 1 o 2 o 0 o); + check!("", pow => 2 o 1 o 0 o 1 o); + } // Unit power. - check("a", 1, &[(0, Some("a")), (0, None)]); - check("a", 1, &[(0, Some("a")), (1, None)]); - check("a", 1, &[(0, Some("a")), (2, None)]); - check("a", 1, &[(1, None), (0, None)]); - check("a", 1, &[(1, None), (1, None)]); - check("a", 1, &[(1, None), (2, None)]); - check("a", 1, &[(2, None), (0, None)]); - check("a", 1, &[(2, None), (1, None)]); - check("a", 1, &[(2, None), (2, None)]); - - check("ab", 1, &[(0, Some("a")), (0, Some("b")), (0, None)]); - check("ab", 1, &[(1, Some("b")), (0, None), (0, None)]); - check("ab", 1, &[(2, None), (0, None), (0, None)]); + let a = Some("a"); + let b = Some("b"); + let c = Some("c"); + + check!("a", 1 => 0 a 0 o 0 a 0 o 0 a 0 o); + check!("a", 1 => 1 o 1 o 1 o 1 o 1 o 1 o); + check!("a", 1 => 2 o 2 o 2 o 2 o 2 o 2 o); + check!("a", 1 => 0 a 1 o 0 a 1 o 0 a 1 o); + check!("a", 1 => 1 o 0 a 1 o 0 a 1 o 0 a); + check!("a", 1 => 0 a 2 o 0 a 2 o 0 a 2 o); + check!("a", 1 => 2 o 0 a 2 o 0 a 2 o 0 a); + check!("a", 1 => 1 o 2 o 1 o 2 o 1 o 2 o); + check!("a", 1 => 2 o 1 o 2 o 1 o 2 o 1 o); + check!("a", 1 => 0 a 1 o 2 o 0 a 1 o 2 o); + check!("a", 1 => 0 a 2 o 1 o 0 a 2 o 1 o); + check!("a", 1 => 1 o 0 a 2 o 1 o 0 a 2 o); + check!("a", 1 => 1 o 2 o 0 a 1 o 2 o 0 a 1 o 2 o 0 a); + check!("a", 1 => 2 o 0 a 1 o 2 o 0 a 1 o 2 o 0 a 1 o); + check!("a", 1 => 2 o 1 o 0 a 2 o 1 o 0 a 2 o 1 o 0 a); + check!("a", 1 => 1 o 0 a 0 o 1 o 0 a 0 o 1 o 0 a 0 o); + check!("a", 1 => 1 o 1 o 0 a 0 o 1 o 1 o 0 a 0 o 1 o 1 o); + + check!("ab", 1 => 0 a 0 b 0 o 0 a 0 b 0 o); + check!("ab", 1 => 1 b 1 o 1 b 1 o 1 b 1 o); + check!("ab", 1 => 2 o 2 o 2 o 2 o 2 o 2 o); + check!("ab", 1 => 0 a 1 o 0 a 1 o 0 a 1 o); + check!("ab", 1 => 1 b 0 o 1 b 0 o 1 b 0 o); + check!("ab", 1 => 0 a 2 o 0 a 2 o 0 a 2 o); + check!("ab", 1 => 2 o 0 a 2 o 0 a 2 o 0 a); + check!("ab", 1 => 1 b 2 o 1 b 2 o 1 b 2 o); + check!("ab", 1 => 2 o 1 b 2 o 1 b 2 o 1 b); + check!("ab", 1 => 0 a 1 o 2 o 0 a 1 o 2 o); + check!("ab", 1 => 0 a 2 o 1 b 0 o 2 o 1 b); + check!("ab", 1 => 1 b 0 o 2 o 1 b 0 o 2 o); + check!("ab", 1 => 1 b 2 o 0 a 1 o 2 o 0 a 1 o 2 o 0 a); + check!("ab", 1 => 2 o 0 a 1 o 2 o 0 a 1 o); + check!("ab", 1 => 2 o 1 b 0 o 2 o 1 b 0 o); + check!("ab", 1 => 1 b 0 o 0 a 1 o 0 a 0 b 1 o 0 a 0 b); + check!("ab", 1 => 1 b 1 o 0 a 0 b 1 o 1 b 0 o 0 a 1 o 1 b); + + check!("abc", 1 => 0 a 0 b 0 c 0 o 0 a 0 b 0 c 0 o); + check!("abc", 1 => 1 b 1 o 1 b 1 o 1 b 1 o); + check!("abc", 1 => 2 c 2 o 2 c 2 o 2 c 2 o); + check!("abc", 1 => 0 a 1 c 0 o 1 b 0 c 1 o 0 a 1 c); + check!("abc", 1 => 1 b 0 c 1 o 0 a 1 c 0 o 1 b 0 c); + check!("abc", 1 => 0 a 2 o 0 a 2 o 0 a 2 o); + check!("abc", 1 => 2 c 0 o 2 c 0 o 2 c 0 o); + check!("abc", 1 => 1 b 2 o 1 b 2 o 1 b 2 o); + check!("abc", 1 => 2 c 1 o 2 c 1 o 2 c 1 o); + check!("abc", 1 => 0 a 1 c 2 o 0 a 1 c 2 o); + check!("abc", 1 => 0 a 2 o 1 b 0 c 2 o 1 b); + check!("abc", 1 => 1 b 0 c 2 o 1 b 0 c 2 o); + check!("abc", 1 => 1 b 2 o 0 a 1 c 2 o 0 a 1 c 2 o 0 a); + check!("abc", 1 => 2 c 0 o 1 b 2 o 0 a 1 c 2 o 0 a 1 c); + check!("abc", 1 => 2 c 1 o 0 a 2 o 1 b 0 c 2 o 1 b 0 c); + check!("abc", 1 => 1 b 0 c 0 o 1 b 0 c 0 o 1 b 0 c 0 o); + check!("abc", 1 => 1 b 1 o 0 a 0 b 1 o 1 b 0 c 0 o 1 b 1 o); + + // Higher power. + let aa = Some("aa"); + let ab = Some("ab"); + let ac = Some("ac"); + let ba = Some("ba"); + let bb = Some("bb"); + let bc = Some("bc"); + let ca = Some("ca"); + let cb = Some("cb"); + let cc = Some("cc"); + + check!("a", 2 => 0 aa 0 o 0 aa 0 o 0 aa 0 o); + check!("a", 2 => 1 o 1 o 1 o 1 o 1 o 1 o); + check!("a", 2 => 2 o 2 o 2 o 2 o 2 o 2 o); + check!("a", 2 => 0 aa 1 o 0 aa 1 o 0 aa 1 o); + check!("a", 2 => 1 o 0 aa 1 o 0 aa 1 o 0 aa 1 o); + check!("a", 2 => 0 aa 2 o 0 aa 2 o 0 aa 2 o); + check!("a", 2 => 2 o 0 aa 2 o 0 aa 2 o 0 aa); + check!("a", 2 => 1 o 2 o 1 o 2 o 1 o 2 o); + check!("a", 2 => 2 o 1 o 2 o 1 o 2 o 1 o); + check!("a", 2 => 0 aa 1 o 2 o 0 aa 1 o 2 o); + check!("a", 2 => 0 aa 2 o 1 o 0 aa 2 o 1 o); + check!("a", 2 => 1 o 0 aa 2 o 1 o 0 aa 2 o); + check!("a", 2 => 1 o 2 o 0 aa 1 o 2 o 0 aa 1 o 2 o 0 aa); + check!("a", 2 => 2 o 0 aa 1 o 2 o 0 aa 1 o 2 o 0 aa 1 o); + check!("a", 2 => 2 o 1 o 0 aa 2 o 1 o 0 aa 2 o 1 o 0 aa); + check!("a", 2 => 1 o 0 aa 0 o 1 o 0 aa 0 o 1 o 0 aa 0 o); + check!("a", 2 => 1 o 1 o 0 aa 0 o 1 o 1 o 0 aa 0 o 1 o 1 o); + + check!("ab", 2 => 0 aa 0 ab 0 ba 0 bb 0 o 0 aa 0 ab); + check!("ab", 2 => 1 ab 1 bb 1 o 1 ab 1 bb 1 o); + check!("ab", 2 => 2 ba 2 o 2 ba 2 o 2 ba 2 o); + check!("ab", 2 => 0 aa 1 ba 0 bb 1 o 0 aa 1 ba); + check!("ab", 2 => 1 ab 0 ba 1 o 0 aa 1 ba 0 bb 1 o 0 aa 1 ba 0 bb); + check!("ab", 2 => 0 aa 2 bb 0 o 2 ba 0 bb 2 o 0 aa 2 bb); + check!("ab", 2 => 2 ba 0 bb 2 o 0 aa 2 bb 0 o 2 ba 0 bb); + check!("ab", 2 => 1 ab 2 o 1 ab 2 o 1 ab 2 o); + check!("ab", 2 => 2 ba 1 o 2 ba 1 o 2 ba 1 o); + check!("ab", 2 => 0 aa 1 ba 2 o 0 aa 1 ba 2 o); + check!("ab", 2 => 0 aa 2 bb 1 o 0 aa 2 bb 1 o); + check!("ab", 2 => 1 ab 0 ba 2 o 1 ab 0 ba 2 o); + check!("ab", 2 => 1 ab 2 o 0 aa 1 ba 2 o 0 aa 1 ba 2 o 0 aa); + check!("ab", 2 => 2 ba 0 bb 1 o 2 ba 0 bb 1 o 2 ba 0 bb 1 o); + check!("ab", 2 => 2 ba 1 o 0 aa 2 bb 1 o 0 aa 2 bb 1 o 0 aa); + check!("ab", 2 => 1 ab 0 ba 0 bb 1 o 0 aa 0 ab 1 bb 0 o 0 aa 1 ba 0 bb 0 o 1 ab 0 ba 0 bb); + check!("ab", 2 => 1 ab 1 bb 0 o 0 aa 1 ba 1 o 0 aa 0 ab 1 bb 1 o 0 aa 0 ab 1 bb 1 o); + + check!("abc", 2 => 0 aa 0 ab 0 ac 0 ba 0 bb 0 bc 0 ca 0 cb 0 cc 0 o 0 aa 0 ab 0 ac 0 ba); + check!("abc", 2 => 1 ab 1 ba 1 bc 1 cb 1 o 1 ab 1 ba 1 bc 1 cb 1 o 1 ab 1 ba 1 bc 1 cb 1 o); + check!("abc", 2 => 2 ac 2 bc 2 cc 2 o 2 ac 2 bc 2 cc 2 o 2 ac 2 bc 2 cc 2 o 2 ac 2 bc 2 cc); + check!("abc", 2 => 0 aa 1 ac 0 ba 1 bc 0 ca 1 cc 0 o 1 ab 0 ac 1 bb 0 bc 1 cb 0 cc 1 o); + check!("abc", 2 => 1 ab 0 ac 1 bb 0 bc 1 cb 0 cc 1 o 0 aa 1 ac 0 ba 1 bc 0 ca 1 cc 0 o); + check!("abc", 2 => 0 aa 2 ba 0 bb 2 cb 0 cc 2 o 0 aa 2 ba 0 bb 2 cb 0 cc 2 o 0 aa 2 ba); + check!("abc", 2 => 2 ac 0 ba 2 ca 0 cb 2 o 0 aa 2 ba 0 bb 2 cb 0 cc 2 o 0 aa 2 ba 0 bb); + check!("abc", 2 => 1 ab 2 bb 1 ca 2 o 1 ab 2 bb 1 ca 2 o 1 ab 2 bb 1 ca 2 o 1 ab 2 bb 1 ca); + check!("abc", 2 => 2 ac 1 bb 2 cb 1 o 2 ac 1 bb 2 cb 1 o 2 ac 1 bb 2 cb 1 o 2 ac 1 bb 2 cb); + check!("abc", 2 => 0 aa 1 ac 2 bc 0 ca 1 cc 2 o 0 aa 1 ac 2 bc 0 ca 1 cc 2 o 0 aa 1 ac); + check!("abc", 2 => 0 aa 2 ba 1 bc 0 ca 2 o 1 ab 0 ac 2 bc 1 cb 0 cc 2 o 1 ab 0 ac 2 bc); + check!("abc", 2 => 1 ab 0 ac 2 bc 1 cb 0 cc 2 o 1 ab 0 ac 2 bc 1 cb 0 cc 2 o 1 ab 0 ac); + check!("abc", 2 => 1 ab 2 bb 0 bc 1 cb 2 o 0 aa 1 ac 2 bc 0 ca 1 cc 2 o 0 aa 1 ac 2 bc); + check!("abc", 2 => 2 ac 0 ba 1 bc 2 cc 0 o 1 ab 2 bb 0 bc 1 cb 2 o 0 aa 1 ac 2 bc 0 ca); + check!("abc", 2 => 2 ac 1 bb 0 bc 2 cc 1 o 0 aa 2 ba 1 bc 0 ca 2 o 1 ab 0 ac 2 bc 1 cb); + check!("abc", 2 => 1 ab 0 ac 0 ba 1 bc 0 ca 0 cb 1 o 0 aa 0 ab 1 ba 0 bb 0 bc 1 cb 0 cc); + check!("abc", 2 => 1 ab 1 ba 0 bb 0 bc 1 cb 1 o 0 aa 0 ab 1 ba 1 bc 0 ca 0 cb 1 o 1 ab); + + let s = |s| Some(s); + + check!( + "abc", 3 => + 0 s("aaa") + 0 s("aab") + 0 s("aac") + 0 s("aba") + 0 s("abb") + 0 s("abc") + 0 s("aca") + 0 s("acb") + 0 s("acc") + 0 s("baa") + 0 s("bab") + 0 s("bac") + 0 s("bba") + 0 s("bbb") + 0 s("bbc") + 0 s("bca") + 0 s("bcb") + 0 s("bcc") + 0 s("caa") + 0 s("cab") + 0 s("cac") + 0 s("cba") + 0 s("cbb") + 0 s("cbc") + 0 s("cca") + 0 s("ccb") + 0 s("ccc") + 0 o + 0 s("aaa") + 0 s("aab") + 0 s("aac") + ); + + check!( + "abc", 3 => + 1 s("aab") + 1 s("aba") + 1 s("abc") + 1 s("acb") + 1 s("baa") + 1 s("bac") + 1 s("bbb") + 1 s("bca") + 1 s("bcc") + 1 s("cab") + 1 s("cba") + 1 s("cbc") + 1 s("ccb") + 1 o + 1 s("aab") + 1 s("aba") + ); + + check!( + "abc", 3 => + 2 s("aac") + 2 s("abc") + 2 s("acc") + 2 s("bac") + 2 s("bbc") + 2 s("bcc") + 2 s("cac") + 2 s("cbc") + 2 s("ccc") + 2 o + 2 s("aac") + 2 s("abc") + ); + + check!( + "abc", 3 => + 3 s("aba") 3 s("acb") 3 s("bac") 3 s("bca") 3 s("cab") 3 s("cbc") 3 o + 3 s("aba") 3 s("acb") 3 s("bac") 3 s("bca") 3 s("cab") 3 s("cbc") 3 o + ); + + check!( + "abc", 3 => + 4 s("abb") 4 s("baa") 4 s("bbc") 4 s("cab") 4 s("cca") 4 o + 4 s("abb") 4 s("baa") 4 s("bbc") 4 s("cab") 4 s("cca") 4 o + ); + + check!( + "abc", 3 => + 5 s("abc") 5 s("bac") 5 s("bcc") 5 s("cbc") 5 o + 5 s("abc") 5 s("bac") 5 s("bcc") 5 s("cbc") 5 o + ); + + check!("abc", 3 => + 6 s("aca") 6 s("bbb") 6 s("cac") 6 o + 6 s("aca") 6 s("bbb") 6 s("cac") 6 o + ); + + check!("abc", 3 => + 7 s("acb") 7 s("bca") 7 s("cbc") 7 o + 7 s("acb") 7 s("bca") 7 s("cbc") 7 o + ); + + check!( + "abc", 3 => + 8 s("acc") 8 s("bcc") 8 s("ccc") 8 o + 8 s("acc") 8 s("bcc") 8 s("ccc") 8 o + ); + + check!("abc", 3 => 9 s("baa") 9 s("cab") 9 o 9 s("baa") 9 s("cab") 9 o); + check!("abc", 3 => 10 s("bab") 10 s("cba") 10 o 10 s("bab") 10 s("cba") 10 o); + check!("abc", 3 => 11 s("bac") 11 s("cbc") 11 o 11 s("bac") 11 s("cbc") 11 o); + check!("abc", 3 => 12 s("bba") 12 s("ccb") 12 o 12 s("bba") 12 s("ccb") 12 o); + check!("abc", 3 => 13 s("bbb") 13 o 13 s("bbb") 13 o); + check!("abc", 3 => 14 s("bbc") 14 o 14 s("bbc") 14 o); + check!("abc", 3 => 25 s("ccb") 25 o 25 s("ccb") 25 o); + check!("abc", 3 => 26 s("ccc") 26 o 26 s("ccc") 26 o); + check!("abc", 3 => 27 o 27 o 27 o 27 o); + check!("abc", 3 => 28 o 28 o 28 o 28 o); } }