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
//! # Trinoise
//! A mathematical noise pattern of 3 values based on Number Theory and Set Theory
//!
//! ### Properties
//!
//! - Assigns a value to every natural number
//! - Fixed interpretation based on aligned positions to identity map
//! - Value counts the number of successors with decreasing value
//! - Never repeats the same number twice for bases greater than `2`
//! - Repeats noise pattern after `N^N` for base `N`
//!
//! The value counts the number of successors with decrementing value.
//! One can use it to skip successors and project into 3 values:
//!
//! 0. `0`
//! 1. `base - 2`
//! 2. `base - 1`
//!
//! This is done using the `tri` function.
//!
//! The frequencies of `0`, `1` and `2` is predictable, e.g.
//! `[470, 470, 155]` for base `5`.
//!
//! The frequency of `0` and `1` are equal for bases greater than `2` (conjecture).
//!
//! The frequency of `0` or `1` divided by frequency of `2` converges rapidly
//! to `base - 2` when `base` goes to infinity (conjecture).
//!
//! ### Inspiration
//!
//! The powerset operator is important in Set Theory.
//! To generate the powerset of a finite set, one can use a binary encoding,
//! where each bit represents a membership of an object.
//!
//! One problem with the powerset operator,
//! is that it does not provide information about isomorphisms of sets
//! to themselves.
//! This extra information is desirable when studying equivalences.
//! Therefore, one would like a more "powerful" way of generating sets.
//!
//! There is a different way of generating sets that respects isomorphisms:
//!
//! - Starting with an identity map `[0, 1, 2, ..., n-1]`
//! - Modify one position at a time, e.g. `[0, 1, 2] => [0, 0, 2]`
//! - The generated discrete combinatorial space forms a groupoid
//! - Redundant members are removed through post-filtering to form subsets
//!
//! For example, `[0, 0, 0]` becomes a set which contains only `{0}`.
//!
//! Isomorphisms are also generated, e.g. `[2, 0, 1]`.
//!
//! This means that the same method construct both subsets and isomorphisms.
//! The combination of subsets and isomorphisms is interesting to study for
//! Sized Type Theory, a type theory where functions can be applied to equivalences.
//! It is believed that an equivalence can ensure the existence of a
//! partial normal path, hence not require the function to be an isomorphism.
//!
//! For example, `[0, 0, 1]` is mapped differently than `[1, 0, 0]`, but both has
//! the same set `{0, 1}`.
//! When a function maps to a smaller set, it can not be an isomorphism,
//! but in Sized Type Theory one can use `f(a ~= b) == (f(a) ~= f(b))`,
//! so this is still meaningful as
//! existence of some normal path `f[g_i->n]` where `g_i(a) == g_i(b)` and
//! `g_n(f(a)) == g_n(f(b))`.
//! Normal paths are commutative squares of functions.
//! In this case, the square commutes by definition.
//!
//! One benefit of this groupoid structure, is that it represents all possible
//! transformations of sets closed under the category of functions.
//! It is much easier to study this structure than reasoning about families of functions,
//! because in families of functions the sets are repeated many times.
//!
//! It turns out that the reachability tree with identity map as root,
//! assigns a node depth equal to `n` minus aligned positions with identity map.
//! When ordering the reachability tree, the nodes form smaller neighborhoods
//! with same node depth, which size is always `1`, `n - 1` or `n` (conjecture).
//!
//! This is because when counting upwards, the following is true:
//!
//! - For every `n` cycle, there is at least one disruption
//! - Any disruption can either collapse 2 positions, swap 1 vs 1, or collapse 1
//! - Swapping 1 vs 1 never happens twice during `n` cycle
//!
//! The order of the identity map is chosen to preserve this property.
//! If one uses `210` instead of `012` in base `3`, this property is destroyed.
//!
//! The nodes in the groupoid can naturally be encoded with numbers in base `n`.
//! In base `n`, the signature of ordered neighborhoods with same node depth
//! is encodable in base `n` by subtracting `1`, counting successors.
//! Therefore, `0`, `base - 2` or `base - 1` are the only values.

/// Counts the number of aligned positions to identity map.
///
/// The maximum number of aligned positions is equal to the base.
///
/// For example, `012` in base `3` is the identity map,
/// therefore the aligned positions are `3`.
pub fn aligned(mut v: u64, base: u8) -> u8 {
    let base = base as u64;
    let mut sum = 0;
    for i in (0..base).rev() {
        if v % base == i {sum += 1}
        v /= base;
    }
    sum
}

/// Returns the number of successors that share number of aligned positions.
///
/// This is always a number `0`, `base - 2` or `base - 1`.
///
/// This number can also be used to increase the counter.
pub fn next(mut v: u64, base: u8) -> u8 {
    let mut sum = 0;
    let mut a = aligned(v, base);
    loop {
        let b = aligned(v + 1, base);
        if a == b {sum += 1; v += 1} else {break}
        a = b;
    }
    sum
}

/// Maps `0 => 0, base - 2 => 1, base - 1 => 2`.
pub fn tri(c: u8, base: u8) -> u8 {
    if c == 0 {0} else if c == base-2 {1} else {2}
}

/// Calculates signature of successors with shared aligned positions.
pub fn signature(base: u8) -> Vec<u8> {
    let end = (base as u64).pow(base as u32);
    let mut v = 0;
    let mut r = vec![];
    // Do not include the end since it would wrap count successors.
    while v + 1 < end {
        let n = next(v, base);
        v += n as u64 + 1;
        r.push(tri(n, base));
    }
    // The end always has no successors.
    r.push(0);
    r
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn it_works() {
        assert_eq!(signature(2), vec![0, 0, 0, 0]);

        let base = 3;
        assert_eq!(signature(base), vec![
            1, 2, 0, 1, 0, 1, 2, 0, 1, 0, 1, 2, 0, 1, 0]);
        assert_eq!(aligned(0, base), 1);
        assert_eq!(aligned(1, base), 1);
        assert_eq!(aligned(2, base), 2);
        assert_eq!(aligned(3, base), 2);
        assert_eq!(aligned(4, base), 2);
        assert_eq!(aligned(5, base), 3);
        assert_eq!(aligned(6, base), 1);
        assert_eq!(aligned(7, base), 1);

        assert_eq!(next(0, base), 1);
        assert_eq!(next(1, base), 0);
        assert_eq!(next(2, base), 2);
        assert_eq!(next(3, base), 1);
        assert_eq!(next(4, base), 0);
        assert_eq!(next(5, base), 0);
        assert_eq!(next(6, base), 1);

        let base = 3;
        let s = signature(base);
        let mut p = [0, 0, 0];
        for i in 0..s.len() {
            p[s[i] as usize] += 1;
        }
        assert_eq!(p, [6, 6, 3]);                   // 3
        // assert_eq!(p, [44, 44, 20]);             // 4
        // assert_eq!(p, [470, 470, 155]);          // 5
        // assert_eq!(p, [6222, 6222, 1554]);       // 6
        // assert_eq!(p, [98042, 98042, 19607]);    // 7
    }
}