1 module bamboo.hashgen;
2 
3 import std.traits;
4 import bamboo.legacy_hash;
5 import bamboo.types;
6 
7 /// Computes a hash using `bamboo.legacy_hash`.
8 uint computeHash(Module file)
9 {
10     HashGenerator gen;
11     hashModule(gen, file);
12     return gen.hash;
13 }
14 
15 /// Provides mechanisms for generating hashes for DClasses.
16 struct HashGenerator
17 {
18     private size_t _hash;
19     private size_t _index;
20     private PrimeGenerator!size_t _primes;
21 
22 nothrow @nogc @safe:
23 
24     /// Adds an int to the accumulator.
25     void addInt(size_t num)
26     {
27         _index++;
28         _hash += _primes.front * num;
29         _primes.popFront;
30     }
31 
32     /// Adds a string to the accumulator.
33     void addString(string str)
34     {
35         addInt(str.length);
36 
37         foreach (c; str)
38         {
39             addInt(cast(size_t) c);
40         }
41     }
42 
43     /// Gets the current hash.
44     uint hash() inout @property
45     {
46         return cast(uint)(_hash & 0xffffffff);
47     }
48 }
49 
50 /// Utility for generating prime numers.
51 struct PrimeGenerator(T) if (isIntegral!T)
52 {
53     private T _current = 2;
54 
55 pure nothrow @nogc @safe:
56 
57     ///
58     enum bool empty = false;
59 
60     /// Returns: the current prime.
61     T front() inout @property
62     {
63         return _current;
64     }
65 
66     /// Advances the generator.
67     void popFront()
68     {
69         if (_current == 2)
70         {
71             ++_current;
72             return;
73         }
74 
75         // This is not at all efficient.
76         while (!isPrime(_current += 2))
77         {
78         }
79     }
80 
81     /// Creates a copy of the generator.
82     typeof(this) save()
83     {
84         return PrimeGenerator(_current);
85     }
86 }
87 
88 ///
89 pure nothrow @nogc @safe unittest
90 {
91     import std.algorithm : equal;
92     import std.range : take;
93 
94     // dfmt off
95     static immutable earlyPrimes = 
96        [ 2,   3,   5,   7,  11,   13,  17,  19,  23,  29,
97         31,  37,  41,  43,  47,   53,  59,  61,  67,  71,
98         73,  79,  83,  89,  97,  101, 103, 107, 109, 113,
99         127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 
100         179, 181, 191, 193, 197, 199, 211, 223, 227, 229];
101     // dfmt on
102 
103     auto primes = PrimeGenerator!int();
104 
105     assert(equal(earlyPrimes, primes.take(earlyPrimes.length)));
106 }
107 
108 /// Checks if a number is prime.
109 /// Returns: `true` if `n` is prime.
110 bool isPrime(T)(T n) if (isIntegral!T)
111 {
112     if (n == 2)
113     {
114         return true;
115     }
116     if (n == 1 || n < 1 || (n & 1) == 0)
117     {
118         return false;
119     }
120     if (n > 3)
121     {
122         for (auto i = 3; i * i <= n; i += 2)
123         {
124             if ((n % i) == 0)
125                 return false;
126         }
127     }
128     return true;
129 }
130 
131 ///
132 pure nothrow @nogc @safe unittest
133 {
134     assert(isPrime(2));
135     assert(isPrime(3));
136     assert(isPrime(5));
137     assert(isPrime(7));
138     assert(isPrime(199UL));
139     assert(isPrime(104_729)); // 10,000th prime.
140 
141     assert(!isPrime(-199));
142     assert(!isPrime(-1));
143     assert(!isPrime(1)); // 1 is not a prime number: https://primes.utm.edu/notes/faq/one.html
144     assert(!isPrime(4));
145 }