binomial_hash_function.sf 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. #!/usr/bin/ruby
  2. # Author: Trizen
  3. # Date: 28 October 2022
  4. # https://github.com/trizen
  5. # A very simple hash function (most likely, not secure), using the binomial function to create the avalanche effect.
  6. # See also:
  7. # https://oeis.org/A014062
  8. # https://yewtu.be/watch?v=KqqOXndnvic
  9. # https://en.wikipedia.org/wiki/Avalanche_effect
  10. define (
  11. BLOCK_SIZE = 16, # in bytes
  12. MODULO = 2**128,
  13. )
  14. func process_block(str) {
  15. func f(bits) {
  16. bits.sum_kv {|k,v|
  17. binomial(k*k + v, k)
  18. } % MODULO
  19. }
  20. while (str.len < BLOCK_SIZE) {
  21. var arr = str.bytes.map{.inc}
  22. str += arr.sum
  23. str += arr.prod
  24. }
  25. var A = f(str.ascii2bits)
  26. var B = f(str.reverse.ascii2bits)
  27. mulmod(A, B, MODULO)
  28. }
  29. func prepare_result(num) {
  30. sprintf('%0*s', 2*BLOCK_SIZE, num % MODULO -> as_hex)
  31. }
  32. func binomial_hash_string(str) {
  33. prepare_result(str.encode_utf8.split(BLOCK_SIZE).map(process_block).sum)
  34. }
  35. func binomial_hash_file(filename) {
  36. var total = 0
  37. var fh = File(filename).open('<:raw') || return nil
  38. while (fh.read(\var block, BLOCK_SIZE)) {
  39. total += process_block(block)
  40. }
  41. prepare_result(total)
  42. }
  43. var tests = [
  44. '',
  45. "\0",
  46. 'a',
  47. 'b',
  48. 'ab',
  49. 'ba',
  50. 'abc',
  51. 'acb',
  52. 'message digest',
  53. 'abcdefghijklmnopqrstuvwxyz',
  54. 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789',
  55. '12345678901234567890123456789012345678901234567890123456789012345678901234567890',
  56. ]
  57. for msg in tests {
  58. var hash = binomial_hash_string(msg)
  59. say "#{hash} : #{msg.dump}"
  60. }
  61. printf("\nBinomial hash of this script: %s\n\n", binomial_hash_file(__FILE__))
  62. #
  63. ## Run some special tests
  64. #
  65. assert_ne(
  66. binomial_hash_string(BLOCK_SIZE.of(50.chr).join),
  67. binomial_hash_string(BLOCK_SIZE.dec.of(50.chr).join)
  68. )
  69. assert_ne(
  70. binomial_hash_string(BLOCK_SIZE.of(16.chr).join),
  71. binomial_hash_string(BLOCK_SIZE.dec.of(8.chr).join)
  72. )
  73. assert_ne(
  74. binomial_hash_string(BLOCK_SIZE.of(16.chr).join),
  75. binomial_hash_string(BLOCK_SIZE.dec.of(16.chr).join)
  76. )
  77. assert_ne(
  78. binomial_hash_string(BLOCK_SIZE.dec.of(15.chr).join),
  79. binomial_hash_string(BLOCK_SIZE.dec.dec.of(15.chr).join)
  80. )
  81. assert_ne(
  82. binomial_hash_string(BLOCK_SIZE.inc.of(2.chr).join),
  83. binomial_hash_string(BLOCK_SIZE.inc.inc.of(2.chr).join)
  84. )
  85. assert_ne(
  86. binomial_hash_string(BLOCK_SIZE.of(15.chr).join),
  87. binomial_hash_string(BLOCK_SIZE.dec.of(15.chr).join)
  88. )
  89. assert_ne(
  90. binomial_hash_string(BLOCK_SIZE.dec.of(14.chr).join),
  91. binomial_hash_string(BLOCK_SIZE.dec.dec.of(14.chr).join)
  92. )
  93. assert_ne(
  94. binomial_hash_string(BLOCK_SIZE.inc.of(1.chr).join),
  95. binomial_hash_string(BLOCK_SIZE.inc.inc.of(1.chr).join)
  96. )
  97. assert_ne(
  98. binomial_hash_string(BLOCK_SIZE.of(126.chr).join),
  99. binomial_hash_string(BLOCK_SIZE.of(63.chr).join)
  100. )
  101. assert_ne(
  102. binomial_hash_string(BLOCK_SIZE.of(254.chr).join),
  103. binomial_hash_string(BLOCK_SIZE.of(191.chr).join)
  104. )
  105. assert_ne(
  106. binomial_hash_string(BLOCK_SIZE.of(124.chr).join),
  107. binomial_hash_string(BLOCK_SIZE.of(62.chr).join)
  108. )
  109. assert_ne(
  110. binomial_hash_string("1"),
  111. binomial_hash_string("149"),
  112. )
  113. for n in (48..52) {
  114. assert_ne(
  115. binomial_hash_string(BLOCK_SIZE.inc.of(n.chr).join),
  116. binomial_hash_string(BLOCK_SIZE.inc.inc.of(n.chr).join),
  117. "Collision for n = #{n}"
  118. )
  119. }
  120. __END__
  121. 00000000000000000000000000000000 : ""
  122. fea891fdc54930fd39620fcc478299f0 : "\0"
  123. e1a06e532bba0bcf2ef6115e06f6e410 : "a"
  124. 004cac7e4dcb9c4129aa7748278626f2 : "b"
  125. 82eb8712eb98d42f187531a7eb3f32b2 : "ab"
  126. c8f774efd7bc079c0b749703106346c2 : "ba"
  127. 23c44e458e6e4b79109b9a0417499796 : "abc"
  128. 643c218207789c53e40d246912feb456 : "acb"
  129. d55aee8c85258c85ed4a866bf4891014 : "message digest"
  130. 82a9f92413c1b3cac57f28488d61836b : "abcdefghijklmnopqrstuvwxyz"
  131. c4c22251fd0388692867eefe8af04b99 : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
  132. bfcdbe7ba1ae8243902eee55226f3a0c : "12345678901234567890123456789012345678901234567890123456789012345678901234567890"