init repo + add edge-telemetry puzzle
This commit is contained in:
commit
b1f730630a
7 changed files with 210 additions and 0 deletions
7
README.md
Normal file
7
README.md
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
# PUZZLES
|
||||||
|
assortment of puzzles which i've completed in order to practice my programming skills.
|
||||||
|
|
||||||
|
# AI DISCLAIMER
|
||||||
|
each puzzle uses LLM generated challenges which are copied by me into a `challenge.md` file.
|
||||||
|
|
||||||
|
**this is the _only_ AI generated content in this repository.**
|
||||||
1
edge-telemetry/.gitignore
vendored
Normal file
1
edge-telemetry/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
/target
|
||||||
7
edge-telemetry/Cargo.lock
generated
Normal file
7
edge-telemetry/Cargo.lock
generated
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "edge-telemetry"
|
||||||
|
version = "0.1.0"
|
||||||
6
edge-telemetry/Cargo.toml
Normal file
6
edge-telemetry/Cargo.toml
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
[package]
|
||||||
|
name = "edge-telemetry"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
8
edge-telemetry/README.md
Normal file
8
edge-telemetry/README.md
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
# EDGE TELEMETRY
|
||||||
|
this is a toy problem used as rust practice. the challenge text is located in `challenge.md`.
|
||||||
|
|
||||||
|
|
||||||
|
# AI DISCLAIMER
|
||||||
|
this repository contains a challenge which was generated by chatGPT. this content exists in: `challenge.md`.
|
||||||
|
|
||||||
|
this is the _only_ LLM / AI generated content in this repository, everything else was done by hand using my brain.
|
||||||
24
edge-telemetry/challenge.md
Normal file
24
edge-telemetry/challenge.md
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
**THIS CHALLENGE GENERATED BY CHATGPT**
|
||||||
|
|
||||||
|
# Edge Telemetry
|
||||||
|
## Problem Overview:
|
||||||
|
|
||||||
|
You are developing software for an edge device that collects power usage data from sensors at periodic intervals.
|
||||||
|
The device has limited memory, so you need to design a system that efficiently stores recent data, compresses it,
|
||||||
|
and manages memory effectively. The goal is to minimize the amount of memory used while ensuring that the telemetry
|
||||||
|
data is still accessible and compressed.
|
||||||
|
|
||||||
|
## Requirements:
|
||||||
|
**Data Structure:** You will be given a list of telemetry readings, each consisting of a timestamp and power usage value. You need to store the readings in a compressed form, using Run-Length Encoding (RLE).
|
||||||
|
|
||||||
|
**Memory Management:** The device has a limited memory capacity of N readings. If the number of readings exceeds this limit, the oldest readings should be discarded to make room for the new data.
|
||||||
|
|
||||||
|
**Compression:** Use Run-Length Encoding (RLE) to compress consecutive identical readings. This will reduce the amount of memory needed for storing repeated values.
|
||||||
|
|
||||||
|
## API:
|
||||||
|
|
||||||
|
`add_reading(timestamp, power_usage)`: Adds a new telemetry reading and compresses the data using RLE.
|
||||||
|
|
||||||
|
`get_compressed_data()`: Returns the compressed data as a list of tuples, where each tuple contains the power usage value and the count of consecutive occurrences.
|
||||||
|
|
||||||
|
**END LLM GENERATED CONTENT**
|
||||||
157
edge-telemetry/src/main.rs
Normal file
157
edge-telemetry/src/main.rs
Normal file
|
|
@ -0,0 +1,157 @@
|
||||||
|
pub trait EdgeTelemetry<E> {
|
||||||
|
/// Add a new telemetry reading to the underlying storage type
|
||||||
|
// TODO: update the timestamp to something that makes more sense maybe?
|
||||||
|
fn add_reading(&mut self, entry: E);
|
||||||
|
|
||||||
|
/// Returns the compressed data as a collection of tuples, where each tuple has the format:
|
||||||
|
/// `(usage, consecutive occurances)`
|
||||||
|
fn get_compressed_data<'a>(&'a self) -> impl Iterator<Item = &'a (E, usize)>
|
||||||
|
where
|
||||||
|
E: 'a;
|
||||||
|
}
|
||||||
|
|
||||||
|
mod entry {
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Entry {
|
||||||
|
timestamp: usize,
|
||||||
|
reading: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Entry {
|
||||||
|
pub fn new(timestamp: usize, reading: usize) -> Self {
|
||||||
|
Self { timestamp, reading }
|
||||||
|
}
|
||||||
|
pub fn timestamp(&self) -> usize {
|
||||||
|
self.timestamp
|
||||||
|
}
|
||||||
|
pub fn reading(&self) -> usize {
|
||||||
|
self.reading
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn create_entry() {
|
||||||
|
let t = 3;
|
||||||
|
let r = 1;
|
||||||
|
|
||||||
|
let e = Entry::new(t, r);
|
||||||
|
|
||||||
|
assert_eq!(t, e.timestamp());
|
||||||
|
assert_eq!(r, e.reading());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod storage {
|
||||||
|
use std::collections::VecDeque;
|
||||||
|
|
||||||
|
use crate::EdgeTelemetry;
|
||||||
|
use crate::entry::Entry;
|
||||||
|
|
||||||
|
pub struct RingBufferStorage {
|
||||||
|
size: usize,
|
||||||
|
buf: VecDeque<(Entry, usize)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RingBufferStorage {
|
||||||
|
pub fn new(size: usize) -> Self {
|
||||||
|
Self {
|
||||||
|
size,
|
||||||
|
buf: VecDeque::with_capacity(size),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_entry(&mut self, entry: Entry) {
|
||||||
|
// buffer is not empty, and the previous value matches
|
||||||
|
if let Some((prev, quantity)) = self.buf.iter_mut().last()
|
||||||
|
&& prev.reading() == entry.reading()
|
||||||
|
{
|
||||||
|
// TODO: add some logic here to overflow into a new entry if needed
|
||||||
|
*quantity = quantity.saturating_add(1);
|
||||||
|
}
|
||||||
|
// buffer is either empty, or the previous value does not match
|
||||||
|
else {
|
||||||
|
// check capacity to make sure we aren't full (not really necessary on the empty case but this is a little easier to read)
|
||||||
|
if self.buf.len() == self.size {
|
||||||
|
self.buf.pop_front();
|
||||||
|
}
|
||||||
|
self.buf.push_back((entry, 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EdgeTelemetry<Entry> for RingBufferStorage {
|
||||||
|
fn add_reading(&mut self, entry: Entry) {
|
||||||
|
self.add_entry(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_compressed_data<'a>(&'a self) -> impl Iterator<Item = &'a (Entry, usize)>
|
||||||
|
where
|
||||||
|
Entry: 'a,
|
||||||
|
{
|
||||||
|
self.buf.iter()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use crate::EdgeTelemetry;
|
||||||
|
use crate::entry::Entry;
|
||||||
|
use crate::storage::RingBufferStorage;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn add_and_get_reading() {
|
||||||
|
let size = 10;
|
||||||
|
let mut s = RingBufferStorage::new(size);
|
||||||
|
|
||||||
|
let t = 1;
|
||||||
|
let r = 3;
|
||||||
|
let e = Entry::new(t, r);
|
||||||
|
|
||||||
|
s.add_reading(e);
|
||||||
|
|
||||||
|
let data = s.get_compressed_data();
|
||||||
|
|
||||||
|
data.into_iter().for_each(|(entry, quantity)| {
|
||||||
|
assert_eq!(entry.timestamp(), t);
|
||||||
|
assert_eq!(entry.reading(), r);
|
||||||
|
assert_eq!(*quantity, 1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn duplicate_readings() {
|
||||||
|
let size = 10;
|
||||||
|
let mut s = RingBufferStorage::new(size);
|
||||||
|
|
||||||
|
let t = 1;
|
||||||
|
let r = 3;
|
||||||
|
let q = 3;
|
||||||
|
|
||||||
|
for _ in 0..q {
|
||||||
|
let e = Entry::new(t, r);
|
||||||
|
s.add_reading(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
let data = s.get_compressed_data();
|
||||||
|
|
||||||
|
data.into_iter().for_each(|(entry, quantity)| {
|
||||||
|
assert_eq!(entry.timestamp(), t);
|
||||||
|
assert_eq!(entry.reading(), r);
|
||||||
|
assert_eq!(*quantity, q);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
println!("Hello, world!");
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTES
|
||||||
|
// * assuming the sample rate is constant, we can extract sample time from the run length by knowing the base stamp, and incrementing to the offset
|
||||||
|
// * this also means we can actually just stamp the start time and then compute the sample time via the offset
|
||||||
Loading…
Add table
Add a link
Reference in a new issue