///|
/// Mutable graph assembler. A builder can be reused after `build`; every built
/// graph owns copies of its adjacency and final-weight arrays.
pub struct TransducerBuilder[W] {
semiring : Semiring[W]
priv outgoing : Array[Array[Arc[W]]]
priv final_weights : Array[W?]
mut start : Int32?
}
///|
/// Validated weighted finite-state transducer. Graph arrays are not exposed as
/// mutable references; use the accessors to inspect a snapshot.
pub struct Transducer[W] {
semiring : Semiring[W]
priv outgoing : Array[Array[Arc[W]]]
priv final_weights : Array[W?]
start : Int32?
}
///|
/// Create an empty transducer builder for the supplied semiring.
pub fn[W] transducer_builder(semiring : Semiring[W]) -> TransducerBuilder[W] {
let outgoing : Array[Array[Arc[W]]] = []
let final_weights : Array[W?] = []
{ semiring, outgoing, final_weights, start: None, }
}
///|
/// Append a state and return its dense, zero-based identifier.
pub fn[W] add_state(builder : TransducerBuilder[W]) -> Int32 {
let id = builder.outgoing.length()
let arcs : Array[Arc[W]] = []
builder.outgoing.push(arcs)
builder.final_weights.push(None)
id
}
///|
/// Set the start state. An invalid state id leaves the builder unchanged.
pub fn[W] set_start(
builder : TransducerBuilder[W],
state : Int32,
) -> Result[Unit, WfstError] {
if state < 0 || state >= builder.outgoing.length() {
Err(WfstError::InvalidStateId(state))
} else {
builder.start = Some(state)
Ok(())
}
}
///|
/// Set a state's final weight. Passing a semiring-zero weight is permitted and
/// retains the distinction between a final state with zero weight and non-final.
pub fn[W] set_final(
builder : TransducerBuilder[W],
state : Int32,
weight : W,
) -> Result[Unit, WfstError] {
if state < 0 || state >= builder.outgoing.length() {
Err(WfstError::InvalidStateId(state))
} else {
builder.final_weights[state] = Some(weight)
Ok(())
}
}
///|
/// Mark a state as non-final.
pub fn[W] clear_final(
builder : TransducerBuilder[W],
state : Int32,
) -> Result[Unit, WfstError] {
if state < 0 || state >= builder.outgoing.length() {
Err(WfstError::InvalidStateId(state))
} else {
builder.final_weights[state] = None
Ok(())
}
}
///|
/// Add an arc after validating both endpoint identifiers.
pub fn[W] add_arc(
builder : TransducerBuilder[W],
source : Int32,
input : Label,
output : Label,
destination : Int32,
weight : W,
) -> Result[Unit, WfstError] {
let count = builder.outgoing.length()
if source < 0 || source >= count {
Err(WfstError::InvalidStateId(source))
} else if destination < 0 || destination >= count {
Err(WfstError::InvalidStateId(destination))
} else {
let arc : Arc[W] = { input, output, destination, weight, }
builder.outgoing[source].push(arc)
Ok(())
}
}
///|
/// Produce a graph snapshot independent of later builder mutations.
pub fn[W] build_transducer(builder : TransducerBuilder[W]) -> Transducer[W] {
let outgoing : Array[Array[Arc[W]]] = []
for source_arcs in builder.outgoing {
let copied_arcs : Array[Arc[W]] = []
for arc in source_arcs {
copied_arcs.push(arc)
}
outgoing.push(copied_arcs)
}
let final_weights : Array[W?] = []
for weight in builder.final_weights {
final_weights.push(weight)
}
{ semiring: builder.semiring, outgoing, final_weights, start: builder.start, }
}
///|
/// Number of states in a transducer snapshot.
pub fn[W] transducer_state_count(transducer : Transducer[W]) -> Int32 {
transducer.outgoing.length()
}
///|
/// Read the optional start-state identifier.
pub fn[W] transducer_start_state(transducer : Transducer[W]) -> Int32? {
transducer.start
}
///|
/// Return a copy of a state's outgoing arcs. Invalid identifiers are reported.
pub fn[W] transducer_arcs_from(
transducer : Transducer[W],
state : Int32,
) -> Result[Array[Arc[W]], WfstError] {
if state < 0 || state >= transducer.outgoing.length() {
Err(WfstError::InvalidStateId(state))
} else {
let copied : Array[Arc[W]] = []
for arc in transducer.outgoing[state] {
copied.push(arc)
}
Ok(copied)
}
}
///|
/// Read a state's optional final weight.
pub fn[W] transducer_final_weight(
transducer : Transducer[W],
state : Int32,
) -> Result[W?, WfstError] {
if state < 0 || state >= transducer.final_weights.length() {
Err(WfstError::InvalidStateId(state))
} else {
Ok(transducer.final_weights[state])
}
}
///|
/// Return the semiring attached to this graph.
pub fn[W] transducer_semiring(transducer : Transducer[W]) -> Semiring[W] {
transducer.semiring
}