1use std::any::Any;
171use std::cell::RefCell;
172use std::rc::Rc;
173
174use super::Rustics;
175use super::Histogram;
176use super::PrintOption;
177use super::hier_box;
178use super::running_integer::RunningInteger;
179use crate::running_integer::IntegerExporter;
180use super::integer_window::IntegerWindow;
181
182use crate::Hier;
183use crate::HierDescriptor;
184use crate::HierConfig;
185use crate::HierGenerator;
186use crate::HierMember;
187use crate::HierExporter;
188use crate::ExporterRc;
189use crate::MemberRc;
190use crate::hier_item;
191
192impl HierMember for RunningInteger {
196 fn to_rustics(&self) -> &dyn Rustics {
197 self
198 }
199
200 fn to_rustics_mut(&mut self) -> &mut dyn Rustics {
201 self
202 }
203
204 fn as_any(&self) -> &dyn Any {
205 self
206 }
207
208 fn as_any_mut(&mut self) -> &mut dyn Any {
209 self
210 }
211
212 fn to_histogram(&self) -> &dyn Histogram {
213 self
214 }
215}
216
217#[derive(Default)]
224pub struct IntegerHier {
225}
226
227pub struct IntegerHierConfig {
234 pub descriptor: HierDescriptor,
235 pub name: String,
236 pub print_opts: PrintOption,
237 pub window_size: Option<usize>,
238}
239
240impl IntegerHier {
241 pub fn new() -> IntegerHier {
245 IntegerHier { }
246 }
247
248 pub fn new_hier(configuration: IntegerHierConfig) -> Hier {
253 let generator = IntegerHier::new();
254 let generator = Rc::from(RefCell::new(generator));
255 let class = "integer".to_string();
256
257 let descriptor = configuration.descriptor;
258 let name = configuration.name;
259 let print_opts = configuration.print_opts;
260 let window_size = configuration.window_size;
261
262 let config = HierConfig { descriptor, generator, name, window_size, class, print_opts };
263
264 Hier::new(config)
265 }
266}
267
268impl HierGenerator for IntegerHier {
274 fn make_member(&self, name: &str, print_opts: &PrintOption) -> MemberRc {
275 let member = RunningInteger::new(name, print_opts);
276
277 hier_box!(member)
278 }
279
280 fn make_window(&self, name: &str, window_size: usize, print_opts: &PrintOption)
281 -> Box<dyn Rustics> {
282 let window = IntegerWindow::new(name, window_size, print_opts);
283
284 Box::new(window)
285 }
286
287 fn make_from_exporter(&self, name: &str, print_opts: &PrintOption, exporter: ExporterRc)
290 -> MemberRc {
291 let mut exporter_borrow = exporter.borrow_mut();
292 let exporter_any = exporter_borrow.as_any_mut();
293 let exporter_impl = exporter_any.downcast_mut::<IntegerExporter>().unwrap();
294 let member = exporter_impl.make_member(name, print_opts);
295
296 hier_box!(member)
297 }
298
299 fn make_exporter(&self) -> ExporterRc {
300 let exporter = IntegerExporter::new();
301
302 Rc::from(RefCell::new(exporter))
303 }
304
305 fn push(&self, exporter: &mut dyn HierExporter, member_rc: MemberRc) {
309 let exporter_any = exporter.as_any_mut();
310 let exporter_impl = exporter_any.downcast_mut::<IntegerExporter>().unwrap();
311
312 let member_borrow = hier_item!(member_rc);
313 let member_any = member_borrow.as_any();
314 let member_impl = member_any.downcast_ref::<RunningInteger>().unwrap();
315
316 exporter_impl.push(member_impl.export_data());
317 }
318
319 fn hz(&self) -> u128 {
320 panic!("IntegerHier::hz: not supported");
321 }
322}
323
324#[cfg(test)]
325pub mod tests {
326 use super::*;
327 use crate::LogHistogramBox;
328 use crate::hier_item_mut;
329 use crate::hier::HierDescriptor;
330 use crate::hier::HierDimension;
331 use crate::PrintOpts;
332 use crate::tests::check_printer_box;
333 use crate::tests::bytes;
334 use crate::integer_window::AnalyzeData;
335
336 fn level_0_period() -> usize {
337 8
338 }
339
340 fn level_0_retain() -> usize {
341 3 * level_0_period()
342 }
343
344 pub fn make_test_hier(auto_next: i64, window_size: Option<usize>, print_opts: PrintOption) -> Hier {
345 let levels = 4;
346 let level_0_period = level_0_period();
347 let dimension = HierDimension::new(level_0_period, level_0_retain());
348 let mut dimensions = Vec::<HierDimension>::with_capacity(levels);
349
350 dimensions.push(dimension);
353
354 let mut period = 4;
357
358 for _i in 1..levels {
359 let dimension = HierDimension::new(period, 3 * period);
360
361 dimensions.push(dimension);
362
363 period += 2;
364 }
365
366 let descriptor = HierDescriptor::new(dimensions, Some(auto_next));
367 let generator = IntegerHier::new();
368 let generator = Rc::from(RefCell::new(generator));
369 let class = "integer".to_string();
370 let name = "test hier".to_string();
371 let print_opts = print_opts;
372
373 let configuration =
374 HierConfig { descriptor, generator, class, name, window_size, print_opts };
375
376 Hier::new(configuration)
377 }
378
379 fn test_generator() {
382 let generator = IntegerHier::new();
385 let member_rc = generator.make_member("test member", &None);
386 let member_clone = member_rc.clone();
387 let mut member = member_clone.borrow_mut();
388 let value = 42;
389
390 member.to_rustics_mut().record_i64(value);
391
392 assert!(member.to_rustics().count() == 1);
393 assert!(member.to_rustics().mean() == value as f64);
394
395 drop(member);
398
399 let exporter_rc = generator.make_exporter();
402 let exporter_clone = exporter_rc.clone();
403
404 generator.push(&mut *exporter_clone.borrow_mut(), member_rc);
407
408 let name = "member export";
409
410 let new_member_rc = generator.make_from_exporter(name, &None, exporter_rc);
411
412 let new_member = hier_item!(new_member_rc);
415
416 assert!(new_member.to_rustics().count() == 1);
417 assert!(new_member.to_rustics().mean() == value as f64);
418
419 let auto_next = 200;
422 let mut hier = make_test_hier(auto_next, None, None);
423 let mut events = 0;
424
425 for i in 1..auto_next / 2 {
426 hier.record_i64(i);
427
428 events += 1;
429 }
430
431 let float = events as f64;
432 let mean = (float * (float + 1.0) / 2.0) / float;
433
434 assert!(hier.mean() == mean );
435 assert!(hier.event_count() == events);
436 hier.print();
437 }
438
439 fn test_window() {
440 let auto_next = 100;
441 let window_size = Some(1000);
442 let mut hier = make_test_hier(auto_next, window_size, None);
443 let period = level_0_period();
444 let window_size = window_size.unwrap() as i64;
445 let mut events = 0 as i64;
446
447 assert!( hier.int_extremes ());
448 assert!(!hier.float_extremes());
449
450 for i in 0..window_size {
451 let sample = i + 1;
452
453 hier.record_i64(sample);
454 events += 1;
455
456 assert!(hier.count() == events as u64);
457 assert!(hier.min_i64() == 1 );
458 assert!(hier.max_i64() == sample );
459
460 let level_0_pushes = (events + auto_next - 1) / auto_next;
461 let level_0_all = std::cmp::min(level_0_pushes, level_0_retain() as i64);
462 let level_0_live = std::cmp::min(level_0_pushes, level_0_period() as i64);
463
464 assert!(hier.all_len (0) == level_0_all as usize);
465 assert!(hier.live_len(0) == level_0_live as usize);
466
467 if hier.all_len(0) > period {
468 assert!(hier.all_len(1) > 0);
469 }
470 }
471
472 let sum = (window_size * (window_size + 1)) / 2;
475 let sum = sum as f64;
476 let count = events as f64;
477 let mean = sum / count;
478
479 assert!(hier.count() == events as u64);
482 assert!(hier.mean() == mean );
483 assert!(hier.event_count() == events );
484
485 hier.record_i64(window_size + 1);
488 events += 1;
489
490 assert!(hier.count() == window_size as u64);
491
492 {
493 let current_rc = hier.current();
494 let current = hier_item!(current_rc);
495 let histogram = current.to_histogram();
496 let histogram = histogram.to_log_histogram().unwrap();
497 let histogram = histogram.borrow();
498
499 let mut sum = 0;
500
501 for sample in histogram.positive.iter() {
502 sum += *sample;
503 }
504
505 let expected = events % window_size;
506
507 assert!(expected != 0);
508
509 println!("test_window: got {}, expected {}", sum, expected);
510 assert!(sum == expected as u64);
511 }
512 }
513
514 fn test_exporter() {
515 let mut exporter = IntegerExporter::new();
516 let running = RunningInteger::new("Test Stat", &None);
517 let export = running.export_data();
518
519 exporter.push(export.clone());
520 exporter.push(export.clone());
521 exporter.push(export.clone());
522
523 assert!(exporter.count() == 3);
527
528 let any = exporter.as_any();
529 let any_exporter = any.downcast_ref::<IntegerExporter>().unwrap();
530
531 assert!(any_exporter.count() == 3);
532
533 let any = exporter.as_any_mut();
534 let any_exporter = any.downcast_ref::<IntegerExporter>().unwrap();
535
536 assert!(any_exporter.count() == 3);
537 }
538
539 #[test]
540 #[should_panic]
541 fn hz_panic() {
542 let hier = make_test_hier(200, None, None);
543 let _ = hier.hz();
544 }
545
546 fn test_print_output() {
547 let expected =
548 [
549 "test hier",
550 " Count 1,000 ",
551 " Minimum 1 byte",
552 " Maximum 1,000 bytes",
553 " Log Mode 10 ",
554 " Mode Value 768 bytes",
555 " Mean +5.00500 e+2 bytes",
556 " Std Dev +2.88819 e+2 bytes",
557 " Variance +8.34166 e+4 ",
558 " Skewness +0.00000 e+0 ",
559 " Kurtosis -1.20000 e+0 ",
560 " Log Histogram",
561 " -----------------------",
562 " 0: 1 1 2 4",
563 " 4: 8 16 32 64",
564 " 8: 128 256 488 0",
565 ""
566 ];
567
568 let printer = Some(check_printer_box(&expected, true, false));
569 let title = None;
570 let units = bytes();
571 let histo_opts = None;
572 let print_opts = Some(PrintOpts { printer, title, units, histo_opts });
573 let samples = 1000;
574 let mut stats = make_test_hier(samples, Some(samples as usize), print_opts);
575
576 for i in 1..=samples {
577 stats.record_i64(i as i64);
578 }
579
580 stats.print();
581 }
582
583 pub fn get_analyze_data(count: i64) -> AnalyzeData {
584 let mut stats = IntegerWindow::new("Analyze", count as usize, &None);
585
586 for i in 1..=count {
587 stats.record_i64(i);
588 }
589
590 stats.analyze()
591 }
592
593 pub fn verify_log_histogram(export: &LogHistogramBox, expected: &LogHistogramBox)
594 -> bool {
595 let export = export .borrow();
596 let expected = expected.borrow();
597
598 export.equals(&expected)
599 }
600
601 pub fn get_analyze_histogram(count: i64) -> LogHistogramBox {
602 let mut stat = IntegerWindow::new("Analyze Histo", count as usize, &None);
603
604 for i in 1..=count {
605 stat.record_i64(i as i64);
606 }
607
608 stat.to_log_histogram().unwrap()
609 }
610
611 fn test_integer_sum() {
616 let mut exporter = IntegerExporter::new();
617 let generator = IntegerHier ::new();
618
619 let stats_1 = generator.make_member("Test Stat 1", &None);
620 let stats_2 = generator.make_member("Test Stat 2", &None);
621 let stats_3 = generator.make_member("Test Stat 3", &None);
622 let stats_4 = generator.make_member("Test Stat 4", &None);
623
624 let samples = 250;
625 let count = 4;
626
627 for i in 0..samples {
628 let sample = i as i64 + 1;
629
630 hier_item_mut!(stats_1).to_rustics_mut().record_i64(sample );
631 hier_item_mut!(stats_2).to_rustics_mut().record_i64(sample + samples);
632 hier_item_mut!(stats_3).to_rustics_mut().record_i64(sample + 2 * samples);
633 hier_item_mut!(stats_4).to_rustics_mut().record_i64(sample + 3 * samples);
634 }
635
636 generator.push(&mut exporter, stats_1);
637 generator.push(&mut exporter, stats_2);
638 generator.push(&mut exporter, stats_3);
639 generator.push(&mut exporter, stats_4);
640
641 let exporter = Rc::from(RefCell::new(exporter));
644 let sum = generator.make_from_exporter("Test Sum", &None, exporter);
645
646 let borrow = hier_item!(sum);
651 let borrow = borrow.to_rustics();
652 let running = borrow.generic().downcast_ref::<RunningInteger>().unwrap();
653
654 assert!(borrow.count() as i64 == count * samples);
655
656 let expected = get_analyze_data(count * samples);
657 let export = running.export_data();
658 let expected_mean = expected.sum / expected.n;
659 let export_count = export.count as f64;
660 let export_histo = export.log_histogram.unwrap();
661
662 assert!(export_count == expected.n );
663 assert!(export.mean == expected_mean );
664 assert!(export.moment_2 == expected.moment_2);
665 assert!(export.min_i64 == expected.min_i64 );
666 assert!(export.max_i64 == expected.max_i64 );
667
668 let cubes_error = (export.cubes - expected.cubes).abs();
672 let cubes_tolerance = cubes_error / expected.cubes;
673
674 let moment_4_error = (export.moment_4 - expected.moment_4).abs();
675 let moment_4_tolerance = moment_4_error / expected.moment_4;
676
677 println!("test_integer_sum: export cubes {}, expected {}, error {}",
678 export.cubes, expected.cubes, cubes_tolerance);
679 println!("test_integer_sum: export moment_4 {}, expected {}, error {}",
680 export.moment_4, expected.moment_4, moment_4_tolerance);
681
682 assert!(cubes_tolerance < 0.01);
683 assert!(moment_4_tolerance < 0.06);
684
685 let expected_histo = get_analyze_histogram(count * samples);
689
690 assert!(verify_log_histogram(&export_histo, &expected_histo));
691 }
692
693 #[test]
694 fn run_tests() {
695 test_generator ();
696 test_exporter ();
697 test_print_output();
698 test_integer_sum ();
699 test_window ();
700 }
701}