hot_ranking_algorithm/
lib.rs

1extern crate chrono;
2
3pub use chrono::offset::Utc;
4pub use chrono::DateTime;
5use std::cmp;
6
7const HALF_LIFE: i32 = 45_000;
8
9/// Calculate the relevancy of an item based on upvotes, downvotes, a current
10/// timestamp and the epoch at which the program is first run (e.g. no value can
11/// be lower than that).
12pub fn rank(
13  upvotes: &i32,
14  downvotes: &i32,
15  timestamp: DateTime<Utc>,
16  epoch: DateTime<Utc>,
17) -> i32 {
18  let duration = timestamp.signed_duration_since(epoch);
19  let seconds = duration.num_seconds() as i32;
20
21  let score = upvotes - downvotes;
22  let upper = cmp::max(score, 1) as f32;
23  let order = upper.log(10.0) as i32;
24
25  let sign = match score {
26    n if n > 0 => 1,
27    n if n < 0 => -1,
28    _ => 0,
29  };
30
31  sign * order + seconds / HALF_LIFE
32}
33
34#[cfg(test)]
35mod tests {
36  use super::*;
37  use chrono::prelude::*;
38
39  #[test]
40  fn one_upvote_one_downvote() {
41    let upvotes: i32 = 1;
42    let downvotes: i32 = 1;
43    let epoch = Utc.ymd(2014, 1, 1).and_hms(21, 15, 33);
44    let timestamp = Utc.ymd(2014, 1, 1).and_hms(21, 15, 33);
45
46    let ranking = rank(&upvotes, &downvotes, timestamp, epoch);
47    assert!(ranking == 0);
48  }
49
50  #[test]
51  fn twenty_upvotes_zero_downvotes() {
52    let upvotes: i32 = 50;
53    let downvotes: i32 = 0;
54    let epoch = Utc.ymd(2014, 1, 1).and_hms(21, 15, 33);
55    let timestamp = Utc.ymd(2014, 1, 1).and_hms(21, 15, 33);
56
57    let ranking = rank(&upvotes, &downvotes, timestamp, epoch);
58    assert!(ranking > 0);
59  }
60
61  #[test]
62  fn one_upvote_twenty_downvotes() {
63    let upvotes: i32 = 1;
64    let downvotes: i32 = 1000;
65    let epoch = Utc.ymd(2014, 1, 1).and_hms(21, 15, 33);
66    let timestamp = Utc.ymd(2014, 1, 1).and_hms(21, 15, 33);
67
68    let ranking = rank(&upvotes, &downvotes, timestamp, epoch);
69    println!("{}", ranking);
70    assert!(ranking == 0);
71  }
72}