1 | **use** **crate**::stats::float::Float; |

2 | **use** cast::{self, usize}; |

3 | |

4 | */// A "view" into the percentiles of a sample* |

5 | **pub** **struct** Percentiles<A>(Box<[A]>) |

6 | **where** |

7 | A: Float; |

8 | |

9 | *// TODO(rust-lang/rfcs#735) move this `impl` into a private percentiles module* |

10 | **impl**<A> Percentiles<A> |

11 | **where** |

12 | A: Float, |

13 | **usize**: cast::From<A, Output = Result<**usize**, cast::Error>>, |

14 | { |

15 | */// Returns the percentile at `p`%* |

16 | *///* |

17 | */// Safety:* |

18 | *///* |

19 | */// - Make sure that `p` is in the range `[0, 100]`* |

20 | **unsafe** **fn** at_unchecked(&self, p: A) -> A { |

21 | **let** _100 = A::cast(`100`); |

22 | debug_assert!(p >= A::cast(`0`) && p <= _100); |

23 | debug_assert!(**self**.`0`.len() > `0`); |

24 | **let** len = self.0.len() - `1`; |

25 | |

26 | **if** p == _100 { |

27 | self.0[len] |

28 | } **else** { |

29 | **let** rank = (p / _100) * A::cast(len); |

30 | **let** integer = rank.floor(); |

31 | **let** fraction = rank - integer; |

32 | **let** n = usize(integer).unwrap(); |

33 | **let** &floor = self.0.get_unchecked(n); |

34 | **let** &ceiling = self.0.get_unchecked(n + `1`); |

35 | |

36 | floor + (ceiling - floor) * fraction |

37 | } |

38 | } |

39 | |

40 | */// Returns the percentile at `p`%* |

41 | *///* |

42 | */// # Panics* |

43 | *///* |

44 | */// Panics if `p` is outside the closed `[0, 100]` range* |

45 | **pub** **fn** at(&self, p: A) -> A { |

46 | **let** _0 = A::cast(`0`); |

47 | **let** _100 = A::cast(`100`); |

48 | |

49 | assert!(p >= _0 && p <= _100); |

50 | assert!(**self**.`0`.len() > `0`); |

51 | |

52 | **unsafe** { self.at_unchecked(p) } |

53 | } |

54 | |

55 | */// Returns the interquartile range* |

56 | **pub** **fn** iqr(&self) -> A { |

57 | **let** q1 = self.at(A::cast(`25`)); |

58 | **let** q3 = self.at(A::cast(`75`)); |

59 | |

60 | q3 - q1 |

61 | } |

62 | |

63 | */// Returns the 50th percentile* |

64 | **pub** **fn** median(&self) -> A { |

65 | self.at(A::cast(`50`)) |

66 | } |

67 | |

68 | */// Returns the 25th, 50th and 75th percentiles* |

69 | **pub** **fn** quartiles(&self) -> (A, A, A) { |

70 | ( |

71 | self.at(A::cast(`25`)), |

72 | self.at(A::cast(`50`)), |

73 | self.at(A::cast(`75`)), |

74 | ) |

75 | } |

76 | } |

77 | |