упр.05 задача 2

Предадени решения

Краен срок:
12.11.2025 23:59
Точки:
3

Срокът за предаване на решения е отминал

// Include the solution source in the same file, so we
// don't have to worry about item visibility.
// Please don't use `include!` in real code, this is a hack
// around the checking system.
include!{ "../src/lib.rs" }
#[derive(Debug)]
struct FloatApprox(f64);
impl PartialEq<FloatApprox> for FloatApprox {
fn eq(&self, rhs: &FloatApprox) -> bool {
return self.0.signum() == rhs.0.signum() &&
(self.0 - rhs.0).abs() < 0.01;
}
}
#[test]
fn test_total() {
let aggr = TotalAggregator{};
let events = &[
Event { timestamp: 0, sensor: String::from("cpu0"), metric: SensorMetric::Load, value: 0.98 },
Event { timestamp: 0, sensor: String::from("cpu1"), metric: SensorMetric::Load, value: 0.04 },
Event { timestamp: 0, sensor: String::from("gpu0"), metric: SensorMetric::Load, value: 13.45 },
Event { timestamp: 0, sensor: String::from("cpu0"), metric: SensorMetric::Frequency, value: 3100.00 },
Event { timestamp: 0, sensor: String::from("cpu1"), metric: SensorMetric::Frequency, value: 3100.00 },
Event { timestamp: 0, sensor: String::from("gpu0"), metric: SensorMetric::Frequency, value: 300.00 },
Event { timestamp: 0, sensor: String::from("cpu0"), metric: SensorMetric::Temperature, value: 56.20 },
Event { timestamp: 0, sensor: String::from("cpu1"), metric: SensorMetric::Temperature, value: 33.33 },
Event { timestamp: 0, sensor: String::from("gpu0"), metric: SensorMetric::Temperature, value: 25.00 },
];
let mut result = aggr.aggregate(events);
result.sort_by(|(k1, _), (k2, _)| k1.cmp(k2));
assert_eq!(
result.into_iter()
.map(|(k, v)| (k, FloatApprox(v)))
.collect::<Vec<_>>(),
vec![
(String::from("Frequency"), FloatApprox(2166.67)),
(String::from("Load"), FloatApprox(4.82)),
(String::from("Temperature"), FloatApprox(38.18)),
],
);
}
#[test]
fn test_by_metric() {
let aggr = MetricAggregator{metric: SensorMetric::Load};
let events = &[
Event { timestamp: 0, sensor: String::from("cpu0"), metric: SensorMetric::Load, value: 0.98 },
Event { timestamp: 0, sensor: String::from("cpu1"), metric: SensorMetric::Load, value: 0.04 },
Event { timestamp: 0, sensor: String::from("gpu0"), metric: SensorMetric::Load, value: 13.45 },
Event { timestamp: 1, sensor: String::from("cpu0"), metric: SensorMetric::Frequency, value: 3100.00 },
Event { timestamp: 1, sensor: String::from("cpu1"), metric: SensorMetric::Frequency, value: 3100.00 },
Event { timestamp: 1, sensor: String::from("gpu0"), metric: SensorMetric::Frequency, value: 300.00 },
];
let mut result = aggr.aggregate(events);
result.sort_by(|(k1, _), (k2, _)| k1.cmp(k2));
assert_eq!(
result.into_iter()
.map(|(k, v)| (k, FloatApprox(v)))
.collect::<Vec<_>>(),
vec![
(String::from("cpu0"), FloatApprox(0.98)),
(String::from("cpu1"), FloatApprox(0.04)),
(String::from("gpu0"), FloatApprox(13.45)),
],
);
}
#[test]
fn test_by_metric_2() {
let aggr = MetricAggregator{metric: SensorMetric::Load};
let events = &[
Event { timestamp: 0, sensor: String::from("gpu0"), metric: SensorMetric::Load, value: 0.98 },
Event { timestamp: 1, sensor: String::from("gpu0"), metric: SensorMetric::Load, value: 0.04 },
Event { timestamp: 2, sensor: String::from("gpu0"), metric: SensorMetric::Load, value: 13.45 },
];
let mut result = aggr.aggregate(events);
result.sort_by(|(k1, _), (k2, _)| k1.cmp(k2));
assert_eq!(
result.into_iter()
.map(|(k, v)| (k, FloatApprox(v)))
.collect::<Vec<_>>(),
vec![
(String::from("gpu0"), FloatApprox(4.83)),
],
);
}

Напишете програма за работа с данни от сензори на произволен компютър.
Имате следните структури (може да добавяте допълнителни derive-ове и trait имплементации, ако ви трябват).

struct Event {
    timestamp: u64,
    sensor: String,
    metric: SensorMetric,
    value: f64,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
enum SensorMetric {
    Load,
    Frequency,
    Temperature,
}

Имплементирайте следната много генерална помощна функция, която:

  • приема списък от събития
  • групира събитията по ключ K
  • взима данните за агрегация D, от всяко събитие, ако има такива
  • за всеки различен ключ K, прилага функцията aggregate върху всички данни, отговарящи на този ключ и записва в hash map ключа и резултата от агрегацията

(Можем ли да го направим по-лесно или по-ефективно с итератори? Сигурно. Но пробвайте така).

fn group_and_aggregate<O, E, D, K>(
    events: &[E],
    group_fn: impl Fn(&E) -> K,
    data_fn: impl Fn(&E) -> Option<D>,
    aggregate: impl Fn(&[D]) -> O,
) -> HashMap<K, O>
where
    K: Eq + Hash,
{
    todo!()
}

Имплементирайте следните две агрегации, които могат да се приложат върху списък от данни, върнати от сензорите.
Използвайте помощната функция, дефинирана по-горе, доколкото можете.
Можете и да използвате всякакви други функционалности от стандартната библиотека.

trait Aggregator {
    type Output;

    fn name(&self) -> String;
    fn aggregate(&self, events: &[Event]) -> Vec<(String, Self::Output)>;
}

// Имплементирайте агрегация, която връща средната стойност на метриките, групирани по тип.
// Резултата трябва да е вектор от три елемента:
// - ("Load", <средната стойност на Load метриките от всички сензори>)
// - ("Frequency", <средната стойност на Frequency метриките от всички сензори>)
// - ("Temperature", <средната стойност на Temperature метриките от всички сензори>)
struct TotalAggregator {}
impl Aggregator for TotalAggregator {
    type Output = f64;

    fn name(&self) -> String {
        "total".to_string()
    }

    fn aggregate(&self, events: &[Event]) -> Vec<(String, f64)> {
        todo!()
    }
}

// Имплементирайте агрегация, която по даден тип на метрика, връща сумата от метриките
// от този тип, групирани по сензор.
// Пример при `MetricAggreator { metric: SensorMetric::Load }`, може да върне:
// - ("cpu0", <средната стойност от Load метиките за сензор cpu0>)
// - ("cpu1", <средната стойност от Load метиките за сензор cpu1>)
// - ("gpu0", <средната стойност от Load метиките за сензор gpu0>)
struct MetricAggregator {
    metric: SensorMetric
}
impl Aggregator for MetricAggregator {
    type Output = f64;

    fn name(&self) -> String {
        format!("{:?}", self.metric);
    }

    fn aggregate(&self, events: &[Event]) -> Vec<(String, f64)> {
        todo!();
    }
}

Пример за изпозлване

fn main() {
    let all: Vec<Box<dyn Aggregator<Output=f64>>> = vec![
        Box::new(TotalAggregator{}),
        Box::new(MetricAggregator{metric: SensorMetric::Load}),
        Box::new(MetricAggregator{metric: SensorMetric::Temperature}),
    ];

    let events = &[
        Event { timestamp: 0, sensor: String::from("cpu0"), metric: SensorMetric::Load, value: 0.98 },
        Event { timestamp: 0, sensor: String::from("cpu1"), metric: SensorMetric::Load, value: 0.04 },
        Event { timestamp: 0, sensor: String::from("gpu0"), metric: SensorMetric::Load, value: 13.45 },
        Event { timestamp: 0, sensor: String::from("cpu0"), metric: SensorMetric::Frequency, value: 3100.00 },
        Event { timestamp: 0, sensor: String::from("cpu1"), metric: SensorMetric::Frequency, value: 3100.00 },
        Event { timestamp: 0, sensor: String::from("gpu0"), metric: SensorMetric::Frequency, value: 300.00 },
        Event { timestamp: 0, sensor: String::from("cpu0"), metric: SensorMetric::Temperature, value: 56.20 },
        Event { timestamp: 0, sensor: String::from("cpu1"), metric: SensorMetric::Temperature, value: 33.33 },
        Event { timestamp: 0, sensor: String::from("gpu0"), metric: SensorMetric::Temperature, value: 25.00 },
    ];

    for aggr in &all {
        for (key, val) in aggr.aggregate(events) {
            println!("{} {:?}: {:.02}", aggr.name(), key, val);
        }
    }
    // total "Temperature": 38.18
    // total "Load": 4.82
    // total "Frequency": 2166.67
    // Load "gpu0": 13.45
    // Load "cpu1": 0.04
    // Load "cpu0": 0.98
    // Temperature "cpu1": 33.33
    // Temperature "cpu0": 56.20
    // Temperature "gpu0": 25.00
}

Задължително прочетете (или си припомнете): Указания за предаване на домашни

Погрижете се решението ви да се компилира с базовия тест:

// Include the solution source in the same file, so we
// don't have to worry about item visibility.
// Please don't use `include!` in real code, this is a hack
// around the checking system.
include!{ "../src/lib.rs" }
#[derive(Debug)]
struct FloatApprox(f64);
impl PartialEq<FloatApprox> for FloatApprox {
fn eq(&self, rhs: &FloatApprox) -> bool {
return self.0.signum() == rhs.0.signum() &&
(self.0 - rhs.0).abs() < 0.01;
}
}
#[test]
fn test_total() {
let aggr = TotalAggregator{};
let events = &[
Event { timestamp: 0, sensor: String::from("cpu0"), metric: SensorMetric::Load, value: 0.98 },
Event { timestamp: 0, sensor: String::from("cpu1"), metric: SensorMetric::Load, value: 0.04 },
Event { timestamp: 0, sensor: String::from("gpu0"), metric: SensorMetric::Load, value: 13.45 },
Event { timestamp: 0, sensor: String::from("cpu0"), metric: SensorMetric::Frequency, value: 3100.00 },
Event { timestamp: 0, sensor: String::from("cpu1"), metric: SensorMetric::Frequency, value: 3100.00 },
Event { timestamp: 0, sensor: String::from("gpu0"), metric: SensorMetric::Frequency, value: 300.00 },
Event { timestamp: 0, sensor: String::from("cpu0"), metric: SensorMetric::Temperature, value: 56.20 },
Event { timestamp: 0, sensor: String::from("cpu1"), metric: SensorMetric::Temperature, value: 33.33 },
Event { timestamp: 0, sensor: String::from("gpu0"), metric: SensorMetric::Temperature, value: 25.00 },
];
let mut result = aggr.aggregate(events);
result.sort_by(|(k1, _), (k2, _)| k1.cmp(k2));
assert_eq!(
result.into_iter()
.map(|(k, v)| (k, FloatApprox(v)))
.collect::<Vec<_>>(),
vec![
(String::from("Frequency"), FloatApprox(2166.67)),
(String::from("Load"), FloatApprox(4.82)),
(String::from("Temperature"), FloatApprox(38.18)),
],
);
}
#[test]
fn test_by_metric() {
let aggr = MetricAggregator{metric: SensorMetric::Load};
let events = &[
Event { timestamp: 0, sensor: String::from("cpu0"), metric: SensorMetric::Load, value: 0.98 },
Event { timestamp: 0, sensor: String::from("cpu1"), metric: SensorMetric::Load, value: 0.04 },
Event { timestamp: 0, sensor: String::from("gpu0"), metric: SensorMetric::Load, value: 13.45 },
Event { timestamp: 1, sensor: String::from("cpu0"), metric: SensorMetric::Frequency, value: 3100.00 },
Event { timestamp: 1, sensor: String::from("cpu1"), metric: SensorMetric::Frequency, value: 3100.00 },
Event { timestamp: 1, sensor: String::from("gpu0"), metric: SensorMetric::Frequency, value: 300.00 },
];
let mut result = aggr.aggregate(events);
result.sort_by(|(k1, _), (k2, _)| k1.cmp(k2));
assert_eq!(
result.into_iter()
.map(|(k, v)| (k, FloatApprox(v)))
.collect::<Vec<_>>(),
vec![
(String::from("cpu0"), FloatApprox(0.98)),
(String::from("cpu1"), FloatApprox(0.04)),
(String::from("gpu0"), FloatApprox(13.45)),
],
);
}
#[test]
fn test_by_metric_2() {
let aggr = MetricAggregator{metric: SensorMetric::Load};
let events = &[
Event { timestamp: 0, sensor: String::from("gpu0"), metric: SensorMetric::Load, value: 0.98 },
Event { timestamp: 1, sensor: String::from("gpu0"), metric: SensorMetric::Load, value: 0.04 },
Event { timestamp: 2, sensor: String::from("gpu0"), metric: SensorMetric::Load, value: 13.45 },
];
let mut result = aggr.aggregate(events);
result.sort_by(|(k1, _), (k2, _)| k1.cmp(k2));
assert_eq!(
result.into_iter()
.map(|(k, v)| (k, FloatApprox(v)))
.collect::<Vec<_>>(),
vec![
(String::from("gpu0"), FloatApprox(4.83)),
],
);
}