Skip to content

Commit b31af0d

Browse files
committed
doubling
1 parent 6355177 commit b31af0d

File tree

2 files changed

+222
-0
lines changed

2 files changed

+222
-0
lines changed

libs/doubling/Cargo.toml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
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]

libs/doubling/src/lib.rs

Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
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+
/// 状態`i`の初期値を`inits[i]`として`f`で畳みこんだ結果を返します。
137+
///
138+
/// [`fold`](Self::fold)をN回呼ぶより高速です。
139+
pub fn fold_all<A, F>(&self, step: usize, inits: Vec<A>, f: F) -> Vec<A>
140+
where
141+
F: Fn(Vec<A>, &[Transition<V>]) -> Vec<A>,
142+
{
143+
assert!(step <= self.max_steps);
144+
assert!(inits.len() == self.n_state);
145+
146+
let mut acc = inits;
147+
for k in 0..=self.log2_max_steps {
148+
if step >> k & 1 == 1 {
149+
let offset = self.n_state * k;
150+
let transitions = &self.transitions[offset..(offset + self.n_state)];
151+
acc = f(acc, transitions);
152+
}
153+
}
154+
155+
acc
156+
}
157+
}
158+
159+
#[cfg(test)]
160+
mod tests {
161+
use super::*;
162+
163+
#[derive(Debug, PartialEq)]
164+
struct Sum(i64);
165+
166+
impl Value for Sum {
167+
fn op(&self, other: &Self) -> Self {
168+
Sum(self.0 + other.0)
169+
}
170+
}
171+
172+
#[test]
173+
fn test_cycle() {
174+
struct E {
175+
to: usize,
176+
value: i64,
177+
}
178+
179+
// 0, 1, 2, 0, 1, 2, ...
180+
let n = 3;
181+
let to = vec![
182+
E { to: 1, value: 1 },
183+
E { to: 2, value: 10 },
184+
E { to: 0, value: 100 },
185+
];
186+
let doubling = Doubling::new(n, 100, |i| {
187+
let e = &to[i];
188+
Transition::new(e.to, Sum(e.value))
189+
});
190+
191+
assert_eq!(
192+
doubling.fold(0, 0, Sum(0), |acc, t| Sum(acc.0 + t.value.0)),
193+
Sum(0)
194+
);
195+
assert_eq!(
196+
doubling.fold(0, 1, Sum(0), |acc, t| Sum(acc.0 + t.value.0)),
197+
Sum(1)
198+
);
199+
assert_eq!(
200+
doubling.fold(0, 2, Sum(0), |acc, t| Sum(acc.0 + t.value.0)),
201+
Sum(1 + 10)
202+
);
203+
assert_eq!(
204+
doubling.fold(0, 3, Sum(0), |acc, t| Sum(acc.0 + t.value.0)),
205+
Sum(1 + 10 + 100)
206+
);
207+
assert_eq!(
208+
doubling.fold(0, 4, Sum(0), |acc, t| Sum(acc.0 + t.value.0)),
209+
Sum(1 + 10 + 100 + 1)
210+
);
211+
}
212+
}

0 commit comments

Comments
 (0)