1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
//! The `Deterministic` and `DeterministicOrDiverges` marker traits.
//!
//! Operations on one or more [`Deterministic`](trait.Deterministic.html) data type values are
//! guaranteed to give consistent (read: the exact same) results across hardware,
//! operating systems, compilers, and build settings (in particular, optimization options).
//! The benefits are mostly cross-platform reproducibilty ("replayability") and
//! consistency of implicitly shared state over time (which is great for games).
//!
//! For instance, `std::num::Wrapping<u32>` implements [`Deterministic`](trait.Deterministic.html)
//! because `1 + 2` will always yield `3`,
//! and even `std::u32::MAX + 1` will always yield `0`.
//! Also, integer division by zero either
//! causes a panic or aborts the program, so this is treated as "consistent" behaviour for
//! the use cases [`Deterministic`](trait.Deterministic.html) was written for.
//!
//! On the other hand, `f32` and `f64` certainly do NOT implement
//! [`Deterministic`](trait.Deterministic.html), because the
//! result of, say, `cos(2.354)` depends too much on optimization options, the target hardware,
//! hardware-specific FP modes which might be set at runtime, and finally the `cos()`
//! implementation, which I wouldn't trust at all to be the same on all systems (provided that the
//! compiler's `cos()` built-in doesn't kick in first).
//!
//! Actually, anything related to native floating-point is a natural source of inconsistency.
//! There are many articles on the subject, the following one being the most widely cited :
//! [http://gafferongames.com/networking-for-game-programmers/floating-point-determinism/](http://gafferongames.com/networking-for-game-programmers/floating-point-determinism/)
//!
//! Admittedly, `Deterministic` trait bounds are not something you would put all over your code
//! in the hope it'll make everything replayable - the purpose is merely to help making such
//! guarantees. Engine-provided time-rewinding mechanics, for instance, might want to use it.
//!
//! # Trivia
//! - Refraining from using native floating-point number types is often enough to get rid of
//! inconsistency, but keep in mind there are subtle ways inconsistency can creep back into your
//! code : for instance, some kind of concurrent update where the order in which operations are executed does matter.
//! - However, that doesn't mean that the floating-point representation itself isn't incompatible with
//! [`Deterministic`](trait.Deterministic.html)'s requirements.
//! "Soft float" types should implement `Deterministic` if they do match the mentioned consistency criteria.
//! - Most (if not all) of the types that derive/implement automatically `std::hash::Hash` are
//! good candidates for [`Deterministic`](trait.Deterministic.html). NOTE: Actually, function pointers
//! are a good counter-example.
//! - `Deterministic` shouldn't be implemented on function/closure types because there's no
//! way to require that they have no side effect (and as of Rust 1.18-nightly, we can't implement traits for
//! `const fn`s);
//! - Operations involving both `Deterministic` and non-`Deterministic` types are non-`Deterministic`.
//! `Deterministic` can't prevent you from shooting yourself in the foot, but makes it a bit harder.
//!
//! # Example (trivial) use case
//! ```rust,no_run
//! extern crate fate;
//! // Requires the latest num from the git repo as of today (20th april 2017)
//! extern crate num;
//!
//! use fate::{Deterministic, Xy};
//! use num::Num;
//! use std::num::Wrapping as Wp;
//!
//! // Work in our own deterministic space. Maybe this is a networked game
//! // in which only player input is shared between peers, from which each peer updates their
//! // their local copy of the game's state (some kind of Peer-to-Peer deterministic lockstep mechanic).
//! fn update_logic<T>(pos: &mut Xy<T>) where T: Copy + Num + Deterministic {
//! pos.x = pos.x + pos.y;
//! }
//! // On the other hand, _rendering_ has no effect on the game's state, so we don't care if it's
//! // inconsistent across peers. It's all the better anyway because GPUs love 32-bit
//! // floating point types, and so do 3D APIs.
//! fn update_rendering(_: Xy<f32>) {
//! unimplemented!()
//! }
//!
//! fn main() {
//! let mut player = Xy::new(Wp(42_i32), Wp(42_i32));
//! loop {
//! update_logic(&mut player);
//! update_rendering(Xy::new(player.x.0 as f32 / 100f32, 0_f32)); // Whatever
//! }
//! }
//! ```
/// Operations on one or more `Deterministic` data type values are guaranteed to give
/// consistent (read: the exact same) results across hardware,
/// operating systems, compilers, and build settings (in particular, optimization options).
///
/// This trait is `unsafe` because it asserts properties that can't be proved to
/// hold true, except by careful programmers.
pub unsafe
/// A type that's close to meeting [`Deterministic`](trait.Deterministic.html)'s requirements,
/// because its only "inconsistent" behaviours (if any) are to diverge (e.g panic).
/// Please don't confuse this with "consistent" divergence (e.g integer divide by zero).
///
/// This was intended for primitive integer types because basic operations may panic if they
/// overflow in debug builds.
///
/// As per the description above :
/// - `DeterministicOrDiverges` does NOT imply `Deterministic`;
/// - `Deterministic` does NOT imply `DeterministicOrDiverges` either.
pub unsafe
use Wrapping;
use *;
use Ordering;
use TypeId;
use PhantomData;
use Arc;
use Rc;
unsafe
unsafe
impl_deterministic!;
impl_deterministic!;
impl_deterministic!;
impl_deterministic!;
impl_deterministic!;
impl_deterministic!;
impl_deterministic!;
impl_deterministic!;
impl_deterministic!;
impl_deterministic!;
impl_deterministic!;
impl_deterministic!;
impl_deterministic!;
impl_deterministic!;
impl_deterministic!;
impl_deterministic!;
impl_deterministic!;
unsafe
unsafe
impl_deterministic!;
impl_deterministic!;
impl_deterministic!;
unsafe
unsafe
unsafe
unsafe
unsafe