///|
/// A stronger shard card that can carry multiple parity rows.
///
/// `index < data_count` is a data card. `index >= data_count` is a parity
/// card, and `index - data_count` is the parity row number.
pub(all) struct ResilientShardCard {
deck : String
index : Int
data_count : Int
parity_count : Int
width : Int
original_len : Int
checksum : Int
cells : Array[Int]
} derive(Eq, @debug.Debug)
///|
/// Recovery result for the multi-parity card format.
pub(all) enum ResilientRecovery {
ResilientRestored(Array[Int])
ResilientNeedCards(Array[Int])
ResilientDamaged(String)
} derive(Eq, @debug.Debug)
///|
/// Parse result for one rendered `RSN1|...` card line.
pub(all) enum CardParse {
ParsedCard(ResilientShardCard)
InvalidCard(String)
} derive(Eq, @debug.Debug)
///|
/// Parse result for a sheet that may contain comments, blank lines, and cards.
pub(all) struct SheetParseReport {
cards : Array[ResilientShardCard]
ignored_lines : Int
errors : Array[String]
} derive(Eq, @debug.Debug)
///|
/// A deterministic plan used by the planner and documentation examples.
pub(all) struct ResilientPlan {
deck : String
payload_len : Int
data_count : Int
parity_count : Int
width : Int
total_cards : Int
recoverable_missing_data_cards : Int
estimated_symbols : Int
} derive(Eq, @debug.Debug)
///|
/// Convert an ASCII string into byte cells. Non-ASCII characters are replaced
/// with the byte value for `?`, so the helper stays deterministic everywhere.
pub fn ascii_payload(text : String) -> Array[Int] {
let out : Array[Int] = []
for ch in text.iter() {
let code = ch.to_int()
if code >= 0 && code <= 127 {
out.push(code)
} else {
out.push(63)
}
}
out
}
///|
/// Convert byte cells back to a printable ASCII string.
pub fn payload_to_ascii(payload : Array[Int]) -> String {
let chars : Array[Char] = []
for value in payload {
let byte = normalize_byte(value)
let printable = if byte >= 32 && byte <= 126 { byte } else { 46 }
match printable.to_char() {
Some(ch) => chars.push(ch)
None => chars.push('?')
}
}
String::from_array(chars)
}
///|
/// Build a practical plan from a payload length and target card width.
pub fn plan_resilient_deck(
deck : String,
payload_len : Int,
target_width : Int,
max_data_cards : Int,
parity_count : Int,
) -> ResilientPlan {
let clean_payload_len = max_int(0, payload_len)
let clean_target_width = clamp(target_width, 1, 96)
let clean_max_data_cards = clamp(max_data_cards, 2, 32)
let clean_parity_count = clamp(parity_count, 1, 8)
let needed_data_cards = max_int(
2,
ceil_div(clean_payload_len, clean_target_width),
)
let data_count = min_int(clean_max_data_cards, needed_data_cards)
let width = max_int(1, ceil_div(clean_payload_len, data_count))
{
deck,
payload_len: clean_payload_len,
data_count,
parity_count: clean_parity_count,
width,
total_cards: data_count + clean_parity_count,
recoverable_missing_data_cards: clean_parity_count,
estimated_symbols: (data_count + clean_parity_count) * width,
}
}
///|
/// Build data cards plus `parity_count` parity cards.
///
/// Parity rows use a small systematic Reed-Solomon-style construction over
/// GF(256). The first parity row is compatible with XOR parity, while later
/// rows provide independent equations for multi-card recovery.
pub fn weave_resilient(
deck : String,
payload : Array[Int],
data_count : Int,
parity_count : Int,
) -> Array[ResilientShardCard] {
let clean_data_count = clamp(data_count, 2, 32)
let clean_parity_count = clamp(parity_count, 1, 8)
let width = max_int(1, ceil_div(payload.length(), clean_data_count))
let rows : Array[Array[Int]] = []
for i in 0.. ResilientRecovery {
guard cards.length() > 0 else { return ResilientNeedCards([0]) }
let profile = cards[0]
let total = profile.data_count + profile.parity_count
guard profile.data_count >= 2 &&
profile.data_count <= 32 &&
profile.parity_count >= 1 &&
profile.parity_count <= 8 &&
profile.width > 0 &&
profile.original_len >= 0 else {
return ResilientDamaged("profile values are outside supported bounds")
}
let buckets : Array[ResilientShardCard?] = []
for _ in 0..= 0 && current.index < total else {
return ResilientDamaged("card index is outside the resilient deck")
}
guard current.cells.length() == current.width else {
return ResilientDamaged(
"card width mismatch at card " + current.index.to_string(),
)
}
guard validate_resilient_card(current) else {
return ResilientDamaged(
"checksum mismatch at card " + current.index.to_string(),
)
}
match buckets[current.index] {
Some(previous) =>
if !cells_equal(previous.cells, current.cells) {
return ResilientDamaged(
"conflicting duplicate card " + current.index.to_string(),
)
}
None => buckets[current.index] = Some(current)
}
}
let missing_data : Array[Int] = []
for i in 0.. parity_indices.length() {
return ResilientNeedCards(
recovery_needed_indices(
missing_data,
profile.data_count,
profile.parity_count,
parity_indices.length(),
),
)
}
match
recover_missing_rows(
buckets,
missing_data,
parity_indices,
profile.data_count,
profile.width,
) {
Some(recovered_rows) => {
for i in 0.. ResilientDamaged("available parity rows are not independent")
}
}
///|
/// Check the checksum carried by one resilient card.
pub fn validate_resilient_card(card : ResilientShardCard) -> Bool {
card.checksum == resilient_checksum(card)
}
///|
/// Return one compact, copy-friendly line for a resilient card.
pub fn render_resilient_card(card : ResilientShardCard) -> String {
"RSN1|" +
card.deck +
"|" +
resilient_role_name(card) +
"|" +
"i=" +
card.index.to_string() +
"|" +
"k=" +
card.data_count.to_string() +
"|" +
"p=" +
card.parity_count.to_string() +
"|" +
"w=" +
card.width.to_string() +
"|" +
"n=" +
card.original_len.to_string() +
"|" +
"c=" +
card.checksum.to_string() +
"|" +
"x=" +
hex_cells(card.cells)
}
///|
/// Render a printable resilient note sheet.
pub fn render_resilient_sheet(cards : Array[ResilientShardCard]) -> String {
let lines : Array[String] = ["# resilient-shard-note"]
for current in cards {
lines.push(render_resilient_card(current))
}
lines.join("\n")
}
///|
/// Parse one `RSN1|...` line emitted by `render_resilient_card`.
pub fn parse_resilient_card(line : String) -> CardParse {
let clean = line.trim().to_owned()
let parts = split_owned(clean, "|")
guard parts.length() == 10 else {
return InvalidCard("expected 10 pipe-separated fields")
}
guard parts[0] == "RSN1" else { return InvalidCard("expected RSN1 prefix") }
let deck = parts[1]
guard parts[2] == "data" || parts[2] == "parity" else {
return InvalidCard("expected data or parity role")
}
let index = parse_prefixed_decimal(parts[3], "i=")
let data_count = parse_prefixed_decimal(parts[4], "k=")
let parity_count = parse_prefixed_decimal(parts[5], "p=")
let width = parse_prefixed_decimal(parts[6], "w=")
let original_len = parse_prefixed_decimal(parts[7], "n=")
let checksum = parse_prefixed_decimal(parts[8], "c=")
let cells = parse_prefixed_hex_cells(parts[9], "x=")
match
(index, data_count, parity_count, width, original_len, checksum, cells) {
(Some(i), Some(k), Some(p), Some(w), Some(n), Some(c), Some(x)) => {
let card : ResilientShardCard = {
deck,
index: i,
data_count: k,
parity_count: p,
width: w,
original_len: n,
checksum: c,
cells: x,
}
guard card.cells.length() == card.width else {
return InvalidCard("hex payload does not match declared width")
}
guard validate_resilient_card(card) else {
return InvalidCard("checksum mismatch")
}
ParsedCard(card)
}
_ => InvalidCard("one or more numeric fields are malformed")
}
}
///|
/// Parse a multi-line sheet. Blank lines and Markdown heading lines are ignored.
pub fn parse_resilient_sheet(text : String) -> SheetParseReport {
let cards : Array[ResilientShardCard] = []
let errors : Array[String] = []
let mut ignored_lines = 0
let mut line_number = 1
for view in text.split("\n") {
let line = view.trim().to_owned()
if line == "" || line.has_prefix("#") {
ignored_lines = ignored_lines + 1
} else if line.has_prefix("RSN1|") {
match parse_resilient_card(line) {
ParsedCard(card) => cards.push(card)
InvalidCard(message) =>
errors.push("line " + line_number.to_string() + ": " + message)
}
} else {
ignored_lines = ignored_lines + 1
}
line_number = line_number + 1
}
{ cards, ignored_lines, errors }
}
///|
/// Parse a sheet and immediately try to recover it.
pub fn recover_resilient_sheet(text : String) -> ResilientRecovery {
let parsed = parse_resilient_sheet(text)
if parsed.errors.length() > 0 {
return ResilientDamaged(parsed.errors.join("; "))
}
recover_resilient(parsed.cards)
}
///|
/// Explain how many data cards can be missing for the current evidence.
pub fn resilient_capacity_summary(cards : Array[ResilientShardCard]) -> String {
guard cards.length() > 0 else { return "empty deck: no cards to inspect" }
let profile = cards[0]
let missing_data = missing_resilient_data_indices(cards)
let parity_seen = count_present_resilient_parity(cards)
"deck=" +
profile.deck +
" data=" +
profile.data_count.to_string() +
" parity=" +
profile.parity_count.to_string() +
" present-parity=" +
parity_seen.to_string() +
" missing-data=" +
render_ints_inline(missing_data) +
" recoverable-now=" +
(missing_data.length() <= parity_seen).to_string()
}
///|
/// Return missing data indices after validating only visible indices.
pub fn missing_resilient_data_indices(
cards : Array[ResilientShardCard],
) -> Array[Int] {
guard cards.length() > 0 else { return [0] }
let profile = cards[0]
let seen : Array[Bool] = []
for _ in 0..= 0 && current.index < profile.data_count {
seen[current.index] = true
}
}
let missing : Array[Int] = []
for i in 0.. Int {
guard cards.length() > 0 else { return 0 }
let profile = cards[0]
let seen : Array[Bool] = []
for _ in 0..= profile.data_count &&
current.index < profile.data_count + profile.parity_count {
seen[current.index - profile.data_count] = true
}
}
let mut count = 0
for value in seen {
if value {
count = count + 1
}
}
count
}
///|
/// GF(256) addition. In characteristic two this is XOR.
pub fn gf256_add(left : Int, right : Int) -> Int {
xor_byte(left, right)
}
///|
/// GF(256) subtraction. It is identical to addition in characteristic two.
pub fn gf256_sub(left : Int, right : Int) -> Int {
xor_byte(left, right)
}
///|
/// GF(256) multiplication using polynomial 0x11D.
pub fn gf256_mul(left : Int, right : Int) -> Int {
let mut a = normalize_byte(left)
let mut b = normalize_byte(right)
let mut out = 0
for _ in 0..<8 {
if b % 2 == 1 {
out = xor_byte(out, a)
}
let carry = a >= 128
a = a * 2
if a >= 256 {
a = a - 256
}
if carry {
a = xor_byte(a, 29)
}
b = b / 2
}
normalize_byte(out)
}
///|
/// GF(256) exponentiation.
pub fn gf256_pow(base : Int, exponent : Int) -> Int {
if exponent <= 0 {
return 1
}
let mut out = 1
let clean_base = normalize_byte(base)
for _ in 0.. Int? {
let clean = normalize_byte(value)
if clean == 0 {
None
} else {
Some(gf256_pow(clean, 254))
}
}
///|
/// GF(256) division.
pub fn gf256_div(left : Int, right : Int) -> Int? {
match gf256_inv(right) {
Some(inv) => Some(gf256_mul(left, inv))
None => None
}
}
///|
fn resilient_card(
deck : String,
index : Int,
data_count : Int,
parity_count : Int,
width : Int,
original_len : Int,
cells : Array[Int],
) -> ResilientShardCard {
let normalized = normalize_cells(cells)
let check = resilient_checksum_values(
deck, index, data_count, parity_count, width, original_len, normalized,
)
{
deck,
index,
data_count,
parity_count,
width,
original_len,
checksum: check,
cells: normalized,
}
}
///|
fn same_resilient_profile(
left : ResilientShardCard,
right : ResilientShardCard,
) -> Bool {
left.deck == right.deck &&
left.data_count == right.data_count &&
left.parity_count == right.parity_count &&
left.width == right.width &&
left.original_len == right.original_len
}
///|
fn resilient_checksum(card : ResilientShardCard) -> Int {
resilient_checksum_values(
card.deck,
card.index,
card.data_count,
card.parity_count,
card.width,
card.original_len,
card.cells,
)
}
///|
fn resilient_checksum_values(
deck : String,
index : Int,
data_count : Int,
parity_count : Int,
width : Int,
original_len : Int,
cells : Array[Int],
) -> Int {
let mut value = 4147
for ch in deck.iter() {
value = checksum_step(value, ch.to_int())
}
value = checksum_step(value, index)
value = checksum_step(value, data_count)
value = checksum_step(value, parity_count)
value = checksum_step(value, width)
value = checksum_step(value, original_len)
for cell in cells {
value = checksum_step(value, cell)
}
value % 65521
}
///|
fn resilient_role_name(card : ResilientShardCard) -> String {
if card.index < card.data_count {
"data"
} else {
"parity"
}
}
///|
fn parity_row(
rows : Array[Array[Int]],
data_count : Int,
width : Int,
parity_index : Int,
) -> Array[Int] {
let out : Array[Int] = []
for offset in 0.. Int {
gf256_pow(data_index + 1, parity_index)
}
///|
fn available_parity_indices(
buckets : Array[ResilientShardCard?],
data_count : Int,
parity_count : Int,
) -> Array[Int] {
let out : Array[Int] = []
for parity in 0.. Array[Int] {
let out : Array[Int] = []
for item in missing_data {
out.push(item)
}
let mut need = missing_data.length() - present_parity_count
let mut parity = 0
while need > 0 && parity < parity_count {
let candidate = data_count + parity
if !out.contains(candidate) {
out.push(candidate)
need = need - 1
}
parity = parity + 1
}
out
}
///|
fn recover_missing_rows(
buckets : Array[ResilientShardCard?],
missing_data : Array[Int],
parity_indices : Array[Int],
data_count : Int,
width : Int,
) -> Array[Array[Int]]? {
let missing_count = missing_data.length()
guard missing_count > 0 else { return Some([]) }
let chosen = first_n(parity_indices, missing_count)
let matrix = coefficient_matrix(missing_data, chosen, data_count)
let recovered_rows : Array[Array[Int]] = []
for _ in 0..
for row in 0.. return None
}
}
Some(recovered_rows)
}
///|
fn coefficient_matrix(
missing_data : Array[Int],
parity_indices : Array[Int],
data_count : Int,
) -> Array[Array[Int]] {
let matrix : Array[Array[Int]] = []
for parity_card_index in parity_indices {
let parity_index = parity_card_index - data_count
let row : Array[Int] = []
for data_index in missing_data {
row.push(parity_coefficient(data_index, parity_index))
}
matrix.push(row)
}
matrix
}
///|
fn recovery_rhs_for_offset(
buckets : Array[ResilientShardCard?],
parity_indices : Array[Int],
missing_data : Array[Int],
data_count : Int,
offset : Int,
) -> Array[Int] {
let rhs : Array[Int] = []
for parity_card_index in parity_indices {
let parity_index = parity_card_index - data_count
let mut value = buckets[parity_card_index].unwrap().cells[offset]
for data_index in 0.. Array[Int]? {
let n = rhs.length()
guard matrix.length() == n else { return None }
let aug : Array[Array[Int]] = []
for row_index in 0..= 0 else { return None }
if pivot != col {
let temp = aug[pivot]
aug[pivot] = aug[col]
aug[col] = temp
}
match gf256_inv(aug[col][col]) {
Some(inv) =>
for j in col..<(n + 1) {
aug[col][j] = gf256_mul(aug[col][j], inv)
}
None => return None
}
for row in 0.. Int {
let mut row = column
while row < size {
if matrix[row][column] != 0 {
return row
}
row = row + 1
}
-1
}
///|
fn join_resilient_data(
buckets : Array[ResilientShardCard?],
data_count : Int,
width : Int,
) -> Array[Int] {
let out : Array[Int] = []
for i in 0.. Array[Int] {
let out : Array[Int] = []
let take = min_int(values.length(), count)
for i in 0.. Bool {
if left.length() != right.length() {
return false
}
for i in 0.. Array[String] {
let out : Array[String] = []
for part in text.split(sep) {
out.push(part.to_owned())
}
out
}
///|
fn parse_prefixed_decimal(field : String, prefix : String) -> Int? {
match field.strip_prefix(prefix) {
Some(rest) => parse_decimal(rest.to_owned())
None => None
}
}
///|
fn parse_prefixed_hex_cells(field : String, prefix : String) -> Array[Int]? {
match field.strip_prefix(prefix) {
Some(rest) => parse_hex_cells(rest.to_owned())
None => None
}
}
///|
fn parse_decimal(text : String) -> Int? {
guard text.length() > 0 else { return None }
let mut value = 0
for ch in text.iter() {
let code = ch.to_int()
if code < 48 || code > 57 {
return None
}
value = value * 10 + code - 48
}
Some(value)
}
///|
fn parse_hex_cells(hex : String) -> Array[Int]? {
guard hex.length() % 2 == 0 else { return None }
let out : Array[Int] = []
let mut index = 0
while index < hex.length() {
let high = hex_digit_value(hex.get_char(index).unwrap())
let low = hex_digit_value(hex.get_char(index + 1).unwrap())
match (high, low) {
(Some(h), Some(l)) => out.push(h * 16 + l)
_ => return None
}
index = index + 2
}
Some(out)
}
///|
fn hex_digit_value(ch : Char) -> Int? {
let code = ch.to_int()
if code >= 48 && code <= 57 {
Some(code - 48)
} else if code >= 65 && code <= 70 {
Some(code - 55)
} else if code >= 97 && code <= 102 {
Some(code - 87)
} else {
None
}
}
///|
fn render_ints_inline(values : Array[Int]) -> String {
let parts : Array[String] = []
for value in values {
parts.push(value.to_string())
}
"[" + parts.join(",") + "]"
}