pHash 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. data/.gitignore +12 -0
  2. data/LICENSE.txt +20 -0
  3. data/README.markdown +53 -0
  4. data/audiophash.diff +17 -0
  5. data/lib/phash.rb +44 -0
  6. data/lib/phash/all.rb +3 -0
  7. data/lib/phash/audio.rb +116 -0
  8. data/lib/phash/image.rb +59 -0
  9. data/lib/phash/text.rb +100 -0
  10. data/lib/phash/video.rb +55 -0
  11. data/pHash.gemspec +20 -0
  12. data/spec/data/audiophash.cpp-0.9.3.txt +571 -0
  13. data/spec/data/audiophash.cpp-0.9.4.txt +572 -0
  14. data/spec/data/audiophash.h-0.9.3.txt +111 -0
  15. data/spec/data/audiophash.h-0.9.4.txt +108 -0
  16. data/spec/data/hal9000-m.mp3 +0 -0
  17. data/spec/data/hal9000-o.mp3 +0 -0
  18. data/spec/data/jug-0-10.jpg +0 -0
  19. data/spec/data/jug-0-120.png +0 -0
  20. data/spec/data/jug-0-50.jpg +0 -0
  21. data/spec/data/jug-0-70.jpg +0 -0
  22. data/spec/data/jug-1-10.jpg +0 -0
  23. data/spec/data/jug-1-120.png +0 -0
  24. data/spec/data/jug-1-50.jpg +0 -0
  25. data/spec/data/jug-1-70.jpg +0 -0
  26. data/spec/data/jug-120.mp4 +0 -0
  27. data/spec/data/jug-150.mp4 +0 -0
  28. data/spec/data/jug-180.mp4 +0 -0
  29. data/spec/data/jug-2-10.jpg +0 -0
  30. data/spec/data/jug-2-120.png +0 -0
  31. data/spec/data/jug-2-50.jpg +0 -0
  32. data/spec/data/jug-2-70.jpg +0 -0
  33. data/spec/data/mouse-0-10.jpg +0 -0
  34. data/spec/data/mouse-0-120.png +0 -0
  35. data/spec/data/mouse-0-50.jpg +0 -0
  36. data/spec/data/mouse-0-70.jpg +0 -0
  37. data/spec/data/mouse-1-10.jpg +0 -0
  38. data/spec/data/mouse-1-120.png +0 -0
  39. data/spec/data/mouse-1-50.jpg +0 -0
  40. data/spec/data/mouse-1-70.jpg +0 -0
  41. data/spec/data/mouse-120.mp4 +0 -0
  42. data/spec/data/mouse-150.mp4 +0 -0
  43. data/spec/data/mouse-180.mp4 +0 -0
  44. data/spec/data/mouse-2-10.jpg +0 -0
  45. data/spec/data/mouse-2-120.png +0 -0
  46. data/spec/data/mouse-2-50.jpg +0 -0
  47. data/spec/data/mouse-2-70.jpg +0 -0
  48. data/spec/data/scream-m.mp3 +0 -0
  49. data/spec/data/scream-o.mp3 +0 -0
  50. data/spec/data/vader-m.mp3 +0 -0
  51. data/spec/data/vader-o.mp3 +0 -0
  52. data/spec/phash_spec.rb +43 -0
  53. data/spec/spec_helper.rb +10 -0
  54. metadata +186 -0
@@ -0,0 +1,572 @@
1
+ /*
2
+
3
+ pHash, the open source perceptual hash library
4
+ Copyright (C) 2009 Aetilius, Inc.
5
+ All rights reserved.
6
+
7
+ This program is free software: you can redistribute it and/or modify
8
+ it under the terms of the GNU General Public License as published by
9
+ the Free Software Foundation, either version 3 of the License, or
10
+ (at your option) any later version.
11
+
12
+ This program is distributed in the hope that it will be useful,
13
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ GNU General Public License for more details.
16
+
17
+ You should have received a copy of the GNU General Public License
18
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
19
+
20
+ Evan Klinger - eklinger@phash.org
21
+ David Starkweather - dstarkweather@phash.org
22
+
23
+ */
24
+
25
+ #include "audiophash.h"
26
+ #include <sndfile.h>
27
+ #include <samplerate.h>
28
+ #include "config.h"
29
+
30
+ #ifdef HAVE_LIBMPG123
31
+ #include <mpg123.h>
32
+ #endif
33
+
34
+ int ph_count_samples(const char *filename, int sr,int channels){
35
+
36
+ SF_INFO sf_info;
37
+ sf_info.format=0;
38
+ SNDFILE *sndfile = sf_open(filename, SFM_READ, &sf_info);
39
+ if (sndfile == NULL){
40
+ return NULL;
41
+ }
42
+ int count = sf_info.frames;
43
+ sf_close(sndfile);
44
+ return count;
45
+ }
46
+
47
+ #ifdef HAVE_LIBMPG123
48
+
49
+ static
50
+ float* readaudio_mp3(const char *filename,long *sr, const float nbsecs, unsigned int *buflen){
51
+ mpg123_handle *m;
52
+ int ret;
53
+
54
+ if (mpg123_init() != MPG123_OK || ((m = mpg123_new(NULL,&ret)) == NULL)|| \
55
+ mpg123_open(m, filename) != MPG123_OK){
56
+ fprintf(stderr,"unable to init mpg\n");
57
+ return NULL;
58
+ }
59
+
60
+ /*turn off logging */
61
+ mpg123_param(m, MPG123_ADD_FLAGS, MPG123_QUIET, 0);
62
+
63
+ off_t totalsamples;
64
+
65
+ mpg123_scan(m);
66
+ totalsamples = mpg123_length(m);
67
+
68
+ int meta = mpg123_meta_check(m);
69
+
70
+ int channels, encoding;
71
+
72
+ if (mpg123_getformat(m, sr, &channels, &encoding) != MPG123_OK){
73
+ fprintf(stderr,"unable to get format\n");
74
+ return NULL;
75
+ }
76
+
77
+ mpg123_format_none(m);
78
+ mpg123_format(m, *sr, channels, encoding);
79
+
80
+ size_t decbuflen = mpg123_outblock(m);
81
+ unsigned char *decbuf = (unsigned char*)malloc(decbuflen);
82
+ if (decbuf == NULL){
83
+ printf("mem alloc error\n");
84
+ return NULL;
85
+ }
86
+
87
+ unsigned int nbsamples = (nbsecs <= 0) ? totalsamples : nbsecs*(*sr);
88
+ nbsamples = (nbsamples < totalsamples) ? nbsamples : totalsamples;
89
+
90
+ size_t i, j, index = 0, done;
91
+
92
+
93
+ float *buffer = (float*)malloc(nbsamples*sizeof(float));
94
+ *buflen = nbsamples;
95
+
96
+ do {
97
+
98
+ ret = mpg123_read(m, decbuf, decbuflen, &done);
99
+ switch (encoding) {
100
+ case MPG123_ENC_SIGNED_16 :
101
+ for (i = 0; i < done/sizeof(short); i+=channels){
102
+ buffer[index] = 0.0f;
103
+ for (j = 0; j < channels ; j++){
104
+ buffer[index] += (float)(((short*)decbuf)[i+j])/(float)SHRT_MAX;
105
+ }
106
+ buffer[index++] /= channels;
107
+ if (index >= nbsamples) break;
108
+ }
109
+ break;
110
+ case MPG123_ENC_SIGNED_8:
111
+ for (i = 0; i < done/sizeof(char); i+=channels){
112
+ buffer[index] = 0.0f;
113
+ for (j = 0; j < channels ; j++){
114
+ buffer[index] += (float)(((char*)decbuf)[i+j])/(float)SCHAR_MAX;
115
+ }
116
+ buffer[index++] /= channels;
117
+ if (index >= nbsamples) break;
118
+ }
119
+ break;
120
+ case MPG123_ENC_FLOAT_32:
121
+ for (i = 0; i < done/sizeof(float); i+=channels){
122
+ buffer[index] = 0.0f;
123
+ for (j = 0; j < channels; j++){
124
+ buffer[index] += ((float*)decbuf)[i+j];
125
+ }
126
+ buffer[index++] /= channels;
127
+ if (index >= nbsamples) break;
128
+ }
129
+ break;
130
+ default:
131
+ done = 0;
132
+ }
133
+
134
+ } while (ret == MPG123_OK && index < nbsamples);
135
+
136
+ free(decbuf);
137
+ mpg123_close(m);
138
+ mpg123_delete(m);
139
+ mpg123_exit();
140
+
141
+ return buffer;
142
+ }
143
+
144
+ #endif /*HAVE_LIBMPG123*/
145
+
146
+ static
147
+ float *readaudio_snd(const char *filename, long *sr, const float nbsecs, unsigned int *buflen){
148
+
149
+ SF_INFO sf_info;
150
+ sf_info.format=0;
151
+ SNDFILE *sndfile = sf_open(filename, SFM_READ, &sf_info);
152
+ if (sndfile == NULL){
153
+ return NULL;
154
+ }
155
+
156
+ /* normalize */
157
+ sf_command(sndfile, SFC_SET_NORM_FLOAT, NULL, SF_TRUE);
158
+
159
+ *sr = (long)sf_info.samplerate;
160
+
161
+ //allocate input buffer for signal
162
+ unsigned int src_frames = (nbsecs <= 0) ? sf_info.frames : (nbsecs*sf_info.samplerate);
163
+ src_frames = (sf_info.frames < src_frames) ? sf_info.frames : src_frames;
164
+ float *inbuf = (float*)malloc(src_frames*sf_info.channels*sizeof(float));
165
+
166
+ /*read frames */
167
+ sf_count_t cnt_frames = sf_readf_float(sndfile, inbuf, src_frames);
168
+
169
+ float *buf = (float*)malloc(cnt_frames*sizeof(float));
170
+
171
+ //average across all channels
172
+ int i,j,indx=0;
173
+ for (i=0;i<cnt_frames*sf_info.channels;i+=sf_info.channels){
174
+ buf[indx] = 0;
175
+ for (j=0;j<sf_info.channels;j++){
176
+ buf[indx] += inbuf[i+j];
177
+ }
178
+ buf[indx++] /= sf_info.channels;
179
+ }
180
+ free(inbuf);
181
+
182
+ *buflen = indx;
183
+ return buf;
184
+ }
185
+
186
+ float* ph_readaudio2(const char *filename, int sr, float *sigbuf, int &buflen, const float nbsecs){
187
+
188
+ long orig_sr;
189
+ float *inbuffer = NULL;
190
+ unsigned int inbufferlength;
191
+ buflen = 0;
192
+
193
+ const char *suffix = strrchr(filename, '.');
194
+ if (suffix == NULL) return NULL;
195
+ if (!strcasecmp(suffix+1, "mp3")) {
196
+ #ifdef HAVE_LIBMPG123
197
+ inbuffer = readaudio_mp3(filename, &orig_sr, nbsecs, &inbufferlength);
198
+ #endif /* HAVE_LIBMPG123 */
199
+ } else {
200
+ inbuffer = readaudio_snd(filename, &orig_sr, nbsecs, &inbufferlength);
201
+ }
202
+
203
+ if (inbuffer == NULL){
204
+ return NULL;
205
+ }
206
+
207
+ /* resample float array */
208
+ /* set desired sr ratio */
209
+ double sr_ratio = (double)(sr)/(double)orig_sr;
210
+ if (src_is_valid_ratio(sr_ratio) == 0){
211
+ free(inbuffer);
212
+ return NULL;
213
+ }
214
+
215
+ /* allocate output buffer for conversion */
216
+ unsigned int outbufferlength = sr_ratio*inbufferlength;
217
+ float *outbuffer = (float*)malloc(outbufferlength*sizeof(float));
218
+ if (!outbuffer){
219
+ free(inbuffer);
220
+ return NULL;
221
+ }
222
+
223
+ int error;
224
+ SRC_STATE *src_state = src_new(SRC_LINEAR, 1, &error);
225
+ if (!src_state){
226
+ free(inbuffer);
227
+ free(outbuffer);
228
+ return NULL;
229
+ }
230
+
231
+ SRC_DATA src_data;
232
+ src_data.data_in = inbuffer;
233
+ src_data.data_out = outbuffer;
234
+ src_data.input_frames = inbufferlength;
235
+ src_data.output_frames = outbufferlength;
236
+ src_data.end_of_input = SF_TRUE;
237
+ src_data.src_ratio = sr_ratio;
238
+
239
+ /* sample rate conversion */
240
+ if (error = src_process(src_state, &src_data)){
241
+ free(inbuffer);
242
+ free(outbuffer);
243
+ src_delete(src_state);
244
+ return NULL;
245
+ }
246
+
247
+ buflen = src_data.output_frames;
248
+
249
+ src_delete(src_state);
250
+ free(inbuffer);
251
+
252
+ return outbuffer;
253
+ }
254
+
255
+
256
+ float* ph_readaudio(const char *filename, int sr, int channels, float *sigbuf, int &buflen,\
257
+ const float nbsecs){
258
+ if(!filename || sr <= 0)
259
+ return NULL;
260
+ return ph_readaudio2(filename, sr, sigbuf, buflen, nbsecs);
261
+ }
262
+
263
+ uint32_t* ph_audiohash(float *buf, int N, int sr, int &nb_frames){
264
+
265
+ int frame_length = 4096;//2^12
266
+ int nfft = frame_length;
267
+ int nfft_half = 2048;
268
+ int start = 0;
269
+ int end = start + frame_length - 1;
270
+ int overlap = (int)(31*frame_length/32);
271
+ int advance = frame_length - overlap;
272
+ int index = 0;
273
+ nb_frames = (int)(floor(N/advance) - floor(frame_length/advance) + 1);
274
+ double window[frame_length];
275
+ for (int i = 0;i<frame_length;i++){
276
+ //hamming window
277
+ window[i] = 0.54 - 0.46*cos(2*M_PI*i/(frame_length-1));
278
+ }
279
+
280
+ double frame[frame_length];
281
+ //fftw_complex *pF;
282
+ //fftw_plan p;
283
+ //pF = (fftw_complex*)fftw_malloc(sizeof(fftw_complex)*nfft);
284
+ complex double *pF = (complex double*)malloc(sizeof(complex double)*nfft);
285
+
286
+ double magnF[nfft_half];
287
+ double maxF = 0.0;
288
+ double maxB = 0.0;
289
+
290
+ double minfreq = 300;
291
+ double maxfreq = 3000;
292
+ double minbark = 6*asinh(minfreq/600.0);
293
+ double maxbark = 6*asinh(maxfreq/600.0);
294
+ double nyqbark = maxbark - minbark;
295
+ int nfilts = 33;
296
+ double stepbarks = nyqbark/(nfilts - 1);
297
+ int nb_barks = (int)(floor(nfft_half/2 + 1));
298
+ double barkwidth = 1.06;
299
+
300
+ double freqs[nb_barks];
301
+ double binbarks[nb_barks];
302
+ double curr_bark[nfilts];
303
+ double prev_bark[nfilts];
304
+ for (int i=0;i< nfilts;i++){
305
+ prev_bark[i] = 0.0;
306
+ }
307
+ uint32_t *hash = (uint32_t*)malloc(nb_frames*sizeof(uint32_t));
308
+ double lof,hif;
309
+
310
+ for (int i=0; i < nb_barks;i++){
311
+ binbarks[i] = 6*asinh(i*sr/nfft_half/600.0);
312
+ freqs[i] = i*sr/nfft_half;
313
+ }
314
+ double **wts = new double*[nfilts];
315
+ for (int i=0;i<nfilts;i++){
316
+ wts[i] = new double[nfft_half];
317
+ }
318
+ for (int i=0;i<nfilts;i++){
319
+ for (int j=0;j<nfft_half;j++){
320
+ wts[i][j] = 0.0;
321
+ }
322
+ }
323
+
324
+ //calculate wts for each filter
325
+ for (int i=0;i<nfilts;i++){
326
+ double f_bark_mid = minbark + i*stepbarks;
327
+ for (int j=0;j<nb_barks;j++){
328
+ double barkdiff = binbarks[j] - f_bark_mid;
329
+ lof = -2.5*(barkdiff/barkwidth - 0.5);
330
+ hif = barkdiff/barkwidth + 0.5;
331
+ double m = std::min(lof,hif);
332
+ m = std::min(0.0,m);
333
+ m = pow(10,m);
334
+ wts[i][j] = m;
335
+ }
336
+ }
337
+
338
+ //p = fftw_plan_dft_r2c_1d(frame_length,frame,pF,FFTW_ESTIMATE);
339
+
340
+ while (end < N){
341
+ maxF = 0.0;
342
+ maxB = 0.0;
343
+ for (int i = 0;i<frame_length;i++){
344
+ frame[i] = window[i]*buf[start+i];
345
+ }
346
+ //fftw_execute(p);
347
+ if (fft(frame, frame_length, pF) < 0){
348
+ return NULL;
349
+ }
350
+ for (int i=0; i < nfft_half;i++){
351
+ //magnF[i] = sqrt(pF[i][0]*pF[i][0] + pF[i][1]*pF[i][1] );
352
+ magnF[i] = cabs(pF[i]);
353
+ if (magnF[i] > maxF){
354
+ maxF = magnF[i];
355
+ }
356
+ }
357
+
358
+ for (int i=0;i<nfilts;i++){
359
+ curr_bark[i] = 0;
360
+ for (int j=0;j < nfft_half;j++){
361
+ curr_bark[i] += wts[i][j]*magnF[j];
362
+ }
363
+ if (curr_bark[i] > maxB)
364
+ maxB = curr_bark[i];
365
+ }
366
+
367
+ uint32_t curr_hash = 0x00000000u;
368
+ for (int m=0;m<nfilts-1;m++){
369
+ double H = curr_bark[m] - curr_bark[m+1] - (prev_bark[m] - prev_bark[m+1]);
370
+ curr_hash = curr_hash << 1;
371
+ if (H > 0)
372
+ curr_hash |= 0x00000001;
373
+ }
374
+
375
+
376
+ hash[index] = curr_hash;
377
+ for (int i=0;i<nfilts;i++){
378
+ prev_bark[i] = curr_bark[i];
379
+ }
380
+ index += 1;
381
+ start += advance;
382
+ end += advance;
383
+ }
384
+
385
+
386
+
387
+ //fftw_destroy_plan(p);
388
+ //fftw_free(pF);
389
+ free(pF);
390
+ for (int i=0;i<nfilts;i++){
391
+ delete [] wts[i];
392
+ }
393
+ delete [] wts;
394
+ return hash;
395
+ }
396
+
397
+
398
+ int ph_bitcount(uint32_t n){
399
+
400
+ //parallel bit count
401
+ #define MASK_01010101 (((uint32_t)(-1))/3)
402
+ #define MASK_00110011 (((uint32_t)(-1))/5)
403
+ #define MASK_00001111 (((uint32_t)(-1))/17)
404
+
405
+ n = (n & MASK_01010101) + ((n >> 1) & MASK_01010101) ;
406
+ n = (n & MASK_00110011) + ((n >> 2) & MASK_00110011) ;
407
+ n = (n & MASK_00001111) + ((n >> 4) & MASK_00001111) ;
408
+ return n % 255;
409
+
410
+ }
411
+
412
+ double ph_compare_blocks(const uint32_t *ptr_blockA,const uint32_t *ptr_blockB, const int block_size){
413
+ double result = 0;
414
+ for (int i=0;i<block_size;i++){
415
+ uint32_t xordhash = ptr_blockA[i]^ptr_blockB[i];
416
+ result += ph_bitcount(xordhash);
417
+ }
418
+ result = result/(32*block_size);
419
+ return result;
420
+ }
421
+ double* ph_audio_distance_ber(uint32_t *hash_a , const int Na, uint32_t *hash_b, const int Nb, const float threshold, const int block_size, int &Nc){
422
+
423
+ uint32_t *ptrA, *ptrB;
424
+ int N1, N2;
425
+ if (Na <= Nb){
426
+ ptrA = hash_a;
427
+ ptrB = hash_b;
428
+ Nc = Nb - Na + 1;
429
+ N1 = Na;
430
+ N2 = Nb;
431
+ } else {
432
+ ptrB = hash_a;
433
+ ptrA = hash_b;
434
+ Nc = Na - Nb + 1;
435
+ N1 = Nb;
436
+ N2 = Na;
437
+ }
438
+
439
+ double *pC = new double[Nc];
440
+ if (!pC)
441
+ return NULL;
442
+ int k,M,nb_above, nb_below, hash1_index,hash2_index;
443
+ double sum_above, sum_below,above_factor, below_factor;
444
+
445
+ uint32_t *pha,*phb;
446
+ double *dist = NULL;
447
+
448
+ for (int i=0; i < Nc;i++){
449
+
450
+ M = (int)floor(std::min(N1,N2-i)/block_size);
451
+
452
+ pha = ptrA;
453
+ phb = ptrB + i;
454
+
455
+ double *tmp_dist = (double*)realloc(dist, M*sizeof(double));
456
+ if (!tmp_dist){
457
+ return NULL;
458
+ }
459
+ dist = tmp_dist;
460
+ dist[0] = ph_compare_blocks(pha,phb,block_size);
461
+
462
+ k = 1;
463
+
464
+ pha += block_size;
465
+ phb += block_size;
466
+
467
+ hash1_index = block_size;
468
+ hash2_index = i + block_size;
469
+
470
+ while ((hash1_index < N1 - block_size) && (hash2_index < N2 - block_size)){
471
+ dist[k++] = ph_compare_blocks(pha,phb,block_size);
472
+ hash1_index += block_size;
473
+ hash2_index += block_size;
474
+ pha += block_size;
475
+ phb += block_size;
476
+ }
477
+ sum_above = 0;
478
+ sum_below = 0;
479
+ nb_above = 0;
480
+ nb_below = 0;
481
+ for (int n = 0; n < M; n++){
482
+
483
+ if (dist[n] <= threshold){
484
+ sum_below += 1-dist[n];
485
+ nb_below++;
486
+ } else {
487
+ sum_above += 1-dist[n];
488
+ nb_above++;
489
+ }
490
+ }
491
+ above_factor = sum_above/M;
492
+ below_factor = sum_below/M;
493
+ pC[i] = 0.5*(1 + below_factor - above_factor);
494
+ }
495
+
496
+ free(dist);
497
+ return pC;
498
+ }
499
+ #ifdef HAVE_PTHREAD
500
+
501
+ void *ph_audio_thread(void *p)
502
+ {
503
+ slice *s = (slice *)p;
504
+ for(int i = 0; i < s->n; ++i)
505
+ {
506
+ DP *dp = (DP *)s->hash_p[i];
507
+ int N, count;
508
+ pair<int,int> *p = (pair<int,int> *)s->hash_params;
509
+ float *buf = ph_readaudio(dp->id, p->first, p->second, NULL, N);
510
+ uint32_t *hash = ph_audiohash(buf, N, p->first, count);
511
+ free(buf);
512
+ buf = NULL;
513
+ dp->hash = hash;
514
+ dp->hash_length = count;
515
+ }
516
+ }
517
+
518
+ DP** ph_audio_hashes(char *files[], int count, int sr, int channels, int threads)
519
+ {
520
+ if(!files || count == 0)
521
+ return NULL;
522
+
523
+ int num_threads;
524
+ if(threads > count)
525
+ {
526
+ num_threads = count;
527
+ }
528
+ else if(threads > 0)
529
+ {
530
+ num_threads = threads;
531
+ }
532
+ else
533
+ {
534
+ num_threads = ph_num_threads();
535
+ }
536
+
537
+ DP **hashes = (DP**)malloc(count*sizeof(DP*));
538
+
539
+ for(int i = 0; i < count; ++i)
540
+ {
541
+ hashes[i] = (DP *)malloc(sizeof(DP));
542
+ hashes[i]->id = strdup(files[i]);
543
+ }
544
+
545
+ pthread_t thds[num_threads];
546
+
547
+ int rem = count % num_threads;
548
+ int start = 0;
549
+ int off = 0;
550
+ slice *s = new slice[num_threads];
551
+ for(int n = 0; n < num_threads; ++n)
552
+ {
553
+ off = (int)floor((count/(float)num_threads) + (rem>0?num_threads-(count % num_threads):0));
554
+
555
+ s[n].hash_p = &hashes[start];
556
+ s[n].n = off;
557
+ s[n].hash_params = new pair<int,int>(sr,channels);
558
+ start += off;
559
+ --rem;
560
+ pthread_create(&thds[n], NULL, ph_audio_thread, &s[n]);
561
+ }
562
+ for(int i = 0; i < num_threads; ++i)
563
+ {
564
+ pthread_join(thds[i], NULL);
565
+ delete (pair<int,int>*)s[i].hash_params;
566
+ }
567
+ delete[] s;
568
+
569
+ return hashes;
570
+
571
+ }
572
+ #endif