Created
March 5, 2025 14:27
-
-
Save iomoath/6a08b8e6f9c8b561ab3a8edcd772fc0e to your computer and use it in GitHub Desktop.
Nilsimsa similarity hash - C#
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Nilsimsa [1] is a similarity hash which is used as an anti-spam focused locality-sensitive hashing method [here]. This produces a score from 0 (dissimilar objects) to 128 (identical or very similar objects). | |
| https://asecuritysite.com/hashsim/nil | |
| Nilsima was created Damiani et al. in order to detect spam emails http://spdp.di.unimi.it/papers/pdcs04.pdf | |
| It uses a 5-byte fixed-size sliding window that analyses the input on a byte-by-byte and produces trigrams of possible combinations of the input characters. | |
| The trigrams map into a 256-bit array (known as the accumulator) to create the hash, and every time a given position is accessed, its value is incremented. At the end of the processing, if the values are above a certain threshold, the value is set to a 1, else it will be zero. This produces a 32-byte digest | |
| To compare two hashes, the method checks the number of identical bits red to the the same position. This produces a score from 0 (dissimilar objects) to 128 (identical or very similar objects). | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using System; | |
| using System.Text; | |
| public class Nilsimsa | |
| { | |
| public const int DigestSize = 32; | |
| // Internal state | |
| private int _count; | |
| private readonly int[] _acc; | |
| private byte _c0, _c1, _c2, _c3; | |
| // Predefined translation table (256 bytes) | |
| private static readonly byte[] Tran = new byte[256] | |
| { | |
| 0x02, 0xD6, 0x9E, 0x6F, 0xF9, 0x1D, 0x04, 0xAB, | |
| 0xD0, 0x22, 0x16, 0x1F, 0xD8, 0x73, 0xA1, 0xAC, | |
| 0x3B, 0x70, 0x62, 0x96, 0x1E, 0x6E, 0x8F, 0x39, | |
| 0x9D, 0x05, 0x14, 0x4A, 0xA6, 0xBE, 0xAE, 0x0E, | |
| 0xCF, 0xB9, 0x9C, 0x9A, 0xC7, 0x68, 0x13, 0xE1, | |
| 0x2D, 0xA4, 0xEB, 0x51, 0x8D, 0x64, 0x6B, 0x50, | |
| 0x23, 0x80, 0x03, 0x41, 0xEC, 0xBB, 0x71, 0xCC, | |
| 0x7A, 0x86, 0x7F, 0x98, 0xF2, 0x36, 0x5E, 0xEE, | |
| 0x8E, 0xCE, 0x4F, 0xB8, 0x32, 0xB6, 0x5F, 0x59, | |
| 0xDC, 0x1B, 0x31, 0x4C, 0x7B, 0xF0, 0x63, 0x01, | |
| 0x6C, 0xBA, 0x07, 0xE8, 0x12, 0x77, 0x49, 0x3C, | |
| 0xDA, 0x46, 0xFE, 0x2F, 0x79, 0x1C, 0x9B, 0x30, | |
| 0xE3, 0x00, 0x06, 0x7E, 0x2E, 0x0F, 0x38, 0x33, | |
| 0x21, 0xAD, 0xA5, 0x54, 0xCA, 0xA7, 0x29, 0xFC, | |
| 0x5A, 0x47, 0x69, 0x7D, 0xC5, 0x95, 0xB5, 0xF4, | |
| 0x0B, 0x90, 0xA3, 0x81, 0x6D, 0x25, 0x55, 0x35, | |
| 0xF5, 0x75, 0x74, 0x0A, 0x26, 0xBF, 0x19, 0x5C, | |
| 0x1A, 0xC6, 0xFF, 0x99, 0x5D, 0x84, 0xAA, 0x66, | |
| 0x3E, 0xAF, 0x78, 0xB3, 0x20, 0x43, 0xC1, 0xED, | |
| 0x24, 0xEA, 0xE6, 0x3F, 0x18, 0xF3, 0xA0, 0x42, | |
| 0x57, 0x08, 0x53, 0x60, 0xC3, 0xC0, 0x83, 0x40, | |
| 0x82, 0xD7, 0x09, 0xBD, 0x44, 0x2A, 0x67, 0xA8, | |
| 0x93, 0xE0, 0xC2, 0x56, 0x9F, 0xD9, 0xDD, 0x85, | |
| 0x15, 0xB4, 0x8A, 0x27, 0x28, 0x92, 0x76, 0xDE, | |
| 0xEF, 0xF8, 0xB2, 0xB7, 0xC9, 0x3D, 0x45, 0x94, | |
| 0x4B, 0x11, 0x0D, 0x65, 0xD5, 0x34, 0x8B, 0x91, | |
| 0x0C, 0xFA, 0x87, 0xE9, 0x7C, 0x5B, 0xB1, 0x4D, | |
| 0xE5, 0xD4, 0xCB, 0x10, 0xA2, 0x17, 0x89, 0xBC, | |
| 0xDB, 0xB0, 0xE2, 0x97, 0x88, 0x52, 0xF7, 0x48, | |
| 0xD3, 0x61, 0x2C, 0x3A, 0x2B, 0xD1, 0x8C, 0xFB, | |
| 0xF1, 0xCD, 0xE4, 0x6A, 0xE7, 0xA9, 0xFD, 0xC4, | |
| 0x37, 0xC8, 0xD2, 0xF6, 0xDF, 0x58, 0x72, 0x4E, | |
| }; | |
| // Population count lookup table: popc[x] = number of 1's in x. | |
| private static readonly byte[] Popc = new byte[256] | |
| { | |
| 0x00, 0x01, 0x01, 0x02, 0x01, 0x02, 0x02, 0x03, | |
| 0x01, 0x02, 0x02, 0x03, 0x02, 0x03, 0x03, 0x04, | |
| 0x01, 0x02, 0x02, 0x03, 0x02, 0x03, 0x03, 0x04, | |
| 0x02, 0x03, 0x03, 0x04, 0x03, 0x04, 0x04, 0x05, | |
| 0x01, 0x02, 0x02, 0x03, 0x02, 0x03, 0x03, 0x04, | |
| 0x02, 0x03, 0x03, 0x04, 0x03, 0x04, 0x04, 0x05, | |
| 0x02, 0x03, 0x03, 0x04, 0x03, 0x04, 0x04, 0x05, | |
| 0x03, 0x04, 0x04, 0x05, 0x04, 0x05, 0x05, 0x06, | |
| 0x01, 0x02, 0x02, 0x03, 0x02, 0x03, 0x03, 0x04, | |
| 0x02, 0x03, 0x03, 0x04, 0x03, 0x04, 0x04, 0x05, | |
| 0x02, 0x03, 0x03, 0x04, 0x03, 0x04, 0x04, 0x05, | |
| 0x03, 0x04, 0x04, 0x05, 0x04, 0x05, 0x05, 0x06, | |
| 0x02, 0x03, 0x03, 0x04, 0x03, 0x04, 0x04, 0x05, | |
| 0x03, 0x04, 0x04, 0x05, 0x04, 0x05, 0x05, 0x06, | |
| 0x03, 0x04, 0x04, 0x05, 0x04, 0x05, 0x05, 0x06, | |
| 0x04, 0x05, 0x05, 0x06, 0x05, 0x06, 0x06, 0x07, | |
| 0x01, 0x02, 0x02, 0x03, 0x02, 0x03, 0x03, 0x04, | |
| 0x02, 0x03, 0x03, 0x04, 0x03, 0x04, 0x04, 0x05, | |
| 0x02, 0x03, 0x03, 0x04, 0x03, 0x04, 0x04, 0x05, | |
| 0x03, 0x04, 0x04, 0x05, 0x04, 0x05, 0x05, 0x06, | |
| 0x02, 0x03, 0x03, 0x04, 0x03, 0x04, 0x04, 0x05, | |
| 0x03, 0x04, 0x04, 0x05, 0x04, 0x05, 0x05, 0x06, | |
| 0x03, 0x04, 0x04, 0x05, 0x04, 0x05, 0x05, 0x06, | |
| 0x04, 0x05, 0x05, 0x06, 0x05, 0x06, 0x06, 0x07, | |
| 0x02, 0x03, 0x03, 0x04, 0x03, 0x04, 0x04, 0x05, | |
| 0x03, 0x04, 0x04, 0x05, 0x04, 0x05, 0x05, 0x06, | |
| 0x03, 0x04, 0x04, 0x05, 0x04, 0x05, 0x05, 0x06, | |
| 0x04, 0x05, 0x05, 0x06, 0x05, 0x06, 0x06, 0x07, | |
| 0x03, 0x04, 0x04, 0x05, 0x04, 0x05, 0x05, 0x06, | |
| 0x04, 0x05, 0x05, 0x06, 0x05, 0x06, 0x06, 0x07, | |
| 0x04, 0x05, 0x05, 0x06, 0x05, 0x06, 0x06, 0x07, | |
| 0x05, 0x06, 0x06, 0x07, 0x06, 0x07, 0x07, 0x08 | |
| }; | |
| public Nilsimsa() | |
| { | |
| _acc = new int[256]; | |
| Reset(); | |
| } | |
| public void Reset() | |
| { | |
| _count = 0; | |
| Array.Clear(_acc, 0, _acc.Length); | |
| _c0 = _c1 = _c2 = _c3 = 0; | |
| } | |
| /// <summary> | |
| /// Updates the hash with the given byte array. | |
| /// </summary> | |
| public void Update(byte[] data) | |
| { | |
| foreach (byte c4 in data) | |
| { | |
| _count++; | |
| if (_count > 4) | |
| { | |
| byte old0 = _c0, old1 = _c1, old2 = _c2; | |
| _acc[Tran53(c4, _c0, _c1, 0)]++; | |
| _acc[Tran53(c4, _c0, _c2, 1)]++; | |
| _acc[Tran53(c4, _c1, _c2, 2)]++; | |
| _acc[Tran53(c4, _c0, _c3, 3)]++; | |
| _acc[Tran53(c4, _c1, _c3, 4)]++; | |
| _acc[Tran53(c4, _c2, _c3, 5)]++; | |
| _acc[Tran53(_c3, _c0, c4, 6)]++; | |
| _acc[Tran53(_c3, _c2, c4, 7)]++; | |
| _c0 = c4; | |
| _c1 = old0; | |
| _c2 = old1; | |
| _c3 = old2; | |
| } | |
| else if (_count > 3) | |
| { | |
| byte old0 = _c0, old1 = _c1, old2 = _c2; | |
| _acc[Tran53(c4, _c0, _c1, 0)]++; | |
| _acc[Tran53(c4, _c0, _c2, 1)]++; | |
| _acc[Tran53(c4, _c1, _c2, 2)]++; | |
| _c0 = c4; | |
| _c1 = old0; | |
| _c2 = old1; | |
| _c3 = old2; | |
| } | |
| else if (_count > 2) | |
| { | |
| byte old0 = _c0, old1 = _c1; | |
| _acc[Tran53(c4, _c0, _c1, 0)]++; | |
| _c0 = c4; | |
| _c1 = old0; | |
| _c2 = old1; | |
| } | |
| else if (_count > 1) | |
| { | |
| byte old0 = _c0; | |
| _c0 = c4; | |
| _c1 = old0; | |
| } | |
| else | |
| { | |
| _c0 = c4; | |
| } | |
| } | |
| } | |
| /// <summary> | |
| /// Updates the hash with the given string (UTF8 encoded). | |
| /// </summary> | |
| public void Update(string s) | |
| { | |
| if (!string.IsNullOrEmpty(s)) | |
| Update(Encoding.UTF8.GetBytes(s)); | |
| } | |
| /// <summary> | |
| /// Computes and returns the final 32-byte digest. | |
| /// </summary> | |
| public byte[] GetDigest() | |
| { | |
| int trigrams = 0; | |
| if (_count == 3) | |
| trigrams = 1; | |
| else if (_count == 4) | |
| trigrams = 4; | |
| else if (_count > 4) | |
| trigrams = 8 * _count - 28; | |
| int threshold = trigrams / 256; | |
| byte[] digest = new byte[DigestSize]; | |
| for (int i = 0; i < 256; i++) | |
| { | |
| if (_acc[i] > threshold) | |
| { | |
| int index = i >> 3; | |
| digest[index] |= (byte)(1 << (i & 7)); | |
| } | |
| } | |
| Array.Reverse(digest); | |
| return digest; | |
| } | |
| /// <summary> | |
| /// Returns the hex string representation of the digest. | |
| /// </summary> | |
| public string HexDigest() | |
| { | |
| byte[] digest = GetDigest(); | |
| StringBuilder sb = new StringBuilder(digest.Length * 2); | |
| foreach (byte b in digest) | |
| sb.Append(b.ToString("x2")); | |
| return sb.ToString(); | |
| } | |
| /// <summary> | |
| /// Computes the Nilsimsa digest for the given data. | |
| /// </summary> | |
| public static byte[] ComputeHash(byte[] data) | |
| { | |
| Nilsimsa n = new Nilsimsa(); | |
| n.Update(data); | |
| return n.GetDigest(); | |
| } | |
| /// <summary> | |
| /// Computes the Nilsimsa digest for the given string. | |
| /// </summary> | |
| public static byte[] ComputeHash(string s) | |
| { | |
| return ComputeHash(Encoding.UTF8.GetBytes(s)); | |
| } | |
| /// <summary> | |
| /// Computes the hex string of the Nilsimsa digest. | |
| /// </summary> | |
| public static string HexSum(string s) | |
| { | |
| return BitConverter.ToString(ComputeHash(s)).Replace("-", "").ToLowerInvariant(); | |
| } | |
| /// <summary> | |
| /// Computes the bitwise similarity score between two 32-byte digests. | |
| /// This score is defined as 128 minus the sum of the population counts | |
| /// of the XOR differences. | |
| /// </summary> | |
| public static int BitsDiff(byte[] d1, byte[] d2) | |
| { | |
| if (d1.Length != DigestSize || d2.Length != DigestSize) | |
| throw new ArgumentException("Digests must be 32 bytes long."); | |
| int bits = 0; | |
| for (int i = 0; i < DigestSize; i++) | |
| { | |
| bits += Popc[d1[i] ^ d2[i]]; | |
| } | |
| return 128 - bits; | |
| } | |
| /// <summary> | |
| /// The tran53 transformation function. | |
| /// </summary> | |
| private static byte Tran53(byte a, byte b, byte c, int n) | |
| { | |
| int first = Tran[(a + n) & 0xFF]; | |
| int second = Tran[b] * (n + n + 1); | |
| int third = Tran[(c ^ Tran[n]) & 0xFF]; | |
| return (byte)(((first ^ second) + third) & 0xFF); | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using System; | |
| using NUnit.Framework; | |
| [TestFixture] | |
| public class NilsimsaUnitTests | |
| { | |
| // Helper method: converts a hex string to a byte array. | |
| public static byte[] HexToBytes(string hex) | |
| { | |
| int len = hex.Length; | |
| byte[] bytes = new byte[len / 2]; | |
| for (int i = 0; i < len; i += 2) | |
| { | |
| bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16); | |
| } | |
| return bytes; | |
| } | |
| [Test] | |
| public void TestEmptyAndAbcdefgh() | |
| { | |
| // Verify empty input produces 32 zero bytes. | |
| string x = Nilsimsa.HexSum(""); | |
| Assert.That(x, Is.EqualTo("0000000000000000000000000000000000000000000000000000000000000000")); | |
| // Verify "abcdefgh" produces expected digest. | |
| x = Nilsimsa.HexSum("abcdefgh"); | |
| Assert.That(x, Is.EqualTo("14c8118000000000030800000004042004189020001308014088003280000078")); | |
| } | |
| [Test] | |
| public void TestIncrementalUpdate() | |
| { | |
| // Create instance, update incrementally, and verify digest. | |
| Nilsimsa n = new Nilsimsa(); | |
| n.Update("abcd"); | |
| n.Update("efgh"); | |
| string x = n.HexDigest(); | |
| Assert.That(x, Is.EqualTo("14c8118000000000030800000004042004189020001308014088003280000078")); | |
| // Continue updating with "ijk" and verify updated digest. | |
| n.Update("ijk"); | |
| x = n.HexDigest(); | |
| Assert.That(x, Is.EqualTo("14c811840010000c0328200108040630041890200217582d4098103280000078")); | |
| } | |
| [Test] | |
| public void TestBitsDiff_AbcdefghijkVsAbcdefgh() | |
| { | |
| byte[] digest1 = Nilsimsa.ComputeHash("abcdefghijk"); | |
| byte[] digest2 = Nilsimsa.ComputeHash("abcdefgh"); | |
| int bitsDiff = Nilsimsa.BitsDiff(digest1, digest2); | |
| Assert.That(bitsDiff, Is.EqualTo(109)); | |
| } | |
| [Test] | |
| public void TestBitsDiffHex() | |
| { | |
| string x1 = Nilsimsa.HexSum("abcdefghijk"); | |
| string x2 = Nilsimsa.HexSum("abcdefgh"); | |
| int bitsDiff = Nilsimsa.BitsDiff(HexToBytes(x1), HexToBytes(x2)); | |
| Assert.That(bitsDiff, Is.EqualTo(109)); | |
| } | |
| [Test] | |
| public void TestLongStrings_DiffNewSequenceMatcher() | |
| { | |
| string x1 = Nilsimsa.HexSum("return diff.NewSequenceMatcherFromFiles(srcPath, dstPath)"); | |
| string x2 = Nilsimsa.HexSum("return diff.NewSequenceMatcherFromFiles(dstPath, srcPath)"); | |
| Assert.That(x1, Is.EqualTo("8beb55d08d78fed441ede9301390b49b716a11af3962db70b24540338cb70035")); | |
| Assert.That(x2, Is.EqualTo("8a5355d09968f8d451efeb309919949b73e211af7952c970f245403b8cb7a035")); | |
| int bitsDiff = Nilsimsa.BitsDiff(HexToBytes(x1), HexToBytes(x2)); | |
| Assert.That(bitsDiff, Is.EqualTo(96)); | |
| } | |
| [Test] | |
| public void TestLongStrings_DiffXYZ() | |
| { | |
| string x1 = Nilsimsa.HexSum("return diff.XYZ"); | |
| string x2 = Nilsimsa.HexSum("return diff.NewSequenceMatcherFromFiles(dstPath, srcPath)"); | |
| Assert.That(x1, Is.EqualTo("84125570884ae840f042ea400400009a721891002011a071225247f7a5241018")); | |
| Assert.That(x2, Is.EqualTo("8a5355d09968f8d451efeb309919949b73e211af7952c970f245403b8cb7a035")); | |
| int bitsDiff = Nilsimsa.BitsDiff(HexToBytes(x1), HexToBytes(x2)); | |
| Assert.That(bitsDiff, Is.EqualTo(35)); | |
| } | |
| [Test] | |
| public void TestTabChangeCallback() | |
| { | |
| byte[] digest1 = Nilsimsa.ComputeHash("C.setTabChangeCallbackWrapper(h.ih())"); | |
| byte[] digest2 = Nilsimsa.ComputeHash("C.setTabChangeCallbackWrapper(ih)"); | |
| int bitsDiff = Nilsimsa.BitsDiff(digest1, digest2); | |
| Assert.That(bitsDiff, Is.EqualTo(40)); | |
| } | |
| [Test] | |
| public void TestNilsimsa3() | |
| { | |
| string[] list = new string[] | |
| { | |
| "a", | |
| "ab", | |
| "abc", | |
| "abcd", | |
| "abcde", | |
| "abcdef", | |
| "abcdefg", | |
| "abcdefgh", | |
| "abcdefghi", | |
| "abcdefghij", | |
| "abcdefghijk", | |
| "abcdefghijkl", | |
| "abcdefghijklm", | |
| "abcdefghijklmn", | |
| "abcdefghijklmno", | |
| "abcdefghijklmnop", | |
| "abcdefghijklmnopq", | |
| "abcdefghijklmnopqr", | |
| "abcdefghijklmnopqrs", | |
| "abcdefghijklmnopqrst", | |
| "abcdefghijklmnopqrstu", | |
| "abcdefghijklmnopqrstuv", | |
| "abcdefghijklmnopqrstuvw", | |
| "abcdefghijklmnopqrstuvwx", | |
| "abcdefghijklmnopqrstuvwxy", | |
| "abcdefghijklmnopqrstuvwxyz", | |
| }; | |
| string[] results = new string[] | |
| { | |
| "0000000000000000000000000000000000000000000000000000000000000000", | |
| "0000000000000000000000000000000000000000000000000000000000000000", | |
| "0040000000000000000000000000000000000000000000000000000000000000", | |
| "0440000000000000000000000000000000100000000000000008000000000000", | |
| "0440008000000000000000000000000000100020001200000008001200000050", | |
| "04c0018000000000000000000000000004188020001200000088001280000058", | |
| "04c8118000000000030000000000002004188020001208004088001280000078", | |
| "14c8118000000000030800000004042004189020001308014088003280000078", | |
| "14c8118400000000030800010804043004189020021318094098003280000078", | |
| "14c81184000000000308200108040430041890200217580d4098103280000078", | |
| "14c811840010000c0328200108040630041890200217582d4098103280000078", | |
| "14c811840010000ca328200108044630041890200a17586d4298103280000078", | |
| "14ca11850010000ca328200188044630041898200a17586dc2d8103284000078", | |
| "14ca11850030004ca3a8200188044630041898200a17586dc2d8107284000078", | |
| "14ca11850032004ca3a8284188044730041898200a17586dc2d8107384000078", | |
| "94ca11850432005ca3a828418804473004199c200a17586dc2d8107384004178", | |
| "94ca11850433005ca3a82841880447341419be200a17586dc2d8107384004178", | |
| "94ca11850433005ca3a82841a88457341419be201a17586dc6d8107384084178", | |
| "94ca11850533005ca3b82841a88657361419be201a17586dc6d8107384084178", | |
| "94ca11850533005ca3b82841aa8657371419be201a17587dc6d81077840c4178", | |
| "94ca15850533005ca3b92841aa8657371419be201a17587dd6d81077844cc178", | |
| "94ca15850533005ca3b92849aa8657371419be201a17587fd6d81077844cc978", | |
| "94ca15850533045cabb92869aa8657371419bea01a17587fd6f81077c44cc978", | |
| "94ca95850533045cabb93869aa8657371499beb01a17587fd6f8107fc44cc978", | |
| "94ca95850733045cabb93869aa8657373499beb01a17587fd6f9107fc54cc978", | |
| "94ca95850773045cabb93869ba8657373499beb81a17587fd6f9107fc54cc978", | |
| }; | |
| byte[] compareResults = new byte[] | |
| { | |
| 128, | |
| 127, | |
| 125, | |
| 120, | |
| 120, | |
| 120, | |
| 120, | |
| 120, | |
| 123, | |
| 122, | |
| 122, | |
| 121, | |
| 124, | |
| 123, | |
| 121, | |
| 123, | |
| 122, | |
| 124, | |
| 123, | |
| 123, | |
| 125, | |
| 122, | |
| 123, | |
| 124, | |
| 125, | |
| }; | |
| byte[] step3CompareResults = new byte[] | |
| { | |
| 116, | |
| 104, | |
| 109, | |
| 111, | |
| 111, | |
| 113, | |
| 114, | |
| 116, | |
| }; | |
| Assert.That(list.Length, Is.EqualTo(results.Length), "Length mismatch between list and results"); | |
| for (int i = 0; i < list.Length; i++) | |
| { | |
| string hex = Nilsimsa.HexSum(list[i]); | |
| Assert.That(hex, Is.EqualTo(results[i]), $"Mismatch at index {i}: {hex}"); | |
| } | |
| Assert.That(list.Length, Is.EqualTo(compareResults.Length + 1), "Length mismatch between list and compareResults"); | |
| byte[] last = Nilsimsa.ComputeHash(list[0]); | |
| for (int i = 1; i < list.Length; i++) | |
| { | |
| byte[] sum = Nilsimsa.ComputeHash(list[i]); | |
| int bits = Nilsimsa.BitsDiff(sum, last); | |
| Assert.That((byte)bits, Is.EqualTo(compareResults[i - 1]), $"Mismatch in BitsDiff at index {i}: {bits:x}"); | |
| last = sum; | |
| } | |
| int j = 0; | |
| last = Nilsimsa.ComputeHash(list[0]); | |
| for (int i = 4; i < list.Length; i += 3) | |
| { | |
| byte[] sum = Nilsimsa.ComputeHash(list[i]); | |
| int bits = Nilsimsa.BitsDiff(sum, last); | |
| Assert.That((byte)bits, Is.EqualTo(step3CompareResults[j]), $"Mismatch in step3 BitsDiff at index {i}: {bits:x}"); | |
| last = sum; | |
| j++; | |
| } | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using System; | |
| class Program | |
| { | |
| static void Main(string[] args) | |
| { | |
| string text1 = "this is the first string"; | |
| string text2 = "this is the second string"; | |
| string digest1 = Nilsimsa.HexSum(text1); | |
| string digest2 = Nilsimsa.HexSum(text2); | |
| byte[] hash1 = Nilsimsa.ComputeHash(text1); | |
| byte[] hash2 = Nilsimsa.ComputeHash(text2); | |
| int score = Nilsimsa.BitsDiff(hash1, hash2); | |
| Console.WriteLine("Nilsimsa method"); | |
| Console.WriteLine($"String 1:\t{text1}"); | |
| Console.WriteLine($"String 2:\t{text2}"); | |
| Console.WriteLine("\n--------------"); | |
| Console.WriteLine($"String 1 digest: {digest1}"); | |
| Console.WriteLine($"String 2 digest: {digest2}"); | |
| Console.WriteLine($"Score:\t\t{score}\n"); | |
| Console.Read(); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment