///|
/// Read legacy 1.0/1.1 network data; ports must come from a trusted filename or
/// explicit caller choice. Frequencies become Hz, non-S values become SI.
/// Noise sections are not accepted by this network-only entry point.
pub fn parse_legacy(text : String, ports~ : Int) -> Network raise {
if ports < 1 || ports > 32 {
raise Failure("ports must be 1..32")
}
let mut options : TextOptions? = None
let pending : Array[Double] = []
let freq = []
let values = []
let mut refs = []
let width = 1 + 2 * ports * ports
for line in text_lines(text) {
let line = uncomment(line)
if line.is_empty() {
continue
}
if line.has_prefix("#") {
// The standard explicitly ignores option lines after the first.
if options is None {
let opt = read_options(line)
refs = references(opt.reference, ports)
options = Some(opt)
}
continue
}
if line.has_prefix("[") {
raise Failure("versioned keywords require the document parser")
}
guard options is Some(opt) else {
raise Failure("missing option line before data")
}
for token in words(line) {
pending.push(number(token))
if pending.length() == width {
let f = pending[0] * opt.factor
if !freq.is_empty() && f <= freq[freq.length() - 1] {
raise Failure(
"network frequencies must increase; use document parser for noise",
)
}
let m = Array::make(ports * ports, complex(0.0))
for k in 0..<(ports * ports) {
let index = if ports == 2 { k % 2 * 2 + k / 2 } else { k }
let raw = pair(pending[1 + 2 * k], pending[2 + 2 * k], opt.format)
m[index] = checked(
raw.scale(
legacy_factor(opt.kind, index / ports, index % ports, refs),
),
)
}
freq.push(f)
values.push(m)
pending.clear()
if freq.length() > 100000 || freq.length() > 2000000 / (ports * ports) {
raise Failure("network exceeds point/value limit")
}
}
}
}
guard options is Some(opt) else { raise Failure("missing option line") }
if !pending.is_empty() {
raise Failure("truncated network record or unsupported legacy noise")
}
network(ports, freq, values, refs, parameter=opt.kind)
}
///|
/// Write 1.0 (uniform reference) or 1.1 (per-port references), preserving SI
/// values by reversing legacy normalization. No metadata exists on Network.
pub fn Network::write_legacy(
self : Network,
format? : String = "RI",
unit? : String = "HZ",
) -> String raise {
let format = format.to_upper()
if format != "RI" && format != "MA" && format != "DB" {
raise Failure("format must be RI, MA or DB")
}
let factor = unit_factor(unit)
let out = StringBuilder()
out.write_string("# \{unit.to_upper()} \{self.kind} \{format} R")
let uniform = self.reference.all(x => x == self.reference[0])
if uniform {
out.write_string(" \{self.reference[0]}")
} else {
for r in self.reference {
out.write_string(" \{r}")
}
}
out.write_string("\n")
for p, f in self.frequencies {
out.write_string("\{f / factor}")
for k in 0..<(self.n * self.n) {
let index = if self.n == 2 { k % 2 * 2 + k / 2 } else { k }
let scale = legacy_factor(
self.kind,
index / self.n,
index % self.n,
self.reference,
)
if !finite(scale) || scale == 0.0 {
raise Failure("legacy normalization outside numerical range")
}
write_pair(out, checked(self.values[p][index].scale(1.0 / scale)), format)
if self.n > 2 &&
((k % self.n + 1) % 4 == 0 || k % self.n == self.n - 1) &&
k + 1 < self.n * self.n {
out.write_string("\n ")
}
}
out.write_string("\n")
}
bounded_output(out)
}