///|
/// L1-L4: Reordering Rules
///
/// These rules reorder characters for display based on resolved levels.
///|
/// L1: Reset levels of segment/paragraph separators and whitespace
fn reset_whitespace_levels(
original_classes : Array[BidiClass],
levels : Array[Int],
paragraph_level : Int,
) -> Unit {
let len = levels.length()
// L1: Reset levels for:
// - Segment separators (S)
// - Paragraph separators (B)
// - Any whitespace (WS) or isolate formatting characters preceding S or B
// - Any whitespace or isolate formatting at the end of the line
// First, handle trailing whitespace
// Note: X9-removed characters (LRE, RLE, LRO, RLO, PDF, BN) should be skipped
// when detecting trailing whitespace, as they are conceptually removed by X9
let mut i = len - 1
while i >= 0 {
let bc = original_classes[i]
match bc {
S | B => {
levels[i] = paragraph_level
// Reset preceding whitespace and isolates (skipping X9-removed chars)
let mut j = i - 1
while j >= 0 {
let prev_bc = original_classes[j]
match prev_bc {
WS | LRI | RLI | FSI | PDI => {
levels[j] = paragraph_level
j -= 1
}
// Skip X9-removed characters
LRE | RLE | LRO | RLO | PDF | BN => j -= 1
_ => break
}
}
i = j
}
WS | LRI | RLI | FSI | PDI => {
// Trailing whitespace at end of paragraph
levels[i] = paragraph_level
i -= 1
}
// Skip X9-removed characters when detecting trailing whitespace
LRE | RLE | LRO | RLO | PDF | BN => i -= 1
_ => break
}
}
// Also reset any S or B in the middle, plus preceding whitespace/isolates
for k = 0; k < len; k = k + 1 {
let bc = original_classes[k]
if bc is S || bc is B {
levels[k] = paragraph_level
// L1.3: Reset preceding whitespace and isolate formatting characters
// (skipping X9-removed characters)
let mut j = k - 1
while j >= 0 {
let prev_bc = original_classes[j]
match prev_bc {
WS | LRI | RLI | FSI | PDI => {
levels[j] = paragraph_level
j -= 1
}
// Skip X9-removed characters
LRE | RLE | LRO | RLO | PDF | BN => j -= 1
_ => break
}
}
}
}
}
///|
/// L2: Reorder text based on levels
/// Returns indices in visual order
fn apply_reordering(
indices : Array[Int],
levels : Array[Int],
paragraph_level : Int,
) -> Array[Int] {
let len = indices.length()
if len == 0 {
return []
}
// Initialize result with logical order after X9 removal.
let result = indices.copy()
// Find the maximum level in the remaining indices.
let mut max_level = paragraph_level
for idx in result {
let level = levels[idx]
if level > max_level {
max_level = level
}
}
// L2: From the highest level to the lowest odd level, reverse each
// contiguous sequence of characters at that level or higher.
let mut level = max_level
while level >= 1 {
let mut i = 0
while i < len {
if levels[result[i]] >= level {
let start = i
while i < len && levels[result[i]] >= level {
i += 1
}
reverse_range(result, start, i - 1)
} else {
i += 1
}
}
level -= 1
}
result
}
///|
/// Reverse a range in an array
fn reverse_range(arr : Array[Int], start : Int, end : Int) -> Unit {
let mut lo = start
let mut hi = end
while lo < hi {
let tmp = arr[lo]
arr[lo] = arr[hi]
arr[hi] = tmp
lo += 1
hi -= 1
}
}