use crate::{
AUX_SLASH, Anl, CODE_CONT, CONT, Digits, Out, Span, blocks, build_block, contr_len,
fill_to_last, later_in_run, lead_run, scanthru, to_lead, trail_run, was, will,
};
const fn lut<const HAN: bool>() -> [u8; 64] {
let mut t = [8u8; 64]; t[0x10] = 0; t[0x20] = 1; t[0x00] = 2; t[0x06] = 10; t[0x01] = 3;
t[0x02] = 3; t[0x03] = 4; t[0x04] = 5; t[0x05] = 6; t[0x09] = 9; t[0x30] = if HAN { 11 } else { 2 }; t[0x31] = if HAN { 11 } else { 3 }; t[0x3A] = if HAN { 11 } else { 8 }; t[0x0F] = CODE_CONT;
t
}
const LUT_SEED: [u8; 64] = lut::<false>();
streams!(
Cls { lead, cont, u, l, c, mark, n, nl, sp, ws, oth, apo, letter, slash, han }
);
#[derive(Default)]
struct Carry {
nl_run: bool,
oth_edge: bool, letter_edge: bool, mark_oth_open: bool, prev_osf: bool,
prev_absorbed: bool, dig: Digits,
anl: Anl,
sfx_carry: u64, force: u64, sfx_end: usize, lc_open: bool, }
fn cls<const AUX: u8, const HAN: bool>(
text: &[u8],
tags: &[u8],
base: usize,
len: usize,
code: u8,
) -> (Cls, u8) {
let valid = if len == 64 { !0u64 } else { (1u64 << len) - 1 };
let (b, last_code) =
build_block::<AUX, true>(text, tags, base, len, &lut::<HAN>(), code, false);
let (p0, p1, p2, p3) = (b.p0, b.p1, b.p2, b.p3);
let low = !p3;
let a = low & !p2;
let w = low & p2;
let u = a & !p1 & !p0 & valid; let l = a & !p1 & p0;
let c = a & p1 & !p0;
let mark = p3 & p1 & !p0; let han = p3 & p1 & p0 & valid; let cls = Cls {
lead: valid & !b.cont,
cont: b.cont,
u,
l,
c,
mark,
n: a & p1 & p0,
nl: w & !p1 & !p0,
sp: w & !p1 & p0,
ws: w & !(p1 & p0), oth: p3 & !p1, apo: p3 & p0 & !p1, letter: u | l | c | mark,
slash: if AUX == AUX_SLASH { b.aux } else { 0 },
han,
};
(cls, last_code)
}
fn seed_at(tags: &[u8], base: usize) -> u8 {
if base == 0 {
return CODE_CONT;
}
let mut p = base - 1;
while p > 0 && tags[p] == CONT {
p -= 1;
}
LUT_SEED[tags[p] as usize]
}
pub(crate) fn later_streams<const AUX: u8, const HAN: bool>(
text: &[u8],
tags: &[u8],
later: &mut [u64],
) {
let ntext = text.len();
let nblk = ntext.div_ceil(64);
let (mut l_carry, mut cm_carry) = (false, false);
for bi in (0..nblk).rev() {
let base = bi * 64;
let len = (ntext - base).min(64);
let valid = if len == 64 { !0u64 } else { (1u64 << len) - 1 };
let (c, _) = cls::<AUX, HAN>(text, tags, base, len, seed_at(tags, base));
let (letter, l) = (c.letter, c.l);
let uc = letter & !l;
let cm = (c.c | c.mark) & letter;
let mut la = later_in_run(l, letter);
let mut ca = later_in_run(cm, uc);
if l_carry && letter >> (len - 1) & 1 != 0 {
la |= trail_run(letter, valid, len);
}
if cm_carry && uc >> (len - 1) & 1 != 0 {
ca |= trail_run(uc, valid, len);
}
later[bi] = la;
later[nblk + bi] = ca;
l_carry = letter & 1 != 0 && la & 1 != 0;
cm_carry = uc & 1 != 0 && ca & 1 != 0;
}
}
#[must_use]
pub(crate) fn run<const AUX: u8, const CONTR: bool, const DIGITS: usize, const HAN: bool>(
text: &[u8],
tags: &[u8],
starts: &mut [u64],
_flag: &mut [u64],
later: &mut [u64],
out: &mut [Span],
) -> usize {
let ntext = text.len();
if ntext == 0 {
return 0;
}
let nblk = ntext.div_ceil(64);
assert!(
tags.len() >= ntext
&& starts.len() >= nblk
&& later.len() >= 2 * nblk
&& out.len() >= ntext
);
later_streams::<AUX, HAN>(text, tags, later);
let mut cy = Carry::default();
blocks(
ntext,
&mut *starts,
None,
CODE_CONT,
|base, len, seed| cls::<AUX, HAN>(text, tags, base, len, seed),
|x, starts| {
let (pv, cur, bk, fw) = (&x.pv, &x.cur, &x.bk, &x.fw);
let (valid, len, lead) = (x.valid, x.len, x.cur.lead);
let tail_cls = cur.nl | cur.slash;
let mut oth = cur.oth;
let mut o_start;
let mut osf;
let mut mark_oth;
let (mut nl_e, mut nl_span);
loop {
let bk_oth = (oth << 1) | u64::from(cy.oth_edge);
let nl_m64 = (bk_oth & cur.nl & lead) | u64::from(cy.nl_run);
nl_e = scanthru(nl_m64 as u128, tail_cls as u128);
nl_span = fill_to_last(nl_m64 & tail_cls, tail_cls);
let o_prev = oth & !nl_span;
o_start = oth
& lead
& !nl_span & !((o_prev << 1) | u64::from(cy.oth_edge && !cy.prev_absorbed))
& !bk.sp;
osf = fill_to_last(
((o_start << 1) | u64::from(cy.prev_osf)) & cur.cont,
cur.cont,
) | o_start;
let contested = fill_to_last(
((o_prev << 1) | u64::from(cy.oth_edge && !cy.prev_absorbed)) & cur.mark & lead,
cur.mark,
);
let claimed = fill_to_last(
((osf << 1) | u64::from(cy.prev_osf)) & cur.mark & lead,
cur.mark,
);
mark_oth = contested & !claimed;
if cy.mark_oth_open {
mark_oth |= lead_run(cur.mark, valid);
}
let grown = cur.oth | mark_oth;
if grown == oth {
break;
}
oth = grown;
}
let letter = cur.letter & !mark_oth;
let bk_letter = (letter << 1) | u64::from(cy.letter_edge);
let ws_start = cur.ws & lead & !bk.ws;
let (steal, steal_patch) =
to_lead(cur.ws & !cur.nl & x.lb & !x.eof & !fw.ws, cur.cont, pv.cont);
let after_nl = bk.nl & cur.ws & lead & !later_in_run(cur.nl, cur.ws);
let l_start = letter
& lead
& !bk_letter
& !(bk.ws & !bk.nl)
& !((osf << 1) | u64::from(cy.prev_osf));
let apo_after = if CONTR { cur.apo & lead & bk_letter } else { 0 };
let mut sfx = cy.sfx_carry; let mut sfx_open = cy.force; cy.sfx_carry = 0;
cy.force = 0;
if CONTR {
let mut a = apo_after;
while a != 0 {
let j = a.trailing_zeros() as usize;
a &= a - 1;
let p = x.base + j;
if p == cy.sfx_end {
continue; }
let k = contr_len(text, p, true);
if k == 0 {
continue;
}
cy.sfx_end = p + k;
let hi = j + k;
let inb = if hi >= 64 { !0u64 } else { (1u64 << hi) - 1 };
sfx |= inb & !((1u64 << j) - 1);
if hi > 64 {
cy.sfx_carry |= (1u64 << (hi - 64)) - 1;
}
if p + k < ntext {
if hi < 64 {
sfx_open |= 1u64 << hi;
} else {
cy.force |= 1u64 << (hi - 64);
}
}
}
}
let lalt = letter & !sfx; let case_gate = cur.u & lead & bk_letter;
let uc = lalt & !cur.l; let lc = lalt & !cur.u; let cm = (cur.c | cur.mark) & lalt; let mm = ((cur.l & lalt) as u128) | u128::from(cy.lc_open);
let f = scanthru(mm, lc as u128);
let lt_in = (f as u64) & lalt;
cy.lc_open = f >> 64 != 0;
let lt_cut = if case_gate == 0 {
0
} else {
uc & lead
& ((cm << 1) | u64::from(was(pv.c) || was(pv.mark)))
& !later[nblk + x.bi]
& !later[x.bi]
};
let han_start = cur.han & lead & !bk.han;
let groups = cy.dig.starts(DIGITS, cur.n, lead, cur.cont, was(pv.n));
cy.anl.retract(starts, was(pv.ws), cur.ws, cur.nl, valid);
let mut st = groups
| l_start
| lt_in
| lt_cut
| han_start
| o_start
| ws_start
| after_nl
| steal;
st &= !nl_span;
st |= nl_e as u64;
st &= !sfx; st |= sfx_open;
cy.nl_run = nl_e >> 64 != 0;
cy.prev_absorbed = nl_span >> (len - 1) & 1 != 0;
cy.dig
.commit(DIGITS, groups, cur.n, lead, valid, len, will(x.nx.n));
let tws = trail_run(cur.ws, valid, len);
cy.anl.commit(
x.base,
after_nl,
tws,
nl_e as u64,
will(x.nx.ws),
was(pv.ws),
);
cy.prev_osf = osf >> (len - 1) & 1 != 0;
cy.oth_edge = oth >> (len - 1) & 1 != 0;
cy.letter_edge = letter >> (len - 1) & 1 != 0;
cy.mark_oth_open = mark_oth >> (len - 1) & 1 != 0;
Out {
st,
patch: steal_patch,
flag: 0,
}
},
);
crate::emit(starts, &[], nblk, ntext, out)
}