perky 0.1.5

An application to permute and score keyboard layouts.
Documentation
use crate::layouts::{Laterality, LayoutTable, Position};

pub type Effort = f64;

pub type Fingering = (usize, usize, Laterality, Position);

pub type UnigramFingering = (Fingering, Effort);

pub type BigramFingering = (Fingering, Fingering, Effort);

pub type TrigramFingering = (Fingering, Fingering, Fingering, Effort);

#[inline]
fn fast_distance(r1: usize, c1: usize, r2: usize, c2: usize) -> f64 {
    let dx = r2.abs_diff(r1);
    let dy = c2.abs_diff(c1);
    match (dx, dy) {
        (0, 0) => 0.0,
        (0, d) | (d, 0) => d as f64,
        _ => ((dx * dx + dy * dy) as f64).sqrt(),
    }
}

impl<const C: usize, const R: usize> LayoutTable<C, R> {
    pub fn iter_f(&self) -> impl Iterator<Item = UnigramFingering> {
        (0..R).flat_map(move |r| {
            (0..C).filter_map(move |c| {
                self.0[r][c].map(|digit| {
                    let effort = 1.0;
                    ((r, c, digit.0, digit.1), effort)
                })
            })
        })
    }

    pub fn iter_fp(&self) -> impl Iterator<Item = BigramFingering> {
        self.iter_f().flat_map(move |(f1, _effort)| {
            self.iter_f().map(move |(f2, _effort)| {
                let (r1, c1, l1, _p1) = f1;
                let (r2, c2, l2, _p2) = f2;
                let effort = if l1 == l2 {
                    fast_distance(r1, c1, r2, c2)
                } else {
                    1.0
                };
                (f1, f2, effort)
            })
        })
    }

    pub fn iter_ft(&self) -> impl Iterator<Item = TrigramFingering> {
        self.iter_f().flat_map(move |(f1, _)| {
            self.iter_f().flat_map(move |(f2, _)| {
                self.iter_f().map(move |(f3, _)| {
                    let (r1, c1, l1, _p1) = f1;
                    let (r2, c2, l2, _p2) = f2;
                    let (r3, c3, l3, _p3) = f3;
                    let effort = if l1 == l2 {
                        fast_distance(r1, c1, r2, c2)
                    } else {
                        1.0
                    } * if l2 == l3 {
                        fast_distance(r2, c2, r3, c3)
                    } else {
                        1.0
                    };
                    (f1, f2, f3, effort)
                })
            })
        })
    }
}