src/main.rs

Wed, 06 Nov 2024 10:19:59 -0500

author
Tuomo Valkonen <tuomov@iki.fi>
date
Wed, 06 Nov 2024 10:19:59 -0500
changeset 19
bc7f268b324a
parent 18
84923812d220
child 20
bd6fd49146ce
permissions
-rw-r--r--

test change

13
f67949050a32 documentation
Tuomo Valkonen <tuomov@iki.fi>
parents: 12
diff changeset
1 /*!
f67949050a32 documentation
Tuomo Valkonen <tuomov@iki.fi>
parents: 12
diff changeset
2 Optimisation on non-Riemannian manifolds.
f67949050a32 documentation
Tuomo Valkonen <tuomov@iki.fi>
parents: 12
diff changeset
3 */
4
e09437844ad9 Forward-backward skeleton
Tuomo Valkonen <tuomov@iki.fi>
parents: 1
diff changeset
4
e09437844ad9 Forward-backward skeleton
Tuomo Valkonen <tuomov@iki.fi>
parents: 1
diff changeset
5 // We use unicode. We would like to use much more of it than Rust allows.
e09437844ad9 Forward-backward skeleton
Tuomo Valkonen <tuomov@iki.fi>
parents: 1
diff changeset
6 // Live with it. Embrace it.
e09437844ad9 Forward-backward skeleton
Tuomo Valkonen <tuomov@iki.fi>
parents: 1
diff changeset
7 #![allow(uncommon_codepoints)]
e09437844ad9 Forward-backward skeleton
Tuomo Valkonen <tuomov@iki.fi>
parents: 1
diff changeset
8 #![allow(mixed_script_confusables)]
e09437844ad9 Forward-backward skeleton
Tuomo Valkonen <tuomov@iki.fi>
parents: 1
diff changeset
9 #![allow(confusable_idents)]
1
29738fae471e Added some missing files
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
10
19
bc7f268b324a test change
Tuomo Valkonen <tuomov@iki.fi>
parents: 18
diff changeset
11 mod manifold;
bc7f268b324a test change
Tuomo Valkonen <tuomov@iki.fi>
parents: 18
diff changeset
12 mod fb;
bc7f268b324a test change
Tuomo Valkonen <tuomov@iki.fi>
parents: 18
diff changeset
13 mod cube;
bc7f268b324a test change
Tuomo Valkonen <tuomov@iki.fi>
parents: 18
diff changeset
14 mod dist;
bc7f268b324a test change
Tuomo Valkonen <tuomov@iki.fi>
parents: 18
diff changeset
15 mod zero;
bc7f268b324a test change
Tuomo Valkonen <tuomov@iki.fi>
parents: 18
diff changeset
16 mod scaled;
bc7f268b324a test change
Tuomo Valkonen <tuomov@iki.fi>
parents: 18
diff changeset
17
12
3b05a8b45b95 Save log to a CSV file
Tuomo Valkonen <tuomov@iki.fi>
parents: 11
diff changeset
18 use serde::Serialize;
16
a6efe0fafd90 CSV output of face data
Tuomo Valkonen <tuomov@iki.fi>
parents: 13
diff changeset
19 use alg_tools::logger::Logger;
a6efe0fafd90 CSV output of face data
Tuomo Valkonen <tuomov@iki.fi>
parents: 13
diff changeset
20 use alg_tools::tabledump::{TableDump, write_csv};
a6efe0fafd90 CSV output of face data
Tuomo Valkonen <tuomov@iki.fi>
parents: 13
diff changeset
21 use alg_tools::error::DynError;
a6efe0fafd90 CSV output of face data
Tuomo Valkonen <tuomov@iki.fi>
parents: 13
diff changeset
22 use alg_tools::lingrid::LinSpace;
a6efe0fafd90 CSV output of face data
Tuomo Valkonen <tuomov@iki.fi>
parents: 13
diff changeset
23 use alg_tools::loc::Loc;
a6efe0fafd90 CSV output of face data
Tuomo Valkonen <tuomov@iki.fi>
parents: 13
diff changeset
24 use alg_tools::types::*;
a6efe0fafd90 CSV output of face data
Tuomo Valkonen <tuomov@iki.fi>
parents: 13
diff changeset
25 use alg_tools::mapping::{Sum, Apply};
a6efe0fafd90 CSV output of face data
Tuomo Valkonen <tuomov@iki.fi>
parents: 13
diff changeset
26 use alg_tools::iterate::{AlgIteratorOptions, AlgIteratorFactory, Verbose};
a6efe0fafd90 CSV output of face data
Tuomo Valkonen <tuomov@iki.fi>
parents: 13
diff changeset
27 use image::{ImageFormat, ImageBuffer, Rgb};
a6efe0fafd90 CSV output of face data
Tuomo Valkonen <tuomov@iki.fi>
parents: 13
diff changeset
28
19
bc7f268b324a test change
Tuomo Valkonen <tuomov@iki.fi>
parents: 18
diff changeset
29 use dist::{DistTo, DistToSquaredDiv2};
12
3b05a8b45b95 Save log to a CSV file
Tuomo Valkonen <tuomov@iki.fi>
parents: 11
diff changeset
30 use fb::{forward_backward, IterInfo};
7
8979a6638424 A simple test
Tuomo Valkonen <tuomov@iki.fi>
parents: 6
diff changeset
31 use manifold::EmbeddedManifoldPoint;
11
933242e0f3b8 Basic face image export
Tuomo Valkonen <tuomov@iki.fi>
parents: 9
diff changeset
32 use cube::*;
16
a6efe0fafd90 CSV output of face data
Tuomo Valkonen <tuomov@iki.fi>
parents: 13
diff changeset
33 use Face::*;
19
bc7f268b324a test change
Tuomo Valkonen <tuomov@iki.fi>
parents: 18
diff changeset
34 #[allow(unused_imports)]
16
a6efe0fafd90 CSV output of face data
Tuomo Valkonen <tuomov@iki.fi>
parents: 13
diff changeset
35 use zero::ZeroFn;
19
bc7f268b324a test change
Tuomo Valkonen <tuomov@iki.fi>
parents: 18
diff changeset
36 use scaled::Scaled;
1
29738fae471e Added some missing files
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
37
13
f67949050a32 documentation
Tuomo Valkonen <tuomov@iki.fi>
parents: 12
diff changeset
38 /// Program entry point
1
29738fae471e Added some missing files
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
39 fn main() {
12
3b05a8b45b95 Save log to a CSV file
Tuomo Valkonen <tuomov@iki.fi>
parents: 11
diff changeset
40 simple_cube_test().unwrap()
7
8979a6638424 A simple test
Tuomo Valkonen <tuomov@iki.fi>
parents: 6
diff changeset
41 }
1
29738fae471e Added some missing files
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
42
12
3b05a8b45b95 Save log to a CSV file
Tuomo Valkonen <tuomov@iki.fi>
parents: 11
diff changeset
43 /// Helper structure for saving the log into a CSV file
3b05a8b45b95 Save log to a CSV file
Tuomo Valkonen <tuomov@iki.fi>
parents: 11
diff changeset
44 #[derive(Serialize)]
3b05a8b45b95 Save log to a CSV file
Tuomo Valkonen <tuomov@iki.fi>
parents: 11
diff changeset
45 struct CSVLog {
3b05a8b45b95 Save log to a CSV file
Tuomo Valkonen <tuomov@iki.fi>
parents: 11
diff changeset
46 iter : usize,
3b05a8b45b95 Save log to a CSV file
Tuomo Valkonen <tuomov@iki.fi>
parents: 11
diff changeset
47 value : f64,
3b05a8b45b95 Save log to a CSV file
Tuomo Valkonen <tuomov@iki.fi>
parents: 11
diff changeset
48 face : Face,
3b05a8b45b95 Save log to a CSV file
Tuomo Valkonen <tuomov@iki.fi>
parents: 11
diff changeset
49 x : f64,
3b05a8b45b95 Save log to a CSV file
Tuomo Valkonen <tuomov@iki.fi>
parents: 11
diff changeset
50 y : f64,
3b05a8b45b95 Save log to a CSV file
Tuomo Valkonen <tuomov@iki.fi>
parents: 11
diff changeset
51 z : f64
3b05a8b45b95 Save log to a CSV file
Tuomo Valkonen <tuomov@iki.fi>
parents: 11
diff changeset
52 }
3b05a8b45b95 Save log to a CSV file
Tuomo Valkonen <tuomov@iki.fi>
parents: 11
diff changeset
53
13
f67949050a32 documentation
Tuomo Valkonen <tuomov@iki.fi>
parents: 12
diff changeset
54 /// Location for saving results
12
3b05a8b45b95 Save log to a CSV file
Tuomo Valkonen <tuomov@iki.fi>
parents: 11
diff changeset
55 static PREFIX : &str = "res";
3b05a8b45b95 Save log to a CSV file
Tuomo Valkonen <tuomov@iki.fi>
parents: 11
diff changeset
56
3b05a8b45b95 Save log to a CSV file
Tuomo Valkonen <tuomov@iki.fi>
parents: 11
diff changeset
57 /// A simple test on the cube
3b05a8b45b95 Save log to a CSV file
Tuomo Valkonen <tuomov@iki.fi>
parents: 11
diff changeset
58 fn simple_cube_test() -> DynError {
7
8979a6638424 A simple test
Tuomo Valkonen <tuomov@iki.fi>
parents: 6
diff changeset
59
8979a6638424 A simple test
Tuomo Valkonen <tuomov@iki.fi>
parents: 6
diff changeset
60 let points = [
9
0fa3ac0c248b change experiment
Tuomo Valkonen <tuomov@iki.fi>
parents: 7
diff changeset
61 //OnCube::new(F1, Loc([0.5, 0.5])),
0fa3ac0c248b change experiment
Tuomo Valkonen <tuomov@iki.fi>
parents: 7
diff changeset
62 //OnCube::new(F2, Loc([0.5, 0.5])),
0fa3ac0c248b change experiment
Tuomo Valkonen <tuomov@iki.fi>
parents: 7
diff changeset
63 //OnCube::new(F4, Loc([0.1, 0.1])),
7
8979a6638424 A simple test
Tuomo Valkonen <tuomov@iki.fi>
parents: 6
diff changeset
64 OnCube::new(F1, Loc([0.5, 0.5])),
9
0fa3ac0c248b change experiment
Tuomo Valkonen <tuomov@iki.fi>
parents: 7
diff changeset
65 OnCube::new(F3, Loc([0.5, 0.5])),
7
8979a6638424 A simple test
Tuomo Valkonen <tuomov@iki.fi>
parents: 6
diff changeset
66 OnCube::new(F2, Loc([0.5, 0.5])),
8979a6638424 A simple test
Tuomo Valkonen <tuomov@iki.fi>
parents: 6
diff changeset
67 ];
8979a6638424 A simple test
Tuomo Valkonen <tuomov@iki.fi>
parents: 6
diff changeset
68
8979a6638424 A simple test
Tuomo Valkonen <tuomov@iki.fi>
parents: 6
diff changeset
69 //let x = points[0].clone();
9
0fa3ac0c248b change experiment
Tuomo Valkonen <tuomov@iki.fi>
parents: 7
diff changeset
70 // OnCube::new(F3, Loc([0.5, 0.5])); goes to opposite side
0fa3ac0c248b change experiment
Tuomo Valkonen <tuomov@iki.fi>
parents: 7
diff changeset
71 let x = OnCube::new(F3, Loc([0.5, 0.4]));
7
8979a6638424 A simple test
Tuomo Valkonen <tuomov@iki.fi>
parents: 6
diff changeset
72 let f = Sum::new(points.into_iter().map(DistToSquaredDiv2));
19
bc7f268b324a test change
Tuomo Valkonen <tuomov@iki.fi>
parents: 18
diff changeset
73 //let g = ZeroFn::new();
bc7f268b324a test change
Tuomo Valkonen <tuomov@iki.fi>
parents: 18
diff changeset
74 let g = Scaled::new(1.0, DistTo(OnCube::new(F4, Loc([0.5, 0.5]))));
7
8979a6638424 A simple test
Tuomo Valkonen <tuomov@iki.fi>
parents: 6
diff changeset
75 let τ = 0.1;
12
3b05a8b45b95 Save log to a CSV file
Tuomo Valkonen <tuomov@iki.fi>
parents: 11
diff changeset
76
3b05a8b45b95 Save log to a CSV file
Tuomo Valkonen <tuomov@iki.fi>
parents: 11
diff changeset
77 let mut logger = Logger::new();
3b05a8b45b95 Save log to a CSV file
Tuomo Valkonen <tuomov@iki.fi>
parents: 11
diff changeset
78 let logmap = |iter, IterInfo { value, point } : IterInfo<OnCube>| {
3b05a8b45b95 Save log to a CSV file
Tuomo Valkonen <tuomov@iki.fi>
parents: 11
diff changeset
79 let Loc([x,y,z]) = point.embedded_coords();
3b05a8b45b95 Save log to a CSV file
Tuomo Valkonen <tuomov@iki.fi>
parents: 11
diff changeset
80 let face = point.face();
3b05a8b45b95 Save log to a CSV file
Tuomo Valkonen <tuomov@iki.fi>
parents: 11
diff changeset
81 CSVLog { iter, value, face, x, y, z }
3b05a8b45b95 Save log to a CSV file
Tuomo Valkonen <tuomov@iki.fi>
parents: 11
diff changeset
82 };
7
8979a6638424 A simple test
Tuomo Valkonen <tuomov@iki.fi>
parents: 6
diff changeset
83 let iter = AlgIteratorOptions{
8979a6638424 A simple test
Tuomo Valkonen <tuomov@iki.fi>
parents: 6
diff changeset
84 max_iter : 100,
8979a6638424 A simple test
Tuomo Valkonen <tuomov@iki.fi>
parents: 6
diff changeset
85 verbose_iter : Verbose::Every(1),
8979a6638424 A simple test
Tuomo Valkonen <tuomov@iki.fi>
parents: 6
diff changeset
86 .. Default::default()
12
3b05a8b45b95 Save log to a CSV file
Tuomo Valkonen <tuomov@iki.fi>
parents: 11
diff changeset
87 }.mapped(logmap)
3b05a8b45b95 Save log to a CSV file
Tuomo Valkonen <tuomov@iki.fi>
parents: 11
diff changeset
88 .into_log(&mut logger);
7
8979a6638424 A simple test
Tuomo Valkonen <tuomov@iki.fi>
parents: 6
diff changeset
89
8979a6638424 A simple test
Tuomo Valkonen <tuomov@iki.fi>
parents: 6
diff changeset
90 let x̂ = forward_backward(&f, &g, x, τ, iter);
8979a6638424 A simple test
Tuomo Valkonen <tuomov@iki.fi>
parents: 6
diff changeset
91 println!("result = {}\n{:?}", x̂.embedded_coords(), &x̂);
11
933242e0f3b8 Basic face image export
Tuomo Valkonen <tuomov@iki.fi>
parents: 9
diff changeset
92
12
3b05a8b45b95 Save log to a CSV file
Tuomo Valkonen <tuomov@iki.fi>
parents: 11
diff changeset
93 std::fs::create_dir_all(PREFIX)?;
3b05a8b45b95 Save log to a CSV file
Tuomo Valkonen <tuomov@iki.fi>
parents: 11
diff changeset
94
3b05a8b45b95 Save log to a CSV file
Tuomo Valkonen <tuomov@iki.fi>
parents: 11
diff changeset
95 logger.write_csv(format!("{PREFIX}/log.txt"))?;
3b05a8b45b95 Save log to a CSV file
Tuomo Valkonen <tuomov@iki.fi>
parents: 11
diff changeset
96
11
933242e0f3b8 Basic face image export
Tuomo Valkonen <tuomov@iki.fi>
parents: 9
diff changeset
97 for face in Face::all() {
16
a6efe0fafd90 CSV output of face data
Tuomo Valkonen <tuomov@iki.fi>
parents: 13
diff changeset
98 write_face_csv(format!("{PREFIX}/{face}"), face, 64, |x| f.apply(x) + g.apply(x))?;
a6efe0fafd90 CSV output of face data
Tuomo Valkonen <tuomov@iki.fi>
parents: 13
diff changeset
99 write_face_img(format!("{PREFIX}/{face}"), face, 128, |x| f.apply(x) + g.apply(x))?;
11
933242e0f3b8 Basic face image export
Tuomo Valkonen <tuomov@iki.fi>
parents: 9
diff changeset
100 }
12
3b05a8b45b95 Save log to a CSV file
Tuomo Valkonen <tuomov@iki.fi>
parents: 11
diff changeset
101
3b05a8b45b95 Save log to a CSV file
Tuomo Valkonen <tuomov@iki.fi>
parents: 11
diff changeset
102 Ok(())
1
29738fae471e Added some missing files
Tuomo Valkonen <tuomov@iki.fi>
parents:
diff changeset
103 }
11
933242e0f3b8 Basic face image export
Tuomo Valkonen <tuomov@iki.fi>
parents: 9
diff changeset
104
12
3b05a8b45b95 Save log to a CSV file
Tuomo Valkonen <tuomov@iki.fi>
parents: 11
diff changeset
105 /// Writes the values of `f` on `face` of a [`OnCube`] into a PNG file
3b05a8b45b95 Save log to a CSV file
Tuomo Valkonen <tuomov@iki.fi>
parents: 11
diff changeset
106 /// with resolution `n × n`.
16
a6efe0fafd90 CSV output of face data
Tuomo Valkonen <tuomov@iki.fi>
parents: 13
diff changeset
107 fn write_face_img(filename : String, face : Face, n : usize, mut f : impl FnMut(&OnCube) -> f64) -> DynError {
11
933242e0f3b8 Basic face image export
Tuomo Valkonen <tuomov@iki.fi>
parents: 9
diff changeset
108 let mut img = ImageBuffer::new(n as u32, n as u32);
933242e0f3b8 Basic face image export
Tuomo Valkonen <tuomov@iki.fi>
parents: 9
diff changeset
109 let grid = LinSpace {
933242e0f3b8 Basic face image export
Tuomo Valkonen <tuomov@iki.fi>
parents: 9
diff changeset
110 start : Loc([0.0, 0.0]),
933242e0f3b8 Basic face image export
Tuomo Valkonen <tuomov@iki.fi>
parents: 9
diff changeset
111 end : Loc([1.0, 1.0]),
933242e0f3b8 Basic face image export
Tuomo Valkonen <tuomov@iki.fi>
parents: 9
diff changeset
112 count : [n, n]
933242e0f3b8 Basic face image export
Tuomo Valkonen <tuomov@iki.fi>
parents: 9
diff changeset
113 };
933242e0f3b8 Basic face image export
Tuomo Valkonen <tuomov@iki.fi>
parents: 9
diff changeset
114 let rawdata : Vec<_> = grid.into_iter()
933242e0f3b8 Basic face image export
Tuomo Valkonen <tuomov@iki.fi>
parents: 9
diff changeset
115 .map(|x| f(&OnCube::new(face, x)))
933242e0f3b8 Basic face image export
Tuomo Valkonen <tuomov@iki.fi>
parents: 9
diff changeset
116 .collect();
933242e0f3b8 Basic face image export
Tuomo Valkonen <tuomov@iki.fi>
parents: 9
diff changeset
117 let a = rawdata.iter().copied().reduce(f64::max).unwrap();
933242e0f3b8 Basic face image export
Tuomo Valkonen <tuomov@iki.fi>
parents: 9
diff changeset
118 img.pixels_mut()
933242e0f3b8 Basic face image export
Tuomo Valkonen <tuomov@iki.fi>
parents: 9
diff changeset
119 .zip(rawdata)
933242e0f3b8 Basic face image export
Tuomo Valkonen <tuomov@iki.fi>
parents: 9
diff changeset
120 .for_each(|(p, v)| {
933242e0f3b8 Basic face image export
Tuomo Valkonen <tuomov@iki.fi>
parents: 9
diff changeset
121 let t = v/a;
933242e0f3b8 Basic face image export
Tuomo Valkonen <tuomov@iki.fi>
parents: 9
diff changeset
122 let rgb = [1.0-t, 1.0-t, 1.0];
933242e0f3b8 Basic face image export
Tuomo Valkonen <tuomov@iki.fi>
parents: 9
diff changeset
123 *p = Rgb(rgb.map(|v| (v*(u8::RANGE_MAX as f64)) as u8))
933242e0f3b8 Basic face image export
Tuomo Valkonen <tuomov@iki.fi>
parents: 9
diff changeset
124 });
933242e0f3b8 Basic face image export
Tuomo Valkonen <tuomov@iki.fi>
parents: 9
diff changeset
125
12
3b05a8b45b95 Save log to a CSV file
Tuomo Valkonen <tuomov@iki.fi>
parents: 11
diff changeset
126 img.save_with_format(format!("{filename}.png"), ImageFormat::Png)?;
3b05a8b45b95 Save log to a CSV file
Tuomo Valkonen <tuomov@iki.fi>
parents: 11
diff changeset
127
3b05a8b45b95 Save log to a CSV file
Tuomo Valkonen <tuomov@iki.fi>
parents: 11
diff changeset
128 Ok(())
11
933242e0f3b8 Basic face image export
Tuomo Valkonen <tuomov@iki.fi>
parents: 9
diff changeset
129 }
16
a6efe0fafd90 CSV output of face data
Tuomo Valkonen <tuomov@iki.fi>
parents: 13
diff changeset
130
a6efe0fafd90 CSV output of face data
Tuomo Valkonen <tuomov@iki.fi>
parents: 13
diff changeset
131 /// Writes the values of `f` on `face` of a [`OnCube`] into a CSV file
a6efe0fafd90 CSV output of face data
Tuomo Valkonen <tuomov@iki.fi>
parents: 13
diff changeset
132 /// with resolution `n × n`.
a6efe0fafd90 CSV output of face data
Tuomo Valkonen <tuomov@iki.fi>
parents: 13
diff changeset
133 fn write_face_csv(filename : String, face : Face, n : usize, mut f : impl FnMut(&OnCube) -> f64) -> DynError {
a6efe0fafd90 CSV output of face data
Tuomo Valkonen <tuomov@iki.fi>
parents: 13
diff changeset
134
a6efe0fafd90 CSV output of face data
Tuomo Valkonen <tuomov@iki.fi>
parents: 13
diff changeset
135 #[derive(Serialize)]
a6efe0fafd90 CSV output of face data
Tuomo Valkonen <tuomov@iki.fi>
parents: 13
diff changeset
136 struct CSVFace { u : f64, v : f64, value : f64 }
a6efe0fafd90 CSV output of face data
Tuomo Valkonen <tuomov@iki.fi>
parents: 13
diff changeset
137
a6efe0fafd90 CSV output of face data
Tuomo Valkonen <tuomov@iki.fi>
parents: 13
diff changeset
138 let grid = LinSpace {
a6efe0fafd90 CSV output of face data
Tuomo Valkonen <tuomov@iki.fi>
parents: 13
diff changeset
139 start : Loc([0.0, 0.0]),
a6efe0fafd90 CSV output of face data
Tuomo Valkonen <tuomov@iki.fi>
parents: 13
diff changeset
140 end : Loc([1.0, 1.0]),
a6efe0fafd90 CSV output of face data
Tuomo Valkonen <tuomov@iki.fi>
parents: 13
diff changeset
141 count : [n, n]
a6efe0fafd90 CSV output of face data
Tuomo Valkonen <tuomov@iki.fi>
parents: 13
diff changeset
142 };
a6efe0fafd90 CSV output of face data
Tuomo Valkonen <tuomov@iki.fi>
parents: 13
diff changeset
143
a6efe0fafd90 CSV output of face data
Tuomo Valkonen <tuomov@iki.fi>
parents: 13
diff changeset
144 let data = grid.into_iter()
a6efe0fafd90 CSV output of face data
Tuomo Valkonen <tuomov@iki.fi>
parents: 13
diff changeset
145 .map(|p@Loc([u,v])| CSVFace{ u, v, value : f(&OnCube::new(face, p)) });
a6efe0fafd90 CSV output of face data
Tuomo Valkonen <tuomov@iki.fi>
parents: 13
diff changeset
146
a6efe0fafd90 CSV output of face data
Tuomo Valkonen <tuomov@iki.fi>
parents: 13
diff changeset
147 write_csv(data, format!("{filename}.csv"))?;
a6efe0fafd90 CSV output of face data
Tuomo Valkonen <tuomov@iki.fi>
parents: 13
diff changeset
148
a6efe0fafd90 CSV output of face data
Tuomo Valkonen <tuomov@iki.fi>
parents: 13
diff changeset
149 Ok(())
a6efe0fafd90 CSV output of face data
Tuomo Valkonen <tuomov@iki.fi>
parents: 13
diff changeset
150 }

mercurial