///|
/// A reusable signal/time query window.
pub struct QueryWindow {
signal_id : String?
start : Int64
end : Int64
} derive(Debug, Eq)
///|
/// Create an inclusive query window.
pub fn query_window(
signal_id? : String,
start~ : Int64,
end~ : Int64,
) -> QueryWindow {
{ signal_id, start, end: Int64::max(start, end) }
}
///|
/// Return all declared identifiers in declaration order.
pub fn trace_signal_ids(trace : Trace) -> Array[String] {
trace.signals.map(fn(signal) { signal.id })
}
///|
/// Return changes for one signal in source order.
pub fn signal_changes(trace : Trace, signal_id : String) -> Array[Change] {
trace.changes.filter(fn(change) { change.id == signal_id })
}
///|
/// Return timestamps for one signal.
pub fn change_times(trace : Trace, signal_id : String) -> Array[Int64] {
signal_changes(trace, signal_id).map(fn(change) { change.time })
}
///|
/// Return distinct values in first-seen order.
pub fn distinct_values(trace : Trace, signal_id : String) -> Array[Value] {
let result : Array[Value] = []
for change in signal_changes(trace, signal_id) {
if !result.contains(change.value) {
result.push(change.value)
}
}
result
}
///|
/// Return the first observed value for a signal.
pub fn first_value(trace : Trace, signal_id : String) -> Value? {
match signal_changes(trace, signal_id).get(0) {
Some(change) => Some(change.value)
None => None
}
}
///|
/// Return the last observed value for a signal.
pub fn last_value(trace : Trace, signal_id : String) -> Value? {
match signal_changes(trace, signal_id).last() {
Some(change) => Some(change.value)
None => None
}
}
///|
/// Return the value strictly before a timestamp.
pub fn value_before(
trace : Trace,
signal_id : String,
timestamp : Int64,
) -> Value? {
let mut result : Value? = None
for change in signal_changes(trace, signal_id) {
if change.time >= timestamp {
break
}
result = Some(change.value)
}
result
}
///|
/// Return the first value strictly after a timestamp.
pub fn value_after(
trace : Trace,
signal_id : String,
timestamp : Int64,
) -> Value? {
for change in signal_changes(trace, signal_id) {
if change.time > timestamp {
return Some(change.value)
}
}
None
}
///|
/// Return the next change after a timestamp.
pub fn next_change(
trace : Trace,
signal_id : String,
timestamp : Int64,
) -> Change? {
for change in signal_changes(trace, signal_id) {
if change.time > timestamp {
return Some(change)
}
}
None
}
///|
/// Return the previous change at or before a timestamp.
pub fn previous_change(
trace : Trace,
signal_id : String,
timestamp : Int64,
) -> Change? {
let mut result : Change? = None
for change in signal_changes(trace, signal_id) {
if change.time > timestamp {
break
}
result = Some(change)
}
result
}
///|
/// Return changes after an exclusive timestamp.
pub fn changes_after(
trace : Trace,
signal_id : String,
timestamp : Int64,
) -> Array[Change] {
signal_changes(trace, signal_id).filter(fn(change) { change.time > timestamp })
}
///|
/// Return changes before an exclusive timestamp.
pub fn changes_before(
trace : Trace,
signal_id : String,
timestamp : Int64,
) -> Array[Change] {
signal_changes(trace, signal_id).filter(fn(change) { change.time < timestamp })
}
///|
/// Return whether a signal changed exactly at a timestamp.
pub fn has_change_at(
trace : Trace,
signal_id : String,
timestamp : Int64,
) -> Bool {
signal_changes(trace, signal_id).any(fn(change) { change.time == timestamp })
}
///|
/// Return a signal's changes filtered by an inclusive query window.
pub fn query_window_changes(
trace : Trace,
window : QueryWindow,
) -> Array[Change] {
trace.changes.filter(fn(change) {
let signal_ok = match window.signal_id {
Some(id) => id == change.id
None => true
}
signal_ok && change.time >= window.start && change.time <= window.end
})
}
///|
/// Return the latest value for every declared signal at a timestamp.
pub fn snapshot(trace : Trace, timestamp : Int64) -> Array[Sample] {
trace.signals.map(fn(signal) {
{
signal_id: signal.id,
time: timestamp,
value: value_at(trace, signal.id, timestamp),
}
})
}
///|
/// Return a snapshot for only signals matching a glob.
pub fn snapshot_glob(
trace : Trace,
pattern : String,
timestamp : Int64,
) -> Array[Sample] {
find_signals_glob(trace, pattern).map(fn(signal) {
{
signal_id: signal.id,
time: timestamp,
value: value_at(trace, signal.id, timestamp),
}
})
}
///|
/// Return a bit at a zero-based position from the most significant side.
pub fn value_bit(value : Value, index : Int) -> LogicBit? {
let bits = value.bits()
if index < 0 || index >= bits.length() {
None
} else {
Some(bits[index])
}
}
///|
/// Return a vector slice with half-open bounds.
pub fn value_slice(value : Value, start : Int, end : Int) -> Value {
let bits = value.bits()
let left = Int::max(0, Int::min(start, bits.length()))
let right = Int::max(left, Int::min(end, bits.length()))
let selected = bits[left:right].to_owned()
if selected.length() == 1 && value.width() == 1 {
Scalar(selected[0].to_char())
} else {
Vector(bits_to_string(selected))
}
}
///|
/// Pad a value on the left to a requested width.
pub fn value_zero_extend(value : Value, width : Int) -> Value {
let bits = value.bits()
let target = Int::max(width, bits.length())
let result : Array[LogicBit] = []
for _ in 0..<(target - bits.length()) {
result.push(Zero)
}
result.append(bits)
if target == 1 {
Scalar(result[0].to_char())
} else {
Vector(bits_to_string(result))
}
}
///|
/// Return the highest known integer value represented by a binary vector.
pub fn unsigned_value(value : Value) -> Int64? {
value.to_int()
}
///|
/// Return changes whose values satisfy a caller predicate.
pub fn filter_changes(
trace : Trace,
signal_id : String,
predicate : (Value) -> Bool,
) -> Array[Change] {
signal_changes(trace, signal_id).filter(fn(change) { predicate(change.value) })
}
///|
/// Return the number of changes observed for each declared identifier.
pub fn change_count_catalog(trace : Trace) -> Array[(String, Int)] {
trace.signals.map(fn(signal) {
(signal.id, signal_changes(trace, signal.id).length())
})
}
///|
/// Return the first timestamp of each declared signal.
pub fn first_time_catalog(trace : Trace) -> Array[(String, Int64?)] {
trace.signals.map(fn(signal) {
(signal.id, change_times(trace, signal.id).get(0))
})
}
///|
/// Return all changes for a set of identifiers without changing trace order.
pub fn changes_for_ids(trace : Trace, ids : Array[String]) -> Array[Change] {
trace.changes.filter(fn(change) { ids.contains(change.id) })
}
///|
/// Return a trace-level change count for a query window.
pub fn query_window_count(trace : Trace, window : QueryWindow) -> Int {
query_window_changes(trace, window).length()
}