96 lines
2.7 KiB
Plaintext
96 lines
2.7 KiB
Plaintext
; A fixed-size ring buffer of samples, generic over the element type, with
|
|
; the statistics a sensor log wants: a median, the distinct values, and a
|
|
; checksum over the raw bytes.
|
|
|
|
struct Ring
|
|
items: [$n $t]
|
|
head: i32
|
|
count: i32
|
|
|
|
struct Reading
|
|
sensor: u8
|
|
value: i32
|
|
|
|
fn push-ring!(r: Ptr(Ring($n, $t)), x: $t) -> ()
|
|
r.items[r.head] = x
|
|
r.head = (r.head + 1) % n
|
|
if r.count < n
|
|
r.count += 1
|
|
|
|
; The oldest sample first.
|
|
fn nth-oldest(r: Ptr(Ring($n, $t)), i: i32) -> $t
|
|
let start = if r.count < n then 0 else r.head
|
|
r.items[(start + i) % n]
|
|
|
|
fn copy-out(r: Ptr(Ring($n, $t))) -> Vec($t)
|
|
let v = vec-new(t)
|
|
for i in range(r.count)
|
|
push(v, nth-oldest(r, i))
|
|
v
|
|
|
|
fn median(xs: [$t]) -> Option($t) where ordered?($t), equal?($t)
|
|
if length(xs) == 0
|
|
return None
|
|
sort(xs)
|
|
Some(xs[length(xs) / 2])
|
|
|
|
fn distinct(xs: [const $t]) -> Vec($t) where equal?($t), hashable?($t)
|
|
let seen = map-new(t, bool)
|
|
defer free(seen)
|
|
let out = vec-new(t)
|
|
for i in range(length(xs))
|
|
if not has-key?(seen, xs[i])
|
|
put(seen, xs[i], true)
|
|
push(out, xs[i])
|
|
out
|
|
|
|
; FNV-1a over the bytes of any array of plain values.
|
|
fn checksum(p: Ptr(u8), size: i64) -> u32
|
|
let bytes = slice-from(p, size)
|
|
let h: u32 = 2166136261
|
|
for i in range(length(bytes))
|
|
h = bit-xor(h, u32(bytes[i]))
|
|
h = h * 16777619
|
|
h
|
|
|
|
; Apply f n times, for any element type.
|
|
fn repeat-apply(f: CFn($t) -> $t, x: $t, n: i32) -> $t
|
|
let v = x
|
|
for i in range(n)
|
|
v = f(v)
|
|
v
|
|
|
|
fn halve-all(x: $t) -> $t where numeric?($t) = repeat-apply(fn(a: $t) -> $t => a / 2, x, 3)
|
|
|
|
fn main() -> i32
|
|
let r: Ring(5, i32) = zeroed()
|
|
let samples = [7 3 9 3 12 5 3 8]
|
|
for :fill i in range(length(samples))
|
|
if samples[i] > 10
|
|
continue :fill
|
|
push-ring!(addr(r), samples[i])
|
|
let kept = copy-out(addr(r))
|
|
defer free(kept)
|
|
println("kept", slice(kept))
|
|
match median(slice(kept))
|
|
Some(m) -> println("median", m)
|
|
None -> println("no samples")
|
|
let empty: [0 i32] = zeroed()
|
|
println("median of none", or-else(median(slice(empty)), -1))
|
|
let d = distinct(slice(samples))
|
|
println("distinct", slice(d))
|
|
free(d)
|
|
let big = filter(slice(samples), fn(x) => x >= 7)
|
|
println("seven and up", slice(big), "sum", reduce(slice(big), 0, fn(a, b) => a + b))
|
|
free(big)
|
|
let rs = [Reading{.sensor 2, .value 40} Reading{.sensor 1, .value 15}
|
|
Reading{.sensor 3, .value 22}]
|
|
sort-by(slice(rs), fn(a, b) => a.value < b.value)
|
|
for i in range(length(rs))
|
|
println("sensor", rs[i].sensor, rs[i].value)
|
|
let raw: [4 u32] = [1 2 3 4]
|
|
let p = Ptr(u8)(addr(raw[0]))
|
|
println("checksum", checksum(p, 16))
|
|
println("doubled", repeat-apply(fn(a: i32) -> i32 => a * 2, 1, 10), "halved", halve-all(800.0))
|
|
0
|