-
Notifications
You must be signed in to change notification settings - Fork 931
/
Copy pathhashing.js
136 lines (115 loc) · 3.69 KB
/
hashing.js
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
/* eslint-disable */
// tag::naiveHashCode[]
/**
* Naïve implementation of a non-cryptographic hashing function
* @param {any} key to be converted to a positive integer
* @returns {integer} hash code (numeric representation of the key)
*/
function hashCodeNaive(key) {
return Array.from(key.toString()).reduce((hashCode, char) => {
return hashCode + char.codePointAt(0);
}, 0);
}
// end::naiveHashCode[]
/* Hash Code examples
// tag::naiveHashCodeExamples[]
hashCode('cat'); //=> 312 (c=99 + a=97 + t=116)
hashCode('dog'); //=> 314 (d=100 + o=111 + g=103)
hashCode('rat'); //=> 327 (r=114 + a=97 + t=116)
hashCode('art'); //=> 327 (a=97 + r=114 + t=116)
hashCode(10); //=> 97 ('1'=49 + '0'=48)
// end::naiveHashCodeExamples[]
*/
// tag::hashCodeOffset[]
/**
* Calculates hash code that maps a key (value) to an integer (unbounded).
* It uses a 20 bit offset to avoid Unicode value overlaps
* @param {any} key to be converted to a positive integer
* @returns {BigInt} returns big integer (unbounded) that maps to the key
*/
function hashCode(key) {
const array = Array.from(`${key}${typeof key}`);
return array.reduce((hashCode, char, position) => {
return hashCode + BigInt(char.codePointAt(0)) * (2n ** (BigInt(position) * 20n));
}, 0n);
}
// end::hashCodeOffset[]
/*
// tag::hashCodeOffsetExample[]
hashCode('art') //↪️ 150534821962845809557083360656040988391557528813665n
hashCode(10) === hashCode('10'); //↪️ false
hashCode('10') === hashCode('10string'); //↪️ false
hashCode('art') === hashCode('rat'); //↪️ false
hashCode('😄') === hashCode('😄'); //↪️ true
hashCode('😄') === hashCode('😸'); //↪️ false
// end::hashCodeOffsetExample[]
*/
// ---- Experiments -----
const primes = [31n, 33n, 37n, 39n, 41n, 101n, 8191n, 131071n, 524287n, 6700417n, 1327144003n, 9007199254740881n];
function doubleToLongBits(number) {
const buffer = new ArrayBuffer(8); // 8 bytes for float64
const dataView = new DataView(buffer);
dataView.setFloat64(0, number); // set as float64
return dataView.getBigInt64(0); // read as long int (BigInt)
}
function hashNumber(number) {
const bigInt = doubleToLongBits(number);
return bigInt > 0 ? bigInt : ((2n ** 63n) + (bigInt * -1n));
}
/**
* Polynomial hash codes
* @param {any} key
*/
function hashString(key) {
return Array.from(key.toString()).reduce((hash, char) => {
return (hash * 33n) + BigInt(char.codePointAt(0));
}, 0n);
}
function hashCode2(key) {
if (typeof(key) === 'number') {
return hashNumber(key);
}
return 2n ** 64n + hashString(key);
}
function hashIndex({key, size = 16} = {}) {
// return hashCode(key) % BigInt(size); // modulo size
// Multiply-Add-Divide (MAD) compression
const p = 524287n; // prime number larger than size.
const a = 8191n; // random [1..p-1]
const b = 0n; // random [0..p-1]
return ( (a * hashCode2(key) + b) % p ) % BigInt(size);
}
module.exports = {
hashCode: hashCode2,
hashIndex
}
/**
function prepareToPrint(key){
return { key: key.substring ? `${key.substring(0, 10)} (${key.length})` : key, hashCode: hashCode(key), hashIndex10: hashIndex({key, size: 10}) };
}
const res = [-2, -1, 0.5, 1, 2, 3, Math.PI, Number.MAX_VALUE, 2.7976931348623157e+308, 17.976931348623156e+400,
'😁',
'hola',
'@', '#', '#!', 'stop', 'pots', 'Ca', 'DB', 'polygenelubricants',
'Aa',
'BB',
'aoffckzdaoffckzdatafwjsh',
'aoffckzdaoffckzdbhlijevx',
Array(50).fill('1').join(''),
// types
{a:1},
1n,
1,
'1',
function a() {return;}
]
.map(prepareToPrint);
console.table(res);
const res1 = [
Array(1500).fill('1').join(''),
Array(1500).fill('😁').join(''),
// Array(1500).fill('z').join(''),
]
.map(prepareToPrint);
console.log(res1);
// */