aboutsummaryrefslogtreecommitdiff
path: root/src/passwordmaker/base_conversion/iterative_conversion_impl/precomputed_constants.rs
blob: 112769259a270bb05a6c36d122f16fa6e24a3c4e (plain) (blame)
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
use super::const_mul_usize;
use super::ArbitraryBytes;
use super::super::iterative_conversion::PrecomputedMaxPowers;

impl PrecomputedMaxPowers<usize> for ArbitraryBytes<5>{
    fn lookup(base : &usize) -> Option<(Self, usize)> { 
        get_from_cache(*base, &CONSTANT_MAX_POWER_CACHE_5)
    }
}

impl PrecomputedMaxPowers<usize> for ArbitraryBytes<8>{
    fn lookup(base : &usize) -> Option<(Self, usize)> { 
        get_from_cache(*base, &CONSTANT_MAX_POWER_CACHE_8)
     }
}

fn get_from_cache<const N : usize>(base : usize, cache : &[([u32;N], usize)]) -> Option<(ArbitraryBytes<N>, usize)>{
    base.checked_sub(2).and_then(|idx|cache.get(idx))
        .map(|c| (ArbitraryBytes(c.0), c.1))
}

const CONSTANT_MAX_POWER_CACHE_5 : [([u32;5],usize);128] = gen_const_max_power_cache();
const CONSTANT_MAX_POWER_CACHE_8 : [([u32;8],usize);128] = gen_const_max_power_cache();

//-----------------------------------------------------------------------------------------

/// This version of `find_highest_fitting_power` is not optimized. But it can run in const contexts. Only use it there, use the normal one everywhere else.
const fn const_find_highest_fitting_power<const N : usize>(base : usize) -> ([u32;N],usize){
    let start = super::from_usize(base);

    let mut x = (start, 1);
    while let Some(next) = const_mul_usize(const_clone(&x.0),base) {
        x.0 = next;
        x.1 +=1;
    }
    (x.0.0, x.1)
}

//If anyone could tell me how to implement "~const Clone" in stable Rust, I'd be very happy.
const fn const_clone<const N : usize>(x : &ArbitraryBytes<N>) -> ArbitraryBytes<N>{ArbitraryBytes(x.0)}

const fn gen_const_max_power_cache<const N : usize, const CNT : usize>() -> [([u32;N],usize);CNT]{
    let mut result = [([0u32;N],0usize);CNT];
    let mut i = 0usize;
    loop {
        let highest = const_find_highest_fitting_power(i + 2);
        result[i] = (highest.0, highest.1);
        i +=1;
        if i == CNT { break; }
    }
    result
}

#[cfg(test)]
mod iterative_conversion_constants_tests{
    use super::ArbitraryBytes;

    #[test]
    fn test_overlows_8()
    {
        let entries = super::CONSTANT_MAX_POWER_CACHE_8.iter().enumerate()
            .map(|(i,(p,e))| (i+2, ArbitraryBytes(*p), *e));
        for (base, power, _exponent) in entries {
            assert!((power * base).is_none())
        }
    }
    #[test]
    fn test_overlows_5()
    {
        let entries = super::CONSTANT_MAX_POWER_CACHE_5.iter().enumerate()
            .map(|(i,(p,e))| (i+2, ArbitraryBytes(*p), *e));
        for (base, power, _exponent) in entries {
            assert!((power * base).is_none())
        }
    }
    #[test]
    fn test_exponent_8()
    {
        let entries = super::CONSTANT_MAX_POWER_CACHE_8.iter().enumerate()
            .map(|(i,(p,e))| (i+2, ArbitraryBytes(*p), *e));
        for (base, mut power, exponent) in entries {
            //exponent is the largest fitting exponent. Soo, if we divide exponent times, we should end up with 1.
            for _i in 0..exponent  {
                let remainder = power.div_assign_with_remainder_usize(base);
                assert_eq!(remainder, 0);
            }
            assert_eq!(power, (&1usize).into());
        }
    }
    #[test]
    fn test_exponent_5()
    {
        let entries = super::CONSTANT_MAX_POWER_CACHE_5.iter().enumerate()
            .map(|(i,(p,e))| (i+2, ArbitraryBytes(*p), *e));
        for (base, mut power, exponent) in entries {
            //exponent is the largest fitting exponent. Soo, if we divide exponent times, we should end up with 1.
            for _i in 0..exponent  {
                let remainder = power.div_assign_with_remainder_usize(base);
                assert_eq!(remainder, 0);
            }
            assert_eq!(power, (&1usize).into());
        }
    }
    #[test]
    fn highest_fitting_power_consistency_5(){
        use super::super::super::iterative_conversion::IterativeBaseConversion;
        let entries = super::CONSTANT_MAX_POWER_CACHE_5.iter().enumerate()
            .map(|(i,(p,e))| (i+2, ArbitraryBytes(*p), *e));
        for (base, power, exponent) in entries {
            let non_cached_result = IterativeBaseConversion::<ArbitraryBytes<5>,usize>::find_highest_fitting_power_non_cached(&base);
            assert_eq!(non_cached_result.exponent,exponent);
            assert_eq!(non_cached_result.power, power);
        }
    }
    #[test]
    fn highest_fitting_power_consistency_8(){
        use super::super::super::iterative_conversion::IterativeBaseConversion;
        let entries = super::CONSTANT_MAX_POWER_CACHE_8.iter().enumerate()
            .map(|(i,(p,e))| (i+2, ArbitraryBytes(*p), *e));
        for (base, power, exponent) in entries {
            let non_cached_result = IterativeBaseConversion::<ArbitraryBytes<8>,usize>::find_highest_fitting_power_non_cached(&base);
            assert_eq!(non_cached_result.exponent,exponent);
            assert_eq!(non_cached_result.power, power);
        }
    }
}