Skip to content

Commit e0f9221

Browse files
committed
doubling
1 parent 6355177 commit e0f9221

2 files changed

Lines changed: 273 additions & 0 deletions

File tree

libs/doubling/Cargo.toml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
[package]
2+
name = "doubling"
3+
version = "0.1.0"
4+
authors = ["ia7ck <23146842+ia7ck@users.noreply.github.com>"]
5+
edition = "2024"
6+
license = "CC0-1.0"
7+
8+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
9+
10+
[dependencies]
11+
12+
[dev-dependencies]
13+
proptest = "1.9.0"

libs/doubling/src/lib.rs

Lines changed: 260 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,260 @@
1+
/// ダブリング
2+
///
3+
/// # Examples
4+
///
5+
/// ```
6+
/// use doubling::{Doubling, Transition, Value};
7+
///
8+
/// #[derive(Debug, PartialEq)]
9+
/// struct Sum(i64);
10+
///
11+
/// impl Value for Sum {
12+
/// fn op(&self, other: &Self) -> Self {
13+
/// Sum(self.0 + other.0)
14+
/// }
15+
/// }
16+
///
17+
/// struct E {
18+
/// to: usize,
19+
/// value: i64,
20+
/// }
21+
///
22+
/// // 0, 1, 2, 0, 1, 2, ...
23+
/// let n = 3;
24+
/// let to = vec![
25+
/// E { to: 1, value: 1 },
26+
/// E { to: 2, value: 10 },
27+
/// E { to: 0, value: 100 },
28+
/// ];
29+
/// let doubling = Doubling::new(n, 100, |i| {
30+
/// let e = &to[i];
31+
/// Transition::new(e.to, Sum(e.value))
32+
/// });
33+
///
34+
/// assert_eq!(
35+
/// doubling.fold(0, 4, Sum(0), |acc, t| Sum(acc.0 + t.value.0)),
36+
/// // 0 -> 1 -> 2 -> 0 -> 1
37+
/// Sum(1 + 10 + 100 + 1)
38+
/// );
39+
/// ```
40+
#[derive(Debug, Clone)]
41+
pub struct Doubling<V> {
42+
transitions: Vec<Transition<V>>,
43+
n_state: usize,
44+
max_steps: usize,
45+
log2_max_steps: usize,
46+
}
47+
48+
#[derive(Debug, Clone)]
49+
pub struct Transition<V> {
50+
pub next: usize,
51+
pub value: V,
52+
}
53+
54+
impl<V> Transition<V> {
55+
pub fn new(next: usize, value: V) -> Self {
56+
Self { next, value }
57+
}
58+
}
59+
60+
pub trait Value {
61+
fn op(&self, other: &Self) -> Self;
62+
}
63+
64+
impl<V> Doubling<V>
65+
where
66+
V: Value,
67+
{
68+
/// ダブリングのテーブルを構築します。
69+
///
70+
/// `step1(i)`は状態`i`から1回の遷移における
71+
///
72+
/// - 遷移先の状態
73+
/// - その遷移にともなう値
74+
///
75+
/// を返す関数。
76+
pub fn new<F>(n_state: usize, max_steps: usize, step1: F) -> Self
77+
where
78+
F: Fn(usize) -> Transition<V>,
79+
{
80+
let log2_max_steps = if max_steps == 0 {
81+
0
82+
} else {
83+
max_steps.ilog2() as usize
84+
};
85+
86+
let mut transitions = Vec::with_capacity(n_state * (log2_max_steps + 1));
87+
for i in 0..n_state {
88+
let t = step1(i);
89+
90+
assert!(t.next < n_state);
91+
92+
transitions.push(t);
93+
}
94+
95+
for k in 1..=log2_max_steps {
96+
let offset = n_state * (k - 1);
97+
for i in 0..n_state {
98+
let t1 = &transitions[offset + i];
99+
let t2 = &transitions[offset + t1.next];
100+
transitions.push(Transition {
101+
next: t2.next,
102+
value: t1.value.op(&t2.value),
103+
});
104+
}
105+
}
106+
107+
Self {
108+
transitions,
109+
n_state,
110+
max_steps,
111+
log2_max_steps,
112+
}
113+
}
114+
115+
/// 状態`start`から`step`回の遷移、初期値`init`から始めて`f`で畳みこんだ結果を返します。
116+
pub fn fold<A, F>(&self, start: usize, step: usize, init: A, f: F) -> A
117+
where
118+
F: Fn(A, &Transition<V>) -> A,
119+
{
120+
assert!(start < self.n_state);
121+
assert!(step <= self.max_steps);
122+
123+
let mut i = start;
124+
let mut acc = init;
125+
for k in 0..=self.log2_max_steps {
126+
if step >> k & 1 == 1 {
127+
let offset = self.n_state * k;
128+
let t = &self.transitions[offset + i];
129+
(i, acc) = (t.next, f(acc, t));
130+
}
131+
}
132+
133+
acc
134+
}
135+
}
136+
137+
#[cfg(test)]
138+
mod tests {
139+
use ::proptest::{collection, prelude::*};
140+
141+
use super::*;
142+
143+
#[derive(Debug, PartialEq)]
144+
struct Sum(i64);
145+
146+
impl Value for Sum {
147+
fn op(&self, other: &Self) -> Self {
148+
Sum(self.0 + other.0)
149+
}
150+
}
151+
152+
#[test]
153+
fn test_cycle() {
154+
struct E {
155+
to: usize,
156+
value: i64,
157+
}
158+
159+
// 0, 1, 2, 0, 1, 2, ...
160+
let n = 3;
161+
let to = vec![
162+
E { to: 1, value: 1 },
163+
E { to: 2, value: 10 },
164+
E { to: 0, value: 100 },
165+
];
166+
let doubling = Doubling::new(n, 100, |i| {
167+
let e = &to[i];
168+
Transition::new(e.to, Sum(e.value))
169+
});
170+
171+
assert_eq!(
172+
doubling.fold(0, 0, Sum(0), |acc, t| Sum(acc.0 + t.value.0)),
173+
Sum(0)
174+
);
175+
assert_eq!(
176+
doubling.fold(0, 1, Sum(0), |acc, t| Sum(acc.0 + t.value.0)),
177+
Sum(1)
178+
);
179+
assert_eq!(
180+
doubling.fold(0, 2, Sum(0), |acc, t| Sum(acc.0 + t.value.0)),
181+
Sum(1 + 10)
182+
);
183+
assert_eq!(
184+
doubling.fold(0, 3, Sum(0), |acc, t| Sum(acc.0 + t.value.0)),
185+
Sum(1 + 10 + 100)
186+
);
187+
assert_eq!(
188+
doubling.fold(0, 4, Sum(0), |acc, t| Sum(acc.0 + t.value.0)),
189+
Sum(1 + 10 + 100 + 1)
190+
);
191+
}
192+
193+
impl Value for String {
194+
fn op(&self, other: &Self) -> Self {
195+
format!("{}{}", self, other)
196+
}
197+
}
198+
199+
proptest! {
200+
#[test]
201+
fn test_fold_associativity(
202+
(n_state, max_steps, nexts, values, start, step1, step2) in (1_usize..=10, 0_usize..=100)
203+
.prop_flat_map(|(n_state, max_steps)| {
204+
(
205+
Just(n_state),
206+
Just(max_steps),
207+
collection::vec(0..n_state, n_state),
208+
collection::vec(proptest::char::range('a', 'z'), n_state),
209+
)
210+
})
211+
.prop_flat_map(|(n_state, max_steps, nexts, values)| {
212+
(
213+
Just(n_state),
214+
Just(max_steps),
215+
Just(nexts),
216+
Just(values),
217+
0..n_state,
218+
0..=max_steps,
219+
)
220+
})
221+
.prop_flat_map(|(n_state, max_steps, nexts, values, start, step1)| {
222+
(
223+
Just(n_state),
224+
Just(max_steps),
225+
Just(nexts),
226+
Just(values),
227+
Just(start),
228+
Just(step1),
229+
0..=(max_steps - step1),
230+
)
231+
})
232+
) {
233+
let doubling = Doubling::new(n_state, max_steps, |i| {
234+
Transition::new(nexts[i], values[i].to_string())
235+
});
236+
237+
#[derive(Debug, Clone, PartialEq)]
238+
struct Acc {
239+
value: String,
240+
state: usize,
241+
}
242+
243+
let init = Acc {
244+
value: String::new(),
245+
state: start,
246+
};
247+
let f = |acc: Acc, t: &Transition<String>| Acc {
248+
value: format!("{}{}", acc.value, t.value),
249+
state: t.next,
250+
};
251+
252+
let combined = doubling.fold(start, step1 + step2, init.clone(), f);
253+
254+
let intermediate = doubling.fold(start, step1, init.clone(), f);
255+
let split = doubling.fold(intermediate.state, step2, intermediate.clone(), f);
256+
257+
prop_assert_eq!(combined.value, split.value);
258+
}
259+
}
260+
}

0 commit comments

Comments
 (0)