typing_engine/statistics/
multi_target_position_convert.rs

1use crate::typing_primitive_types::chunk::key_stroke_candidate::KeyStrokeElementCount;
2use crate::utility::convert_by_weighted_count;
3
4#[cfg(test)]
5mod test;
6
7pub(crate) enum BaseTarget {
8    Chunk,
9    Spell,
10    IdealKeyStroke,
11    KeyStroke,
12}
13
14/// A struct that converts the position between chunk, spell in chunk, ideal key stroke sequence,
15/// and key stroke sequence.
16pub(crate) struct MultiTargetDeltaConverter {
17    /// Conversion base.
18    base: BaseTarget,
19    /// Spell count in a target chunk.
20    spell_count: usize,
21    /// Ideal key stroke count in a target chunk.
22    ideal_key_stroke_count: KeyStrokeElementCount,
23    /// Key stroke count in a target chunk.
24    key_stroke_count: KeyStrokeElementCount,
25}
26
27impl MultiTargetDeltaConverter {
28    pub(crate) fn new(
29        spell_count: usize,
30        ideal_key_stroke_count: KeyStrokeElementCount,
31        key_stroke_count: KeyStrokeElementCount,
32        base: BaseTarget,
33    ) -> Self {
34        assert!(spell_count == 1 || spell_count == 2);
35        assert!(
36            !key_stroke_count.is_double() || (key_stroke_count.is_double() && spell_count == 2)
37        );
38        assert!(
39            !ideal_key_stroke_count.is_double()
40                || (ideal_key_stroke_count.is_double() && spell_count == 2)
41        );
42
43        Self {
44            spell_count,
45            ideal_key_stroke_count,
46            key_stroke_count,
47            base,
48        }
49    }
50
51    /// Convert the position delta of base to the position delta of chunk.
52    /// Because the unit of this converter is a chunk, the delta is always 1.
53    pub(crate) fn chunk_delta(&self, base_deltas: &[usize]) -> Vec<usize> {
54        // 他の対象のどこに位置があったとしてもそのチャンク末であることには変わりない
55        base_deltas.iter().map(|_| 1).collect()
56    }
57
58    /// Convert the position delta of base to the position delta of spell.
59    pub(crate) fn spell_delta(&self, base_deltas: &[usize]) -> Vec<usize> {
60        match self.base {
61            BaseTarget::Chunk => base_deltas.iter().map(|_| self.spell_count).collect(),
62            BaseTarget::Spell => base_deltas.to_vec(),
63            BaseTarget::IdealKeyStroke => base_deltas
64                .iter()
65                .map(|ideal_key_stroke_delta| {
66                    self.ideal_key_stroke_count
67                        .convert_key_stroke_delta_to_spell_delta(
68                            self.spell_count,
69                            *ideal_key_stroke_delta,
70                        )
71                })
72                .collect(),
73            BaseTarget::KeyStroke => base_deltas
74                .iter()
75                .map(|key_stroke_delta| {
76                    self.key_stroke_count
77                        .convert_key_stroke_delta_to_spell_delta(
78                            self.spell_count,
79                            *key_stroke_delta,
80                        )
81                })
82                .collect(),
83        }
84    }
85
86    /// Convert the position delta of base to the position delta of ideal key strokes.
87    pub(crate) fn ideal_key_stroke_delta(&self, base_deltas: &[usize]) -> Vec<usize> {
88        match self.base {
89            BaseTarget::Chunk => base_deltas
90                .iter()
91                .map(|_| self.ideal_key_stroke_count.whole_count())
92                .collect(),
93            BaseTarget::Spell => base_deltas
94                .iter()
95                .map(|spell_delta| {
96                    self.ideal_key_stroke_count
97                        .convert_spell_delta_to_key_stroke_delta(self.spell_count, *spell_delta)
98                })
99                .collect(),
100            BaseTarget::IdealKeyStroke => base_deltas.to_vec(),
101            BaseTarget::KeyStroke => base_deltas
102                .iter()
103                .map(|key_stroke_delta| {
104                    convert_between_key_stroke_delta(
105                        &self.key_stroke_count,
106                        &self.ideal_key_stroke_count,
107                        self.spell_count,
108                        *key_stroke_delta,
109                    )
110                })
111                .collect(),
112        }
113    }
114
115    /// Convert the position delta of base to the position delta of key strokes.
116    pub(crate) fn key_stroke_delta(&self, base_deltas: &[usize]) -> Vec<usize> {
117        match self.base {
118            BaseTarget::Chunk => base_deltas
119                .iter()
120                .map(|_| self.key_stroke_count.whole_count())
121                .collect(),
122            BaseTarget::Spell => base_deltas
123                .iter()
124                .map(|spell_delta| {
125                    self.key_stroke_count
126                        .convert_spell_delta_to_key_stroke_delta(self.spell_count, *spell_delta)
127                })
128                .collect(),
129            BaseTarget::IdealKeyStroke => base_deltas
130                .iter()
131                .map(|key_stroke_delta| {
132                    convert_between_key_stroke_delta(
133                        &self.ideal_key_stroke_count,
134                        &self.key_stroke_count,
135                        self.spell_count,
136                        *key_stroke_delta,
137                    )
138                })
139                .collect(),
140            BaseTarget::KeyStroke => base_deltas.to_vec(),
141        }
142    }
143}
144
145/// Convert the position delta between key strokes and ideal key strokes.
146pub(crate) fn convert_between_key_stroke_delta(
147    from: &KeyStrokeElementCount,
148    to: &KeyStrokeElementCount,
149    spell_count: usize,
150    from_delta: usize,
151) -> usize {
152    let pseudo_from_cose = from.construct_pseudo_count_of_spell_elements(spell_count);
153
154    let pseudo_to_cose = to.construct_pseudo_count_of_spell_elements(spell_count);
155
156    let i = pseudo_from_cose.spell_elements_index_of_delta(from_delta);
157
158    let in_spell_element_from_delta = if i == 0 {
159        from_delta
160    } else {
161        assert!(i > 0);
162        from_delta - pseudo_from_cose.key_stroke_count_offset(i)
163    };
164
165    let delta = convert_by_weighted_count(
166        pseudo_from_cose.count_of_spell_elements_index(i),
167        pseudo_to_cose.count_of_spell_elements_index(i),
168        in_spell_element_from_delta,
169    );
170
171    if i > 0 {
172        delta + pseudo_to_cose.key_stroke_count_offset(i)
173    } else {
174        delta
175    }
176}