Cache preferences in memory

[?]
Jan 18, 2022, 11:40 AM
MFGAW2Q2U6VB7A5H2OIYSX7JGB5FYSSBXWPPC5P5HCXKGWKLUEGQC

Dependencies

  • [2] CYDK6S5L Fix bugs
  • [3] 272LZTVT Move terminal cleanup to Drop implementation
  • [4] 5Y7ZXB53 Start picker UI
  • [5] G5YNDTPH Cargo clippy
  • [6] AV73DYWQ Initial functions for using sqlite in async environment
  • [7] LLFG625I Should be all the database functions I need
  • [8] KUANIPWF Add function for adding name to database
  • [9] QEKHTVB7 Get most things semi-working except the final sort
  • [10] AVLXUT3R Make the boxed errors Send
  • [*] HMOBTVJ4 Initialize crate and add expected dependencies
  • [*] RNW6D777 Minor tidy

Change contents

  • edit in src/pick.rs at line 2
    [4.19]
    [4.0]
    use crate::grid::BoolGrid;
  • replacement in src/pick.rs at line 66
    [4.153][3.33:98]()
    let mut comparator = CompareContext::new(&db, &mut stderr)?;
    [4.153]
    [4.1875]
    let mut comparator = CompareContext::new(&db, &mut stderr).await?;
  • replacement in src/pick.rs at line 97
    [2.170][2.170:293]()
    let mut stack: Vec<(*mut (String, i64), *mut (String, i64))> = vec![(&mut shortlist[0], &mut shortlist[len - 1])];
    [2.170]
    [2.293]
    let mut stack: Vec<(*mut (String, i64), *mut (String, i64))> =
    vec![(&mut shortlist[0], &mut shortlist[len - 1])];
  • edit in src/pick.rs at line 109
    [4.2736]
    [2.642]
    std::mem::drop(comparator);
  • replacement in src/pick.rs at line 120
    [4.2908][4.2908:2933]()
    needs_newline: bool,
    [4.2908]
    [3.173]
    cache: BoolGrid,
  • replacement in src/pick.rs at line 131
    [4.2987][3.367:440]()
    fn new(db: &'a AsyncConnection, out: &'a mut W) -> DynResult<Self> {
    [4.2987]
    [3.440]
    async fn new(
    db: &'a AsyncConnection,
    out: &'a mut W,
    ) -> DynResult<CompareContext<'a, W>> {
    let mut cache = BoolGrid::new();
    for (x, y) in db.list_preferences().await.unwrap_or_else(|_| Vec::new())
    {
    cache.set_transitive(x as usize, y as usize);
    }
  • replacement in src/pick.rs at line 146
    [3.605][3.605:639]()
    needs_newline: false,
    [3.605]
    [3.639]
    cache,
  • replacement in src/pick.rs at line 154
    [4.3101][4.3101:3140]()
    mut pivot: *mut (String, i64),
    [4.3101]
    [4.3140]
    pivot: *mut (String, i64),
  • edit in src/pick.rs at line 156
    [4.3181]
    [4.3181]
    use std::cmp::Ordering::*;
  • edit in src/pick.rs at line 159
    [4.3250]
    [4.3250]
    let pivot = (&*pivot).clone();
  • replacement in src/pick.rs at line 161
    [4.3265][4.3265:3284]()
    loop {
    [4.3265]
    [4.3284]
    'l: loop {
  • replacement in src/pick.rs at line 163
    [4.3317][4.3317:3375](),[4.3375][2.760:815](),[2.815][4.3427:3459](),[4.3427][4.3427:3459]()
    match self.compare(&*i, &*pivot).await? {
    std::cmp::Ordering::Greater => (),
    _ => break,
    [4.3317]
    [4.3459]
    if self.compare(&*i, &pivot).await? != Greater {
    break 'l;
  • replacement in src/pick.rs at line 167
    [4.3491][4.3491:3510]()
    loop {
    [4.3491]
    [4.3510]
    'r: loop {
  • replacement in src/pick.rs at line 169
    [4.3544][4.3544:3602](),[4.3602][2.816:868](),[2.868][4.3657:3689](),[4.3657][4.3657:3689]()
    match self.compare(&*j, &*pivot).await? {
    std::cmp::Ordering::Less => (),
    _ => break,
    [4.3544]
    [4.3689]
    if self.compare(&*j, &pivot).await? != Less {
    break 'r;
  • edit in src/pick.rs at line 176
    [4.3796][4.3796:3947]()
    if pivot == i {
    pivot = j;
    } else if pivot == j {
    pivot = i;
    }
  • replacement in src/pick.rs at line 186
    [4.4141][4.4141:4219]()
    let (a, b) = (a, b);
    match self.db.compare(a.1, b.1).await? {
    [4.4141]
    [4.4219]
    match self.cache.compare(a.1 as usize, b.1 as usize) {
  • edit in src/pick.rs at line 188
    [4.4241][4.4241:4435]()
    if self.needs_newline {
    self.out.queue(cursor::MoveToNextLine(1))?;
    } else {
    self.needs_newline = true;
    }
  • replacement in src/pick.rs at line 228
    [4.6354][4.6354:6522]()
    let tdb = self.db.clone();
    tokio::spawn(async move {
    tdb.insert_preference(better, worse).await
    });
    [4.6354]
    [4.6522]
    self.out.queue(style::Print('\n'))?;
    self.out.execute(cursor::MoveToColumn(0))?;
    self.cache.set_transitive(better as usize, worse as usize);
    {
    let tdb = self.db.clone();
    tokio::spawn(async move {
    tdb.insert_preference(better as i64, worse as i64).await
    });
    }
  • replacement in src/names_database.rs at line 78
    [4.968][4.968:2180]()
    pub async fn compare(
    &self,
    a: i64,
    b: i64,
    ) -> DynResult<Option<std::cmp::Ordering>> {
    if a == b {
    return Ok(Some(std::cmp::Ordering::Equal));
    }
    Ok(self.post(move |conn| -> DynResult<Option<std::cmp::Ordering>> {
    let mut stmnt = conn.prepare("SELECT better, worse FROM preferences WHERE (better = ? AND worse = ?) OR (better = ? AND worse = ?)")?;
    stmnt.bind::<i64>(1,a)?;
    stmnt.bind::<i64>(2,b)?;
    stmnt.bind::<i64>(3,b)?;
    stmnt.bind::<i64>(4,a)?;
    match stmnt.next()? {
    sqlite::State::Done => Ok(None),
    sqlite::State::Row => {
    let better = stmnt.read::<i64>(0)?;
    let worse = stmnt.read::<i64>(1)?;
    if better == a && worse == b {
    Ok(Some(std::cmp::Ordering::Greater))
    } else if better == b && worse == a {
    Ok(Some(std::cmp::Ordering::Less))
    } else {
    Ok(Some(std::cmp::Ordering::Equal))
    }
    }
    }
    }).await??)
    }
    [4.968]
    [4.2180]
    // pub async fn compare(
    // &self,
    // a: i64,
    // b: i64,
    // ) -> DynResult<Option<std::cmp::Ordering>> {
    // if a == b {
    // return Ok(Some(std::cmp::Ordering::Equal));
    // }
    // Ok(self.post(move |conn| -> DynResult<Option<std::cmp::Ordering>> {
    // let mut stmnt = conn.prepare("SELECT better, worse FROM preferences WHERE (better = ? AND worse = ?) OR (better = ? AND worse = ?)")?;
    // stmnt.bind::<i64>(1,a)?;
    // stmnt.bind::<i64>(2,b)?;
    // stmnt.bind::<i64>(3,b)?;
    // stmnt.bind::<i64>(4,a)?;
    // match stmnt.next()? {
    // sqlite::State::Done => Ok(None),
    // sqlite::State::Row => {
    // let better = stmnt.read::<i64>(0)?;
    // let worse = stmnt.read::<i64>(1)?;
    // if better == a && worse == b {
    // Ok(Some(std::cmp::Ordering::Greater))
    // } else if better == b && worse == a {
    // Ok(Some(std::cmp::Ordering::Less))
    // } else {
    // Ok(Some(std::cmp::Ordering::Equal))
    // }
    // }
    // }
    // }).await??)
    // }
  • replacement in src/names_database.rs at line 131
    [4.2735][4.6613:6649](),[4.6649][4.2767:2943](),[4.2767][4.2767:2943]()
    pub async fn insert_preference(
    &self,
    better: i64,
    worse: i64,
    ) -> DynResult<()> {
    self.insert_one_preference(better, worse).await?;
    self.make_transitive().await
    [4.2735]
    [4.2943]
    pub async fn list_preferences(&self) -> DynResult<Vec<(i64, i64)>> {
    self.post(|conn| {
    let mut stmnt =
    conn.prepare("SELECT better, worse FROM preferences")?;
    let mut rows = Vec::new();
    while let sqlite::State::Row = stmnt.next()? {
    rows.push((stmnt.read::<i64>(0)?, stmnt.read::<i64>(1)?));
    }
    Ok(rows)
    })
    .await?
  • replacement in src/names_database.rs at line 144
    [4.2950][4.2950:2986]()
    async fn insert_one_preference(
    [4.2950]
    [4.2986]
    pub async fn insert_preference(
  • edit in src/names_database.rs at line 159
    [4.3398][4.3398:4938](),[4.4938][4.1261:1271](),[4.1261][4.1261:1271]()
    }
    pub async fn make_transitive(&self) -> DynResult<()> {
    loop {
    let todo = self
    .post(|db| -> DynResult<_> {
    let mut stmnt = db.prepare(
    "SELECT a.better, b.worse
    FROM preferences AS a
    INNER JOIN preferences as b ON a.worse = b.better
    WHERE NOT EXISTS (
    SELECT rowid FROM preferences AS x
    WHERE x.better = a.better
    AND x.worse = b.worse
    )
    ",
    )?;
    let mut rows = Vec::new();
    while let sqlite::State::Row = stmnt.next()? {
    rows.push((
    stmnt.read::<i64>(0)?,
    stmnt.read::<i64>(1)?,
    ));
    }
    Ok(rows)
    })
    .await??;
    if todo.is_empty() {
    return Ok(());
    } else {
    let handles: Vec<_> = todo
    .into_iter()
    .map(|(better, worse)| {
    let target = self.clone();
    tokio::spawn(async move {
    target.insert_one_preference(better, worse).await
    })
    })
    .collect();
    for handle in handles {
    handle.await??;
    }
    }
    }
  • edit in src/main.rs at line 4
    [13.222]
    [4.1990]
    mod grid;
  • file addition: grid.rs (----------)
    [12.15]
    #[derive(Debug)]
    pub struct BoolGrid {
    width: usize,
    content: Vec<bool>,
    }
    impl BoolGrid {
    pub fn new() -> Self {
    Self {
    width: 0,
    content: Vec::new(),
    }
    }
    pub fn column<'a>(&'a self, x: usize) -> BoolColumn<'a> {
    BoolColumn::new(self, x)
    }
    pub fn row<'a>(&'a self, y: usize) -> BoolRow<'a> {
    BoolRow::new(self, y)
    }
    pub fn set_transitive(
    &mut self,
    x: usize,
    y: usize,
    ) -> Vec<(usize, usize)> {
    use std::collections::VecDeque;
    let mut q = VecDeque::new();
    q.push_back((x, y));
    let mut result = Vec::new();
    while let Some((x, y)) = q.pop_front() {
    let t = &mut self[(x, y)];
    if !*t {
    *t = true;
    result.push((x, y));
    for (c, v) in self.row(x).enumerate() {
    if v {
    q.push_back((c, y));
    }
    }
    for (c, v) in self.column(y).enumerate() {
    if v {
    q.push_back((x, c));
    }
    }
    }
    }
    result
    }
    pub fn compare(&self, x: usize, y: usize) -> Option<std::cmp::Ordering> {
    use std::cmp::Ordering::*;
    if x == y {
    Some(Equal)
    } else {
    match (self[(x, y)], self[(y, x)]) {
    (true, true) => Some(Equal),
    (true, false) => Some(Greater),
    (false, true) => Some(Less),
    (false, false) => None,
    }
    }
    }
    }
    #[test]
    fn transitive_works() {
    let mut grid = BoolGrid::new();
    grid.set_transitive(1, 2);
    grid.set_transitive(14, 15);
    grid.set_transitive(2, 3);
    grid.set_transitive(10, 11);
    grid.set_transitive(4, 5);
    grid.set_transitive(12, 13);
    grid.set_transitive(15, 16);
    grid.set_transitive(6, 7);
    grid.set_transitive(13, 14);
    grid.set_transitive(5, 6);
    grid.set_transitive(9, 10);
    grid.set_transitive(11, 12);
    grid.set_transitive(8, 9);
    for x in 0..=16 {
    for y in 0..=16 {
    assert_eq!(
    x < y
    && [1..=3, 4..=7, 8..=16]
    .into_iter()
    .any(|i| i.contains(&x) && i.contains(&y)),
    grid[(x, y)]
    );
    }
    }
    }
    #[test]
    fn row_iterator() {
    let mut grid = BoolGrid::new();
    grid[(5, 4)] = true;
    let row: Vec<_> = grid.row(4).collect();
    assert_eq!(&row[..], &[false, false, false, false, false, true]);
    assert!(grid.row(5).all(|v| !v));
    }
    #[test]
    fn col_iterator() {
    let mut grid = BoolGrid::new();
    grid[(5, 4)] = true;
    let row: Vec<_> = grid.column(5).collect();
    assert_eq!(&row[..], &[false, false, false, false, true]);
    assert!(grid.column(6).all(|v| !v));
    }
    #[test]
    fn test_compare() {
    use std::cmp::Ordering::*;
    let mut grid = BoolGrid::new();
    let mut set = Vec::new();
    set.extend(grid.set_transitive(1, 2).into_iter());
    set.extend(grid.set_transitive(2, 3).into_iter());
    assert_eq!(grid.compare(3, 4), None);
    assert_eq!(grid.compare(1, 3), Some(Greater));
    assert_eq!(grid.compare(3, 1), Some(Less));
    assert_eq!(grid.compare(2, 2), Some(Equal));
    assert!(set.iter().all(|(a, b)| a < b));
    }
    #[test]
    fn test_transitive_bug() {
    let mut grid = BoolGrid::new();
    let mut set = Vec::new();
    for (a, b) in [(1, 4), (4, 2), (8, 4), (3, 4), (4, 7), (4, 6), (4, 12)] {
    set.extend(grid.set_transitive(a, b));
    }
    for (a, b) in set {
    assert!(a != 11);
    assert!(b != 11);
    }
    for x in 1..=12 {
    assert!(!grid[(11, x)]);
    assert!(!grid[(x, 11)]);
    }
    }
    #[test]
    fn test_rows_cols_bug() {
    use std::collections::HashSet;
    let mut grid = BoolGrid::new();
    let mut set = HashSet::new();
    grid.set_transitive(1, 4);
    set.insert((1, 4));
    for x in 0..=5 {
    for y in 0..=5 {
    assert_eq!(grid[(x, y)], (x, y) == (1, 4));
    }
    }
    let c: Vec<_> = grid.column(3).collect();
    println!("column 3: {:?}", c);
    for x in 0..12 {
    for (y, v) in grid.column(x).enumerate() {
    if v != set.contains(&(x, y)) {
    panic!("x: {}, y: {}, v: {}", x, y, v);
    }
    }
    }
    }
    static OFF_GRID: bool = false;
    impl std::ops::Index<(usize, usize)> for BoolGrid {
    type Output = bool;
    fn index(&self, (x, y): (usize, usize)) -> &bool {
    if x >= self.width {
    return &OFF_GRID;
    }
    let ix = self.width * y + x;
    if ix >= self.content.len() {
    return &OFF_GRID;
    }
    <Vec<bool> as std::ops::Index<usize>>::index(&self.content, ix)
    }
    }
    impl std::ops::IndexMut<(usize, usize)> for BoolGrid {
    fn index_mut(&mut self, (x, y): (usize, usize)) -> &mut bool {
    let height = self.content.len() / self.width.max(1);
    let rq_width = self.width.max(x + 1);
    let rq_height = (y + 1).max(height);
    if (self.width, height) != (rq_width, rq_height) {
    struct Resize {
    old_content: Vec<bool>,
    old_width: usize,
    new_width: usize,
    new_height: usize,
    x: usize,
    y: usize,
    }
    impl Resize {
    fn new(
    content: Vec<bool>,
    old_width: usize,
    new_width: usize,
    new_height: usize,
    ) -> Self {
    Self {
    old_content: content,
    old_width,
    new_width,
    new_height,
    x: 0,
    y: 0,
    }
    }
    }
    impl Iterator for Resize {
    type Item = bool;
    fn next(&mut self) -> Option<bool> {
    if self.y >= self.new_height {
    return None;
    }
    let result = if self.x >= self.old_width {
    Some(false)
    } else {
    let ix = self.y * self.old_width + self.x;
    if ix >= self.old_content.len() {
    Some(false)
    } else {
    Some(self.old_content[ix])
    }
    };
    let z = if self.x < self.new_width - 1 {
    (self.x + 1, self.y)
    } else {
    (0, self.y + 1)
    };
    self.x = z.0;
    self.y = z.1;
    result
    }
    fn size_hint(&self) -> (usize, Option<usize>) {
    let r =
    self.new_width * (self.new_height - self.y) - self.x;
    (r, Some(r))
    }
    }
    self.content = Resize::new(
    std::mem::replace(&mut self.content, Vec::new()),
    self.width,
    rq_width,
    rq_height,
    )
    .collect();
    self.width = rq_width;
    }
    <Vec<bool> as std::ops::IndexMut<usize>>::index_mut(
    &mut self.content,
    self.width * y + x,
    )
    }
    }
    pub struct BoolColumn<'a> {
    grid: &'a BoolGrid,
    ix: usize,
    }
    impl<'a> BoolColumn<'a> {
    fn new(grid: &'a BoolGrid, x: usize) -> Self {
    if x < grid.width {
    Self { grid, ix: x }
    } else {
    Self {
    grid,
    ix: usize::MAX,
    }
    }
    }
    }
    impl<'a> Iterator for BoolColumn<'a> {
    type Item = bool;
    fn next(&mut self) -> Option<bool> {
    if self.ix < self.grid.content.len() {
    let result = self.grid.content[self.ix];
    self.ix += self.grid.width;
    Some(result)
    } else {
    None
    }
    }
    fn size_hint(&self) -> (usize, Option<usize>) {
    let len = self.grid.content.len();
    let result = (len - self.ix) / len;
    (result, Some(result))
    }
    }
    pub struct BoolRow<'a> {
    grid: &'a BoolGrid,
    ix: usize,
    end: usize,
    }
    impl<'a> BoolRow<'a> {
    fn new(grid: &'a BoolGrid, y: usize) -> Self {
    Self {
    grid,
    ix: y * grid.width,
    end: (y + 1) * grid.width,
    }
    }
    }
    impl<'a> Iterator for BoolRow<'a> {
    type Item = bool;
    fn next(&mut self) -> Option<bool> {
    if self.ix < self.end && self.ix < self.grid.content.len() {
    let result = self.grid.content[self.ix];
    self.ix += 1;
    Some(result)
    } else {
    None
    }
    }
    fn size_hint(&self) -> (usize, Option<usize>) {
    let result = self.end - self.ix;
    (result, Some(result))
    }
    }